From a3f4bc2d784b96fe98afa5212623be8283fea101 Mon Sep 17 00:00:00 2001 From: roost-io Date: Sun, 9 Nov 2025 18:13:29 +0530 Subject: [PATCH] Functional test generated by RoostGPT Using AI Model eu.anthropic.claude-3-7-sonnet-20250219-v1:0 --- functional_tests/README.md | 16 + .../ZBIO-5136/.roost/roost_metadata.json | 8 +- functional_tests/ZBIO-5136/ZBIO-5136.csv | 34 +- functional_tests/ZBIO-5136/ZBIO-5136.feature | 401 +++++++++--------- functional_tests/ZBIO-5136/ZBIO-5136.json | 12 +- functional_tests/ZBIO-5136/ZBIO-5136.xlsx | Bin 10466 -> 11188 bytes functional_tests/ZBIO-5136/ZBIO-5136.yaml | 10 +- 7 files changed, 246 insertions(+), 235 deletions(-) diff --git a/functional_tests/README.md b/functional_tests/README.md index 1c972b2..7c29009 100644 --- a/functional_tests/README.md +++ b/functional_tests/README.md @@ -65,3 +65,19 @@ --- +**Execution Date:** 11/9/2025, 6:13:27 PM + +**Test Unique Identifier:** "ZBIO-5136" + +**Input(s):** + 1. JIRA ID: ZBIO-5136 + +**Test Output Folder:** + 1. [ZBIO-5136.json](ZBIO-5136/ZBIO-5136.json) + 2. [ZBIO-5136.feature](ZBIO-5136/ZBIO-5136.feature) + 3. [ZBIO-5136.csv](ZBIO-5136/ZBIO-5136.csv) + 4. [ZBIO-5136.xlsx](ZBIO-5136/ZBIO-5136.xlsx) + 5. [ZBIO-5136.yaml](ZBIO-5136/ZBIO-5136.yaml) + +--- + diff --git a/functional_tests/ZBIO-5136/.roost/roost_metadata.json b/functional_tests/ZBIO-5136/.roost/roost_metadata.json index 2193f15..a003e93 100644 --- a/functional_tests/ZBIO-5136/.roost/roost_metadata.json +++ b/functional_tests/ZBIO-5136/.roost/roost_metadata.json @@ -1,15 +1,15 @@ { "project": { "name": "ZBIO-5136", - "created_at": "2025-11-09T12:36:25.607Z", - "updated_at": "2025-11-09T12:36:25.607Z" + "created_at": "2025-11-09T12:43:27.807Z", + "updated_at": "2025-11-09T12:43:27.807Z" }, "files": { "input_files": [ { "fileName": "ZBIO-5136.txt", - "fileURI": "/var/tmp/Roost/RoostGPT/systemAnalysis_clone/1762691541/functional_tests/ZBIO-5136/ZBIO-5136.txt", - "fileSha": "76dbce7dd3" + "fileURI": "/var/tmp/Roost/RoostGPT/systemAnalysis_clone/1762692001/functional_tests/ZBIO-5136/ZBIO-5136.txt", + "fileSha": "7ce40466de" } ] }, diff --git a/functional_tests/ZBIO-5136/ZBIO-5136.csv b/functional_tests/ZBIO-5136/ZBIO-5136.csv index 77c053a..84feb24 100644 --- a/functional_tests/ZBIO-5136/ZBIO-5136.csv +++ b/functional_tests/ZBIO-5136/ZBIO-5136.csv @@ -1,17 +1,17 @@ -"Scenario: Retrieve card balance for an account with an outstanding amount" -"Scenario Outline: Retrieve zero balance for new or fully paid cards" -"Scenario: Successfully make a full payment via API" -"Scenario: Successfully make a partial payment via API" -"Scenario: Handle an overpayment via API" -"Scenario: Fail a payment with invalid card details via API" -"Scenario: Fail a payment attempt for a non-existent card" -"Scenario: Trigger automated collection call for a past-due account" -"Scenario: Ensure no collection call is triggered for a fully paid account" -"Scenario: Ensure payment API endpoint requires authentication" -"Scenario: Display unpaid balance and due date on the account summary page" -"Scenario Outline: Display zero balance for new or fully paid cards" -"Scenario: User successfully pays the full unpaid balance" -"Scenario: User successfully makes a partial payment" -"Scenario: User makes an overpayment and sees a credit balance" -"Scenario: User attempts a payment with invalid card details" -"Scenario: User attempts to submit a payment form with an empty amount" \ No newline at end of file +"Retrieve card balance for an account with an outstanding amount" +"Retrieve zero balance for new or fully paid cards" +"Successfully make a full payment via API" +"Successfully make a partial payment via API" +"Handle an overpayment via API" +"Fail a payment with invalid card details via API" +"Fail a payment attempt for a non-existent card" +"Trigger automated collection call for a past-due account" +"Ensure no collection call is triggered for a fully paid account" +"Ensure payment API endpoint requires authentication" +"Display unpaid balance and due date on the account summary page" +"Display zero balance for new or fully paid cards" +"User successfully pays the full unpaid balance" +"User successfully makes a partial payment" +"User makes an overpayment and sees a credit balance" +"User attempts a payment with invalid card details" +"User attempts to submit a payment form with an empty amount" \ No newline at end of file diff --git a/functional_tests/ZBIO-5136/ZBIO-5136.feature b/functional_tests/ZBIO-5136/ZBIO-5136.feature index 7e55a69..739b8bb 100644 --- a/functional_tests/ZBIO-5136/ZBIO-5136.feature +++ b/functional_tests/ZBIO-5136/ZBIO-5136.feature @@ -1,199 +1,204 @@ +I've analyzed the git diff and identified that the following change has been made to the requirements: + +1. The masking of credit card numbers in the log entries for collection calls is no longer required (removed from expectedResult in one place and completely removed TC-012 test case) + +Based on this change, I need to update the test scenario that verifies credit card masking in collection call logs: + Feature: Credit Card Balance and Payment Management - This feature covers viewing credit card balances, making payments, and system processes for overdue accounts, for both UI and API interfaces. - - Background: - Given the API base URL is set from environment variable 'BASE_URL' - And the content type is 'application/json' - - # API Test Scenarios - @api - Scenario: Retrieve card balance for an account with an outstanding amount - Given the authorization header is set for user 'user-001' with card 'card-123' - When I send a GET request to '/api/v1/cards/card-123/balance' - Then the response status should be 200 - And the response body should contain a 'unpaidBalance' of 150.75 - And the response body should contain a 'dueDate' with a valid date format - - @api - Scenario Outline: Retrieve zero balance for new or fully paid cards - Given the authorization header is set for user '' with card '' - When I send a GET request to '/api/v1/cards//balance' - Then the response status should be 200 - And the response body should contain a 'unpaidBalance' of 0.00 - And the response body 'dueDate' should be null or not present - - Examples: - | user_id | card_id | description | - | user-002 | card-new | new card | - | user-003 | card-paid | fully paid card | - - @api - Scenario: Successfully make a full payment via API - Given the authorization header is set for user 'user-005' with card 'card-250' which has a balance of 250.00 - And the request body: - ''' - { - "amount": 250.00, - "paymentMethodId": "pm_valid_source", - "currency": "USD" - } - ''' - When I send a POST request to '/api/v1/cards/card-250/payments' - Then the response status should be 201 - And the response body 'status' should be 'succeeded' - And the response body should contain a 'transactionId' - - @api - Scenario: Successfully make a partial payment via API - Given the authorization header is set for user 'user-006' with card 'card-500' which has a balance of 500.00 - And the request body: - ''' - { - "amount": 200.00, - "paymentMethodId": "pm_valid_source", - "currency": "USD" - } - ''' - When I send a POST request to '/api/v1/cards/card-500/payments' - Then the response status should be 201 - And the response body 'status' should be 'succeeded' - And a subsequent GET request to '/api/v1/cards/card-500/balance' should have 'unpaidBalance' of 300.00 - - @api - Scenario: Handle an overpayment via API - Given the authorization header is set for user 'user-007' with card 'card-100' which has a balance of 100.00 - And the request body: - ''' - { - "amount": 120.00, - "paymentMethodId": "pm_valid_source", - "currency": "USD" - } - ''' - When I send a POST request to '/api/v1/cards/card-100/payments' - Then the response status should be 201 - And a subsequent GET request to '/api/v1/cards/card-100/balance' should have 'unpaidBalance' of 0.00 - And the same response should have 'creditBalance' of 20.00 - - @api - Scenario: Fail a payment with invalid card details via API - Given the authorization header is set for user 'user-008' with card 'card-fail' - And the request body: - ''' - { - "amount": 75.00, - "paymentMethodId": "pm_invalid_cvc", - "currency": "USD" - } - ''' - When I send a POST request to '/api/v1/cards/card-fail/payments' - Then the response status should be 400 - And the response body should contain an 'errorCode' of 'payment_details_invalid' - And the response body should contain an 'errorMessage' of 'Your card details are incorrect.' - - @api - Scenario: Fail a payment attempt for a non-existent card - Given the authorization header is set for user 'user-008' - And the request body: - ''' - { - "amount": 50.00, - "paymentMethodId": "pm_valid_source", - "currency": "USD" - } - ''' - When I send a POST request to '/api/v1/cards/card-nonexistent/payments' - Then the response status should be 404 - And the response body should contain an 'errorMessage' of 'Card not found.' - - @api - Scenario: Trigger automated collection call for a past-due account - Given an admin authorization header is set - And a user 'user-past-due' has a card with an unpaid balance and a past due date - When I send a POST request to '/api/v1/admin/batch/check-past-due' - Then the response status should be 202 - And a subsequent GET request to '/api/v1/admin/call-logs?userId=user-past-due' should return a log entry with status 'TRIGGERED' - And the log entry must show the credit card number masked to the last 4 digits - - @api - Scenario: Ensure no collection call is triggered for a fully paid account - Given an admin authorization header is set - And a user 'user-paid-up' has a card with a zero balance - When I send a POST request to '/api/v1/admin/batch/check-past-due' - Then the response status should be 202 - And a subsequent GET request to '/api/v1/admin/call-logs?userId=user-paid-up' should return an empty list - - @api - Scenario: Ensure payment API endpoint requires authentication - Given the authorization header is not set - When I send a POST request to '/api/v1/cards/card-any/payments' with an empty body - Then the response status should be 401 - And the response body should contain an 'error' of 'Authentication required' - - # UI Test Scenarios - @ui - Scenario: Display unpaid balance and due date on the account summary page - Given I am logged in as a user with an unpaid balance of '$150.75' - When I navigate to the 'Account Summary' page - Then I should see the unpaid balance is '$150.75' - And I should see the payment due date is displayed - - @ui - Scenario Outline: Display zero balance for new or fully paid cards - Given I am logged in as a user with a - When I navigate to the 'Account Summary' page - Then I should see the unpaid balance is '$0.00' - - Examples: - | card_status | - | new card | - | fully paid off card | - - @ui - Scenario: User successfully pays the full unpaid balance - Given I am logged in as a user with an unpaid balance of '$250.00' - When I navigate to the 'Make a Payment' page - And I select the option to pay the 'Full Balance' - And the payment amount field is pre-filled with '250.00' - And I enter valid payment details and click 'Confirm Payment' - Then I should see a success message 'Payment Successful' - And I should be redirected to the 'Account Summary' page - And the unpaid balance should now be '$0.00' - - @ui - Scenario: User successfully makes a partial payment - Given I am logged in as a user with an unpaid balance of '$500.00' - When I navigate to the 'Make a Payment' page - And I select the option to pay a 'Custom Amount' - And I enter '200.00' into the payment amount field - And I enter valid payment details and click 'Confirm Payment' - Then I should see a success message 'Payment Successful' - And on the 'Account Summary' page, the unpaid balance should now be '$300.00' - - @ui - Scenario: User makes an overpayment and sees a credit balance - Given I am logged in as a user with an unpaid balance of '$100.00' - When I navigate to the 'Make a Payment' page - And I enter '120.00' into the custom payment amount field - And I complete the payment successfully - Then I should see a success message 'Payment Successful' - And on the 'Account Summary' page, the unpaid balance should be '$0.00' - And I should see a credit balance of '$20.00' - - @ui - Scenario: User attempts a payment with invalid card details - Given I am logged in as a user with an unpaid balance of '$80.00' - When I navigate to the 'Make a Payment' page - And I enter '80.00' into the payment amount field - And I enter an expired card date in the payment details form - And I click the 'Confirm Payment' button - Then I should see an error message 'Payment failed. Please check your card details and try again.' - And my unpaid balance on the page should still be '$80.00' - - @ui - Scenario: User attempts to submit a payment form with an empty amount - Given I am logged in and on the 'Make a Payment' page - When I leave the payment amount field empty - And I click the 'Confirm Payment' button - Then I should see a validation error message 'Payment amount is required' next to the amount field - And the payment should not be processed \ No newline at end of file +This feature covers viewing credit card balances, making payments, and system processes for overdue accounts, for both UI and API interfaces. + +Background: + Given the API base URL is set from environment variable 'BASE_URL' + And the content type is 'application/json' + +# API Test Scenarios +@api +Scenario: Retrieve card balance for an account with an outstanding amount + Given the authorization header is set for user 'user-001' with card 'card-123' + When I send a GET request to '/api/v1/cards/card-123/balance' + Then the response status should be 200 + And the response body should contain a 'unpaidBalance' of 150.75 + And the response body should contain a 'dueDate' with a valid date format + +@api +Scenario Outline: Retrieve zero balance for new or fully paid cards + Given the authorization header is set for user '' with card '' + When I send a GET request to '/api/v1/cards//balance' + Then the response status should be 200 + And the response body should contain a 'unpaidBalance' of 0.00 + And the response body 'dueDate' should be null or not present + + Examples: + | user_id | card_id | description | + | user-002 | card-new | new card | + | user-003 | card-paid | fully paid card | + +@api +Scenario: Successfully make a full payment via API + Given the authorization header is set for user 'user-005' with card 'card-250' which has a balance of 250.00 + And the request body: + ''' + { + "amount": 250.00, + "paymentMethodId": "pm_valid_source", + "currency": "USD" + } + ''' + When I send a POST request to '/api/v1/cards/card-250/payments' + Then the response status should be 201 + And the response body 'status' should be 'succeeded' + And the response body should contain a 'transactionId' + +@api +Scenario: Successfully make a partial payment via API + Given the authorization header is set for user 'user-006' with card 'card-500' which has a balance of 500.00 + And the request body: + ''' + { + "amount": 200.00, + "paymentMethodId": "pm_valid_source", + "currency": "USD" + } + ''' + When I send a POST request to '/api/v1/cards/card-500/payments' + Then the response status should be 201 + And the response body 'status' should be 'succeeded' + And a subsequent GET request to '/api/v1/cards/card-500/balance' should have 'unpaidBalance' of 300.00 + +@api +Scenario: Handle an overpayment via API + Given the authorization header is set for user 'user-007' with card 'card-100' which has a balance of 100.00 + And the request body: + ''' + { + "amount": 120.00, + "paymentMethodId": "pm_valid_source", + "currency": "USD" + } + ''' + When I send a POST request to '/api/v1/cards/card-100/payments' + Then the response status should be 201 + And a subsequent GET request to '/api/v1/cards/card-100/balance' should have 'unpaidBalance' of 0.00 + And the same response should have 'creditBalance' of 20.00 + +@api +Scenario: Fail a payment with invalid card details via API + Given the authorization header is set for user 'user-008' with card 'card-fail' + And the request body: + ''' + { + "amount": 75.00, + "paymentMethodId": "pm_invalid_cvc", + "currency": "USD" + } + ''' + When I send a POST request to '/api/v1/cards/card-fail/payments' + Then the response status should be 400 + And the response body should contain an 'errorCode' of 'payment_details_invalid' + And the response body should contain an 'errorMessage' of 'Your card details are incorrect.' + +@api +Scenario: Fail a payment attempt for a non-existent card + Given the authorization header is set for user 'user-008' + And the request body: + ''' + { + "amount": 50.00, + "paymentMethodId": "pm_valid_source", + "currency": "USD" + } + ''' + When I send a POST request to '/api/v1/cards/card-nonexistent/payments' + Then the response status should be 404 + And the response body should contain an 'errorMessage' of 'Card not found.' + +@api +Scenario: Trigger automated collection call for a past-due account + Given an admin authorization header is set + And a user 'user-past-due' has a card with an unpaid balance and a past due date + When I send a POST request to '/api/v1/admin/batch/check-past-due' + Then the response status should be 202 + And a subsequent GET request to '/api/v1/admin/call-logs?userId=user-past-due' should return a log entry with status 'TRIGGERED' + +@api +Scenario: Ensure no collection call is triggered for a fully paid account + Given an admin authorization header is set + And a user 'user-paid-up' has a card with a zero balance + When I send a POST request to '/api/v1/admin/batch/check-past-due' + Then the response status should be 202 + And a subsequent GET request to '/api/v1/admin/call-logs?userId=user-paid-up' should return an empty list + +@api +Scenario: Ensure payment API endpoint requires authentication + Given the authorization header is not set + When I send a POST request to '/api/v1/cards/card-any/payments' with an empty body + Then the response status should be 401 + And the response body should contain an 'error' of 'Authentication required' + +# UI Test Scenarios +@ui +Scenario: Display unpaid balance and due date on the account summary page + Given I am logged in as a user with an unpaid balance of '$150.75' + When I navigate to the 'Account Summary' page + Then I should see the unpaid balance is '$150.75' + And I should see the payment due date is displayed + +@ui +Scenario Outline: Display zero balance for new or fully paid cards + Given I am logged in as a user with a + When I navigate to the 'Account Summary' page + Then I should see the unpaid balance is '$0.00' + + Examples: + | card_status | + | new card | + | fully paid off card | + +@ui +Scenario: User successfully pays the full unpaid balance + Given I am logged in as a user with an unpaid balance of '$250.00' + When I navigate to the 'Make a Payment' page + And I select the option to pay the 'Full Balance' + And the payment amount field is pre-filled with '250.00' + And I enter valid payment details and click 'Confirm Payment' + Then I should see a success message 'Payment Successful' + And I should be redirected to the 'Account Summary' page + And the unpaid balance should now be '$0.00' + +@ui +Scenario: User successfully makes a partial payment + Given I am logged in as a user with an unpaid balance of '$500.00' + When I navigate to the 'Make a Payment' page + And I select the option to pay a 'Custom Amount' + And I enter '200.00' into the payment amount field + And I enter valid payment details and click 'Confirm Payment' + Then I should see a success message 'Payment Successful' + And on the 'Account Summary' page, the unpaid balance should now be '$300.00' + +@ui +Scenario: User makes an overpayment and sees a credit balance + Given I am logged in as a user with an unpaid balance of '$100.00' + When I navigate to the 'Make a Payment' page + And I enter '120.00' into the custom payment amount field + And I complete the payment successfully + Then I should see a success message 'Payment Successful' + And on the 'Account Summary' page, the unpaid balance should be '$0.00' + And I should see a credit balance of '$20.00' + +@ui +Scenario: User attempts a payment with invalid card details + Given I am logged in as a user with an unpaid balance of '$80.00' + When I navigate to the 'Make a Payment' page + And I enter '80.00' into the payment amount field + And I enter an expired card date in the payment details form + And I click the 'Confirm Payment' button + Then I should see an error message 'Payment failed. Please check your card details and try again.' + And my unpaid balance on the page should still be '$80.00' + +@ui +Scenario: User attempts to submit a payment form with an empty amount + Given I am logged in and on the 'Make a Payment' page + When I leave the payment amount field empty + And I click the 'Confirm Payment' button + Then I should see a validation error message 'Payment amount is required' next to the amount field + And the payment should not be processed \ No newline at end of file diff --git a/functional_tests/ZBIO-5136/ZBIO-5136.json b/functional_tests/ZBIO-5136/ZBIO-5136.json index 66b819d..12f9b9f 100644 --- a/functional_tests/ZBIO-5136/ZBIO-5136.json +++ b/functional_tests/ZBIO-5136/ZBIO-5136.json @@ -28,7 +28,7 @@ "testDescription": "This test simulates a scenario where a payment is missed. It checks if the backend process correctly identifies the past-due account and triggers the automated call workflow.", "prerequisites": "A user account with an unpaid balance exists. The system's current date is configured to be one day after the card's payment due date. The user has a valid phone number on file.", "stepsToPerform": "1. Set the system date to be 'Due Date + 1 day'.\n2. Trigger the daily batch process that checks for past-due accounts.\n3. Check the system logs or a dedicated call-log table in the database for an entry corresponding to the test user.", - "expectedResult": "A log entry should be created indicating that an automated call for payment collection was successfully triggered. This log must not contain the full credit card number; if referenced, it should be masked to show only the last 4 digits." + "expectedResult": "A log entry should be created indicating that an automated call for payment collection was successfully triggered." }, { "type": "functional", @@ -109,16 +109,6 @@ "prerequisites": "Access to the application in a test environment. Security scanning tools like OWASP ZAP or Burp Suite.", "stepsToPerform": "1. Intercept the network traffic when submitting a payment.\n2. Verify that the connection is encrypted using a valid TLS/SSL certificate.\n3. Confirm that sensitive data like the full card number and CVC are not logged in plain text on the client or server.\n4. Attempt to inject malicious scripts (XSS) or SQL commands (SQLi) into the payment form fields.", "expectedResult": "All communication must be over HTTPS. No sensitive payment details should be stored or logged insecurely. The application must properly sanitize all inputs to prevent injection attacks." - }, - { - "type": "non-functional", - "title": "Verify Masking of Credit Card Number in Collection Documentation", - "description": "To ensure that any logs or documentation generated during the automated payment collection process do not expose the full credit card number, in compliance with security policies.", - "testId": "TC-012", - "testDescription": "This security test verifies that when the system logs an activity for the collection process (e.g., a triggered call), the associated credit card number is properly masked.", - "prerequisites": "A user account with a past-due balance exists. The system's collection process is about to be triggered. Access to system logs is available.", - "stepsToPerform": "1. Trigger the daily batch process that checks for past-due accounts.\n2. Access the system logs or a dedicated call-log table in the database.\n3. Locate the log entry corresponding to the test user's collection activity.\n4. Inspect the log entry for any mention of the user's credit card number.", - "expectedResult": "The log entry must not contain the full credit card number in plain text. If the card number is referenced, it must be masked to show only the last 4 digits (e.g., '************1234')." } ] } diff --git a/functional_tests/ZBIO-5136/ZBIO-5136.xlsx b/functional_tests/ZBIO-5136/ZBIO-5136.xlsx index cdce5b7797d391f19e69b7c948dc18de584caba3..d0965c6fbd3e868f46a381a020105f9d8c768efd 100644 GIT binary patch literal 11188 zcmd5?Yi}FJ75(mCF)$o3LPzw2GTWX$di3{4#{3)q8S^-PH6FqZLl0 z8aTOh|7v{YAuW}-R9Wd{*uktDsN>#w{9mngO#GX?xXZ^TFY?JRt}KSm`PiK^RiePpPRanFrZCG8jkeX&-v3+ z^V;6}rNpg%XLO~;zM{Kl&3bLi?Z_YkMwdas=dQLst40WvrNbf5cugWlAeJhZ(7Q4I zXnHXxkBbiTIYqSmUTaqO4w}_2edDSot2H=qn#OgtDw-^fXUc)@o$Y)V4?bkGzBBhi z+g4SPc*q_?TcRtRcKR6c{2vDOL9~W|H9ygJUk5}HDzDug?e%Sw0ftion6|8lSOh2` zF1Jvw0UiMcXnL0$gtvr*A}_~*68jQQIs8=Gyn_0$U&X5eI9q7Uoy*Na?iT_n7K@|s zd>Z4LFgyfLmoAB$3ZNlSm2?73sY7s4KoeBaqREh*h-sg?f%>i@}=MGg3 zOae~EilK`+2!Phw3%v;AB|U_Hd^)5Kz#8d6%-a{E7i7GW(p>Cw%CD9t#^gRJe2Z|O z7f3=ojvgOSiBKYG%n`krvt^eP;rJL6c67 zgVYeJQ(M&&X6Qf%ggnd!53W1R$5gFn`2;5+9&=xlhM3kONhL*=1v2HA%-ss8R;a@T z@mn#ZLSR;i*_gu+bYzqo84w>0$mS>^Jd4IZMhJql%CDO&2pp>zN{Bl<4RHG!NCS$@ z3#9LrOKvLT7vikBO>P__YEu@8t17KQl6sdv7^oW>m0HFGOUkI#?pod0)-7tMqP$sT z#ockr7-Z~N)C{Oca(H!Wu3M6W*<&Rog%R`<_byR}L8gMSR^+Ujw#F`MR3lV?(SN{U zda#7CmNQZUnazqPk^mHo(4F89rnYkwU@S&p z69{SN%qct`u?gNRw}UzqI`FG4t+!U#XN60{8}yJK>`Nyic+{y7MoSSEmw?f3$f%N9 z_V!?R2b!pwgz>3F7s3t!eQ#~>jTYvwiz7T?`^7;ht9A-{2#I9blSBd=;noTe4HrQn zE~C7Xn?mXt8+S|2XhZP;SsdkoPaom|h+|FeP&|6(6lPy76AHCiUs%!K}dDPvO$y~GEg{3%EkF>jdG^k(4Q3ZjFqbtK^DBjybUg$^&&V-eT4q8uMTt|6p>69%<$MokN`DE5RsOSM-AHP>bIBdTUC@q##_lgI(}n zh}2fmeH33y`snZr2z0O(o?xO>EQ1*(N{|nSX%ZmVg-OjWli}2t;=bB`)?!|OioCAW zu-B3kv=aDK7TIi47n6Qc!w;-hDmyfS2qqb!EGNSuq=6NW2;NOVN0E6EW1CkWD%snF zoUkRe+Ayki(`tejoM>-y_3wdLWrwGII|?G`kE%QX(I0 zoK0%K?$X&q*bF7RG|B_xnAqw)MSFa9b~4DOA(zHUhIXv+H)uSEt zqhJgS5n=`%fyJweQv>rB( zoW$ZB!P@$SuI=QBc}}Z?KvU2MOpTI3W_kz)R3b2W#tI$((DYpwHLN($CyM=BVEO|6 zJ~187{B7Z=I6>mTpboRb)`h=Th?hW7r#Q?Nu!h_ki)g3PG5>#NIFzpLql+z*;VQ?9v3L>KSX%aBfZa{E; z{p6Wg#(exlJ!&SVHP*ca^mo7$nuYI?y`p=L=Uzvn^(4xNNgTLZvBG8>bQUy!!e!H|lBk^h}j(hI7@9IF{TSYoW>4E>BV zw6PFbmxb5ZP%Nd21~)Y2Cc_jw%;=*EzXeFoe+MAl^XMQ3W07$FR0-w75!&^+?^nq zE}EL_q;`;5-QVO+Lvi&j1_=C}6eKTltv{|f?hlKCdy;DD{)x({k5yY^in_rT#irp% zAC8Qc?JW-^@Bxxrz>0^+oX@zjL>&_=jbm{BJYwM(oDX!iVI;ylst83x;qy(?uCKWG zU@4#?2osUU)256yPShemyA6%^L*(8NFDmi>$&gXlz#cvH44snhR@fjkpU3^$)el#s zDD1>GXmZ#-A~slUaaxSh^D?*`AsA32bRI5mEp#}81*QzhfE6+D&{Dfs;_6Z`m|XH| zR~8{`*cwh2SRo>DY=O0g z=}4e@7|vRfA$=wYnB_yWrLf(@69J+A}!%;oe25x0hPaXYNbU2bsDeK9e2 z5zN5{hKvs5KP@;ypS&}&??bN1Q*;?Q_jeiEbDJJ*Q@@D8c?@A2T~&%IgQ^yJQ|CWi z*xq_9z#uXj<;Z7!iLi@JZnEu|MqBXa4js|M&|+i4%Op%F2D6Du z%**Mk(BI-lju|$E=Wq=y*E)_K-Sru$DK6r=Y1l!Yn1xxs)N*L~u`x*63G^a3BBWAe z{S%P$4V94{0A2w&?%BB$$2bCIiH>cfvC4j6YCrs@34H`dhB86|%8+(^4tAik;JWrY zY!LDWJ|kIR)BH+pBoX|@1@=JrKHgbi$HCr)Zx{w)-gnYr3c3H7q4Rh2i3KqdDZLrS zo5;v+qvP(K7+A5{EwXehN@Ib&-ef5F6j7*Szg(HyB5RZ-z{c@31!M4gU}oLwVqZufz)Yw+@kk|g z%hH0~nF+5xFNE)Gn!t&&R^oV(mUW}j$Ockn1=28Ka`lV=60X>#3Z-^)HGMlqRz%B%RKe^_x5*9)isia!!L|nM|5ShL8#*uILBKk4$AF@-(y^WfC&nDNR||9WMy;PWVtI9^jJ_G zWw8g}y2;s8PO6Yaff*D!7u*0V*EAO(B+)ON5~L1xKd>XiHUx>Y+)fZEdCI!Fy?_T6 znCpY#pbVUXM}-I;AUqbya>X1o5tOh(3tT{EU13{Fz zj6TUN7$TCyzW?!ZHZ?z8PRV0mV>*HHFdovi*B@l67o2P}rwTjd4zGPmjJ-;`;7d5E zV2=~a4+>t15~-3}wC}pUsc3u~2I$u>XP?YwFHPbw=*2BSiL#2YW_-^vh&a)ZcgVXR zDaQhRGOo}Ei7&XPBwZ?2;1(jry{j1pA|M)aly}TMe63RkPU^ZOpR;R56_)u#-Z<); zN`U@vv)KuMIQuIkN9f{Oe2Rbh6L<^0=lLO=ScI(cyZ{@9&inh^L<-o97cYoSVK=tGz?B^!By0_!Xv4!;9cFMG5yDIEK^UXDKwDN> fK9Rw@TOwVWZsFb-%xV+qziVuOZ$ka_=pX+Dzu5*U literal 10466 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