diff --git a/composer.json b/composer.json index f394442..99db56c 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,7 @@ }, "require-dev": { "phpunit/phpunit": "^9", - "gullevek/dotenv": "dev-master" + "gullevek/dotenv": "dev-master", + "dg/bypass-finals": "dev-master" } } diff --git a/phpunit.xml b/phpunit.xml index c333bf5..d472240 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -2,4 +2,8 @@ colors="true" verbose="true" > + + + + diff --git a/test/agcod_http_return_samples.md b/test/agcod_http_return_samples.md new file mode 100644 index 0000000..52673a0 --- /dev/null +++ b/test/agcod_http_return_samples.md @@ -0,0 +1,140 @@ +# Return data from AWS as json string + +## Mock tests self + +Tests run in local mock tests without connection to AWS + +### funds + +```json +{"availableFunds":{"amount":0.0,"currencyCode":"JPY"},"status":"SUCCESS","timestamp":"20220610T085450Z"} +``` + +### buy + +```json +{"cardInfo":{"cardNumber":null,"cardStatus":"Fulfilled","expirationDate":null,"value":{"amount":1000.0,"currencyCode":"JPY"}},"creationRequestId":"PartnerId_62a309167e7a4","gcClaimCode":"LJ49-AKDUV6-UYCP","gcExpirationDate":"Thu Jun 10 14:59:59 UTC 2032","gcId":"5535125272070255","status":"SUCCESS"} +``` + +### cancel + +```json +{"creationRequestId":"EG3bd_62a309167e7a4","gcId":"5535125272070255","status":"SUCCESS"} +``` + +### buy other 1 + +```json +{"cardInfo":{"cardNumber":null,"cardStatus":"RefundedToPurchaser","expirationDate":null,"value":{"amount":1000.0,"currencyCode":"JPY"}},"creationRequestId":"PartnerId_62a309167e7a4","gcClaimCode":"LJ49-AKDUV6-UYCP","gcExpirationDate":"Thu Jun 10 14:59:59 UTC 2032","gcId":"5535125272070255","status":"SUCCESS"} +``` + +### buy aother 2 + +```json +{"cardInfo":{"cardNumber":null,"cardStatus":"Fulfilled","expirationDate":null,"value":{"amount":1000.0,"currencyCode":"JPY"}},"creationRequestId":"PartnerId_62a30923e9705","gcClaimCode":"UM97-FD5QKK-WKCT","gcExpirationDate":"Thu Jun 10 14:59:59 UTC 2032","gcId":"5540334324324221","status":"SUCCESS"} +``` + +### buy other 2 (same create request id) + +```json +{"cardInfo":{"cardNumber":null,"cardStatus":"Fulfilled","expirationDate":null,"value":{"amount":1000.0,"currencyCode":"JPY"}},"creationRequestId":"PartnerId_62a30923e9705","gcClaimCode":"UM97-FD5QKK-WKCT","gcExpirationDate":"Thu Jun 10 14:59:59 UTC 2032","gcId":"5540334324324221","status":"SUCCESS"} +``` + +## AWS GCOD Mocks + +Mocking on AWS side, these will be returned if given request ID codes are sent. +A working test account must exist for this + +## success + +### buy gift card F0000 + +```json +{"cardInfo":{"cardNumber":null,"cardStatus":"Fulfilled","expirationDate":null,"value":{"amount":500.0,"currencyCode":"JPY"}},"creationRequestId":"F0000","gcClaimCode":"ZYXW-VUTS-RQPO","gcExpirationDate":null,"gcId":"ABC123ZYX987","status":"SUCCESS"} +``` + +## errors + +### F1000 -> F100 + +```json +{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F100","errorType":"GeneralError","message":"General Error"} +``` + +### F2003 -> F200 + +```json +{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"InvalidAmountInput","message":"Amount can't be null"} +``` + +### F2004 -> F200 + +```json +{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"InvalidAmountValue","message":"Amount must be larger than 0"} +``` + +### F2005 -> F200 + +```json +{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"InvalidCurrencyCodeInput","message":"Currency Code can't be null or empty"} +``` + +### F2010 -> F200 + +```json +{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"CardAlreadyActivatedWithDifferentRequestId","message":"The card was already activated with a different request id"} +``` + +### F2015 -> F200 + +```json +{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"MaxAmountExceeded","message":"Max Amount Exceeded"} +``` + +### F2016 -> F200 + +```json +{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"CurrencyCodeMismatch","message":"Currency Code Mismatch"} +``` + +### F2017 -> F200 + +```json +{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"FractionalAmountNotAllowed","message":"Fractional Amount Not Allowed"} +``` + +### F2047 -> F200 + +```json +{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F200","errorType":"CancelRequestArrivedAfterTimeLimit","message":"Cancellation cannot be processed as too much time has elapsed since creation"} +``` + +### F3003 -> F300 + +```json +{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F300","errorType":"InsufficientFunds","message":"Insufficient Funds"} +``` + +### F3005 -> F300 + +```json +{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F300","errorType":"GeneralError","message":"General Error"} +``` + +### F3010 -> F300 + +```json +{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F300","errorType":"CustomerSurpassedDailyVelocityLimit","message":"Customer has exceeded daily velocity limit"} +``` + +### F4000 -> F400 + +```json +{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"RESEND"},"errorCode":"F400","errorType":"SystemTemporarilyUnavailable","message":"System Temporarily Unavailable"} +``` + +### F5000 -> F500 + +```json +{"agcodResponse":{"__type":"CreateGiftCardResponse:http://internal.amazon.com/coral/com.amazonaws.agcod/","cardInfo":null,"creationRequestId":null,"gcClaimCode":null,"gcExpirationDate":null,"gcId":null,"status":"FAILURE"},"errorCode":"F500","errorType":"GeneralError","message":"General Error"} +``` diff --git a/test/aws_gift_card_tests.php b/test/aws_gift_card_tests.php index 9808382..8f7b640 100644 --- a/test/aws_gift_card_tests.php +++ b/test/aws_gift_card_tests.php @@ -59,6 +59,7 @@ $loader->addPsr4('gullevek\\', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_ // print "LOADER:
" . print_r($loader, true) . "
"; use gullevek\AmazonIncentives\AmazonIncentives; +use gullevek\AmazonIncentives\Exceptions\AmazonErrors; use gullevek\dotEnv\DotEnv; // load env data with dotenv @@ -75,30 +76,34 @@ print "

Amazon Gift Card Incentives


"; // optional // debug: AWS_DEBUG (if not set: off) +// run info test (prints ENV vars) +$run_info_test = !empty($_GET['info']) ? true : false; +// run test to get funds info +$run_fund_test = !empty($_GET['fund']) ? true : false; +// run the normal get/cancel gift card tests +$run_gift_tests = !empty($_GET['gift']) ? true : false; +// run mock error check tests +$run_mocks = !empty($_GET['mocks']) ? true : false; + +// should we print debug info +$debug_print = !empty($_GET['debug']) ? true : false; +// how long to wait between each call +$debug_wait = 2; +// if set to true will print all the debug logs too +$mock_debug = !empty($_GET['debug_mock']) ? true : false; +// wait in seconds between mock tests +$mock_wait = 2; + +if (empty($_GET)) { + print "Use _GET parameters to start tests"; +} + // open debug file output $fp = fopen('log/debug.' . date('YmdHis') . '.log', 'w'); if (!is_resource($fp)) { die("Cannot open log debug file"); } -// run info test (prints ENV vars) -$run_info_test = false; -// run test to get funds info -$run_fund_test = true; -// run the normal get/cancel gift card tests -$run_gift_tests = true; -// run mock error check tests -$run_mocks = true; - -// should we print debug info -$debug_print = false; -// how long to wait between each call -$debug_wait = 2; -// if set to true will print all the debug logs too -$mock_debug = false; -// wait in seconds between mock tests -$mock_wait = 2; - if ($run_info_test === true) { $aws = new AmazonIncentives(); $aws_check_me = $aws->checkMe(); @@ -120,7 +125,7 @@ if ($run_fund_test === true) { } fwrite($fp, writeLog((array)$aws_test)); } catch (Exception $e) { - $error = AmazonIncentives::decodeExceptionMessage($e->getMessage()); + $error = AmazonErrors::decodeExceptionMessage($e->getMessage()); printException('getAvailableFunds', $e->getCode(), $error, $debug_print); fwrite($fp, writeLog($error)); }; @@ -152,7 +157,7 @@ if ($run_gift_tests === true) { } fwrite($fp, writeLog((array)$aws_test)); } catch (\Exception $e) { - $error = AmazonIncentives::decodeExceptionMessage($e->getMessage()); + $error = AmazonErrors::decodeExceptionMessage($e->getMessage()); printException('buyGiftCard', $e->getCode(), $error, $debug_print); fwrite($fp, writeLog($error)); } @@ -169,7 +174,7 @@ if ($run_gift_tests === true) { } fwrite($fp, writeLog((array)$aws_test)); } catch (\Exception $e) { - $error = AmazonIncentives::decodeExceptionMessage($e->getMessage()); + $error = AmazonErrors::decodeExceptionMessage($e->getMessage()); print "AWS: cancelGiftCard: " . $error['status'] . " [" . $e->getCode() . "]: " . $error['code'] . " | " . $error['type'] @@ -197,7 +202,7 @@ if ($run_gift_tests === true) { } fwrite($fp, writeLog((array)$aws_test)); } catch (\Exception $e) { - $error = AmazonIncentives::decodeExceptionMessage($e->getMessage()); + $error = AmazonErrors::decodeExceptionMessage($e->getMessage()); printException('buyGiftCard', $e->getCode(), $error, $debug_print); fwrite($fp, writeLog($error)); } @@ -221,7 +226,7 @@ if ($run_gift_tests === true) { } fwrite($fp, writeLog((array)$aws_test)); } catch (\Exception $e) { - $error = AmazonIncentives::decodeExceptionMessage($e->getMessage()); + $error = AmazonErrors::decodeExceptionMessage($e->getMessage()); printException('cancelGiftCard', $e->getCode(), $error, $debug_print); fwrite($fp, writeLog($error)); } @@ -242,7 +247,7 @@ if ($run_gift_tests === true) { } fwrite($fp, writeLog((array)$aws_test)); } catch (\Exception $e) { - $error = AmazonIncentives::decodeExceptionMessage($e->getMessage()); + $error = AmazonErrors::decodeExceptionMessage($e->getMessage()); printException('buyGiftCard', $e->getCode(), $error, $debug_print); fwrite($fp, writeLog($error)); } @@ -295,7 +300,7 @@ if ($run_mocks === true) { } fwrite($fp, writeLog((array)$aws_test)); } catch (Exception $e) { - $error = AmazonIncentives::decodeExceptionMessage($e->getMessage()); + $error = AmazonErrors::decodeExceptionMessage($e->getMessage()); print "AWS: MOCK: " . $creation_id . ": buyGiftCard: " . $error['status'] . " [" . $e->getCode() . "]: " . $error['code'] . " | " . $error['type'] diff --git a/test/phpUnit/AmazonIncentivesTest.php b/test/phpUnit/AmazonIncentivesTest.php new file mode 100644 index 0000000..214e6d0 --- /dev/null +++ b/test/phpUnit/AmazonIncentivesTest.php @@ -0,0 +1,948 @@ +expectException(AmazonIncentives\Exceptions\AmazonErrors::class); + // we don't need a class here, we just need client + $client = new AmazonIncentives\Client\Client(); + // produce any error + $client->request('invalid', [], ''); + } + + /** + * curl/connection error checks + * + * @return array + */ + public function amazonIncentivesProviderErrors(): array + { + // parameter data only for this + // 0: url + // 1: expected status + // 2: expected code + // 3: expected type + return [ + // C001 + 'C002 error' => [ + 'url' => 'invalid', + 'expected_status' => 'FAILURE', + 'expected_error' => 'C002', + 'expected_type' => 'CurlError' + ], + // T001 timeout + // 'T001 error' => [ + // 'url' => 'https://timeout.teq.jp', + // 'expected_status' => 'RESEND', + // 'expected_error' => 'T001', + // 'expected_type' => 'RateExceeded' + // ], + // other error + 'E999 error' => [ + 'url' => 'https://www.yahoo.co.jp', + 'expected_status' => 'FAILURE', + 'expected_error' => 'E999', + 'expected_type' => 'OtherUnknownError' + ] + ]; + } + + /** + * Test errors thrown in Client class + * + * @dataProvider amazonIncentivesProviderErrors + * @testdox AWS Incentives error handling [$_dataName] + * + * @param string $url + * @return void + */ + public function testAwsIncentivesCurlErrors( + string $url, + string $expected_status, + string $expected_error, + string $expected_type + ): void { + // HANDLE: + // * Init error + // - C001/Curl init error + // * Client errors (C002)/false: + // - CURLE_COULDNT_CONNECT + // - CURLE_COULDNT_RESOLVE_HOST + // - CURLE_OPERATION_TIMEOUTED + // - CURLE_SSL_PEER_CERTIFICATE + // - 0/OTHER + // * Client errors other + // - T001/Rate exceeded + // - E999/Other error + + // try/catch + // -decodeExceptionMessage (static) + + // we don't need the full interface here, we just need client class + $client = new AmazonIncentives\Client\Client(); + try { + // set expected throw error + $result = $client->request($url, [], ''); + print "R: " . $result . "\n"; + } catch (AmazonIncentives\Exceptions\AmazonErrors $e) { + $curl_error = AmazonIncentives\Exceptions\AmazonErrors::decodeExceptionMessage($e->getMessage()); + // print "E-B: " . print_r($curl_error, true) . "\n"; + $this->assertEquals( + $expected_status, + $curl_error['status'], + 'Assert error status' + ); + $this->assertEquals( + $expected_error, + $curl_error['code'], + 'Assert error code' + ); + $this->assertEquals( + $expected_type, + $curl_error['type'], + 'Assert error type' + ); + } + } + + /** + * init amazon incentive interface + * + * @param array $connect + * @param bool $mock + * @param array|null $mock_response + * @return AmazonIncentives\AmazonIncentives + */ + private function awsIncentivesStartUp( + array $connect, + bool $mock, + ?array $mock_response, + ): AmazonIncentives\AmazonIncentives { + $env_folder = $connect['env_folder'] ?? ''; + $env_file = $connect['env_file'] ?? ''; + $parameters = $connect['parameters'] ?? []; + // reset _ENV always + $_ENV = []; + // env file read status + $status = null; + if (!empty($env_folder)) { + if (!empty($env_file)) { + $status = DotEnv::readEnvFile($env_folder, $env_file); + } else { + $status = DotEnv::readEnvFile($env_folder); + } + } + + // ENV must match _ENV vars if set + if (!empty($env_folder) && $status != 0) { + // abort with error + $this->markTestSkipped( + 'Cannot read .env file needed for AWS tests: ' . $status + ); + } + + // MOCK: + // - for all buyGiftCard|cancelGiftCard|getAvailableFunds + // WHAT: + // \AWS->getCode|cancelCode|getBalance + // -> \AWS->makeReqeust + // -> NEW Client->request <= MOCK this + // NOT MOCK: + // any error calls in Client->request or exceptions + + if ($mock === true) { + // create a new config with or without parameters + $agcod_config = new AmazonIncentives\Config\Config( + $parameters['key'] ?? null, + $parameters['secret'] ?? null, + $parameters['partner'] ?? null, + $parameters['endpoint'] ?? null, + $parameters['currency'] ?? null, + $parameters['debug'] ?? null + ); + + // MOCK CLIENT + // Master mock the Client class for request call + // If we wan't to get errors thrown + /** @var AmazonIncentives\Client\Client&MockObject */ + $client_mock = $this->createPartialMock(AmazonIncentives\Client\Client::class, ['request']); + // set the needed return here + $client_mock->method('request')->willReturn(json_encode($mock_response)); + + // MOCK AWS and attache above class in client return + /** @var AmazonIncentives\AWS\AWS&MockObject */ + $aws_mock = $this->getMockBuilder(AmazonIncentives\AWS\AWS::class) + ->setConstructorArgs([$agcod_config]) + ->onlyMethods(['newClient']) + ->getMock(); + // attach mocked client + $aws_mock->method('newClient')->willReturn($client_mock); + + // MOCK AMAZONINCENTIVES + /** @var AmazonIncentives\AmazonIncentives&MockObject */ + $agcod = $this->getMockBuilder(AmazonIncentives\AmazonIncentives::class) + ->setConstructorArgs([ + $parameters['key'] ?? null, + $parameters['secret'] ?? null, + $parameters['partner'] ?? null, + $parameters['endpoint'] ?? null, + $parameters['currency'] ?? null, + $parameters['debug'] ?? null + ]) + ->onlyMethods(['newAWS']) + ->getMock(); + // attach mocked AWS class + $agcod->method('newAWS')->willReturn($aws_mock); + } else { + // if we mock, we mock the Client->request + $agcod = new AmazonIncentives\AmazonIncentives( + $parameters['key'] ?? null, + $parameters['secret'] ?? null, + $parameters['partner'] ?? null, + $parameters['endpoint'] ?? null, + $parameters['currency'] ?? null, + $parameters['debug'] ?? null + ); + } + + return $agcod; + } + + /** + * Holds the configs for loading data from .env for parameter + * + * @return array + */ + public function awsIncentivesProvider(): array + { + // 0: .env file folder + // 1: .env file name (if not set use .env) + // 2: parameters that override _ENV variables + return [ + // this is with real test account data + 'env_test' => [ + 'env_folder' => __DIR__ . DIRECTORY_SEPARATOR . '..', + 'env_file' => null, + 'parameters' => null + ], + // this is for mocking only + 'parameter_dummy' => [ + 'env_folder' => null, + 'env_file' => null, + 'parameters' => [ + null, + null, + null, + 'http://i.dont.exist.at.all', + 'JPY' + ] + ], + ]; + } + + /** + * Undocumented function + * + * @return array + */ + public function amazonIncentivesProviderGetFunds(): array + { + // remove final keyword + // BypassFinals::enable(); + // get connectors + $connectors = $this->awsIncentivesProvider(); + // 0: connect array (env file, env folder, parameters array) + // 1: mock or normal call + // 2: if mock connect response must be defined here + // 3: exepcted response array + return [ + 'non mock test data' => [ + 'connect' => $connectors['env_test'], + 'mock' => false, + 'mock_response' => null, + 'expected' => [ + // + ] + ], + 'mock data test' => [ + 'connect' => $connectors['parameter_dummy'], + 'mock' => true, + 'mock_response' => [ + 'availableFunds' => [ + 'amount' => 0.0, + 'currencyCode' => 'JPY', + ], + 'status' => 'SUCCESS', + 'timestamp' => '20220610T085450Z', + ], + ], + ]; + } + + /** + * Undocumented function + * + * @dataProvider amazonIncentivesProviderGetFunds + * @testdox AWS Incentives get available funds [$_dataName] + * + * @param array $connect + * @param bool $mock + * @param array|null $mock_response + * @return void + */ + public function testAwsIncentivesGetAvailableFunds( + array $connect, + bool $mock, + ?array $mock_response + ): void { + // load class + $agcod = $this->awsIncentivesStartUp( + $connect, + $mock, + $mock_response, + ); + + // - getAvailableFunds: get available fund + // - getStatus + // - getAmount + // - getCurrency + // - getTimestamp + $funds = $agcod->getAvailableFunds(); + // if not mock do type check + // if mock do matching check from mcok + if ($mock === false) { + $this->assertEquals( + 'SUCCESS', + $funds->getStatus(), + 'Assert status is success' + ); + // numeric number + $this->assertIsNumeric( + $funds->getAmount(), + 'Assert amoount is numerc' + ); + // USD, JPY, etc + $this->assertIsString( + $funds->getCurrency(), + 'Assert currency is string' + ); + // 20220610T085450Z + $this->assertMatchesRegularExpression( + "/^\d{8}T\d{6}Z$/", + $funds->getTimestamp(), + 'Assert timestamp matches regex' + ); + } else { + $this->assertEquals( + $mock_response['status'], + $funds->getStatus(), + 'Assert mock status' + ); + $this->assertEquals( + $mock_response['availableFunds']['amount'], + $funds->getAmount(), + 'Assert mock amount' + ); + $this->assertEquals( + $mock_response['availableFunds']['currencyCode'], + $funds->getCurrency(), + 'Assert mock currency code' + ); + $this->assertEquals( + $mock_response['timestamp'], + $funds->getTimestamp(), + 'Assert mock timestamp' + ); + } + } + + /** + * Undocumented function + * + * @return array + */ + public function amazonIncentivesProviderBuy(): array + { + // get connectors + $connectors = $this->awsIncentivesProvider(); + // 0: connect array (env file, env folder, parameters array) + // 1: mock or normal call + // 2: if mock connect response must be defined here + // 3: exepcted response array + // 4: value in float + return [ + 'non mock test data' => [ + 'connect' => $connectors['env_test'], + 'mock' => false, + 'mock_response' => null, + 'amount' => 500.0, + ], + 'mock data test' => [ + 'connect' => $connectors['parameter_dummy'], + 'mock' => true, + 'mock_response' => [ + 'cardInfo' => [ + 'cardNumber' => null, + 'cardStatus' => 'Fulfilled', + 'expirationDate' => null, + 'value' => [ + 'amount' => 1000.0, + 'currencyCode' => 'JPY', + ], + ], + 'creationRequestId' => 'PartnerId_62a309167e7a4', + 'gcClaimCode' => 'LJ49-AKDUV6-UYCP', + 'gcExpirationDate' => 'Thu Jun 10 14:59:59 UTC 2032', + 'gcId' => '5535125272070255', + 'status' => 'SUCCESS', + ], + 'amount' => 1000.0, + ], + ]; + } + + /** + * Undocumented function + * + * @dataProvider amazonIncentivesProviderBuy + * @testdox AWS Incentives buy gift card [$_dataName] + * + * @param array $connect + * @param bool $mock + * @param array|null $mock_response + * @param float $amount + * @return void + */ + public function testAwsIncentivesBuyGiftCard( + array $connect, + bool $mock, + ?array $mock_response, + float $amount + ): void { + // - init plain + // * via ::make() + + // - buyGiftCard: buy gift card + // - getCreationRequestId + // - getId + // - getClaimCode + // - getExpirationDate + // - getStatus + + // load class + $agcod = $this->awsIncentivesStartUp( + $connect, + $mock, + $mock_response, + ); + + $response = $agcod->buyGiftCard($amount); + + if ($mock === false) { + // type check + $this->assertEquals( + 'SUCCESS', + $response->getStatus(), + 'Assert status' + ); + // creation request id must start with partner id + $this->assertStringStartsWith( + $agcod->checkMe()['CONFIG']->getPartner(), + $response->getCreationRequestId(), + 'Assert creation request id starts with partner id' + ); + // gift card id is number + $this->assertIsNumeric( + $response->getId(), + 'Assert gift card id is numeric' + ); + // claim code is 4-6-4 alphanumeric + $this->assertIsString( + $response->getClaimCode(), + 'Assert claim code is string' + ); + // only for requests outside US/Australia cards + // expiration date: Thu Jun 10 14:59:59 UTC 2032 + } else { + // value match to mock response + $this->assertEquals( + $mock_response['status'], + $response->getStatus(), + 'Assert mock status' + ); + $this->assertEquals( + $mock_response['creationRequestId'], + $response->getCreationRequestId(), + 'Assert mock creation request id' + ); + $this->assertEquals( + $mock_response['gcId'], + $response->getId(), + 'Assert mock gift card id' + ); + $this->assertEquals( + $mock_response['gcClaimCode'], + $response->getClaimCode(), + 'Assert mock claim code' + ); + $this->assertEquals( + $mock_response['gcExpirationDate'], + $response->getExpirationDate(), + 'Assert mock expiration date' + ); + } + } + + /** + * Buy a gift card and use same creation request id to get another gift card + * has to return same data ggain + * + * @dataProvider amazonIncentivesProviderBuy + * @testdox AWS Incentives buy gift card and again with same creation request id [$_dataName] + * + * @param array $connect + * @param bool $mock + * @param array|null $mock_response + * @param float $amount + * @return void + */ + public function testAwsIncentivesSameBuyGiftCard( + array $connect, + bool $mock, + ?array $mock_response, + float $amount + ): void { + // load class + $agcod = $this->awsIncentivesStartUp( + $connect, + $mock, + $mock_response, + ); + // get one + $response_a = $agcod->buyGiftCard($amount); + // get one again with same code + $response_b = $agcod->buyGiftCard($amount, $response_a->getCreationRequestId()); + + // a and b must be equalt + $this->assertEquals( + $response_a->getStatus(), + $response_b->getStatus(), + 'Assert status' + ); + $this->assertEquals( + $response_a->getCreationRequestId(), + $response_b->getCreationRequestId(), + 'Assert creation request id' + ); + $this->assertEquals( + $response_a->getId(), + $response_b->getId(), + 'Assert gift card id' + ); + $this->assertEquals( + $response_a->getClaimCode(), + $response_b->getClaimCode(), + 'Assert claim code' + ); + $this->assertEquals( + $response_a->getExpirationDate(), + $response_b->getExpirationDate(), + 'Assert expiration date' + ); + } + + /** + * Undocumented function + * + * @return array + */ + public function amazonIncentivesProviderCancel(): array + { + // get connectors + $connectors = $this->awsIncentivesProvider(); + // 0: connect array (env file, env folder, parameters array) + // 1: mock or normal call + // 2: if mock connect response must be defined here + // 3: exepcted response array + return [ + 'non mock test data' => [ + 'connect' => $connectors['env_test'], + 'mock' => false, + 'mock_response' => null, + ], + 'mock data test' => [ + 'connect' => $connectors['parameter_dummy'], + 'mock' => true, + 'mock_response' => [ + 'creationRequestId' => 'PartnerId_62a309167e7a4', + 'gcId' => '5535125272070255', + 'status' => 'SUCCESS', + ], + ], + ]; + } + + /** + * Undocumented function + * + * @dataProvider amazonIncentivesProviderCancel + * @testdox AWS Incentives cancel gift card [$_dataName] + * + * @param array $connect + * @param bool $mock + * @param array|null $mock_response + * @return void + */ + public function testAwsIncentivesCancelGiftCard( + array $connect, + bool $mock, + ?array $mock_response + ): void { + // - cancelGiftCard: cancel gift card + // load class + $agcod = $this->awsIncentivesStartUp( + $connect, + $mock, + $mock_response, + ); + + if ($mock === false) { + // get a gift card, then cancel it + $purchase = $agcod->buyGiftCard(500.0); + $response = $agcod->cancelGiftCard( + $purchase->getCreationRequestId(), + $purchase->getId() + ); + $this->assertEquals( + 'SUCCESS', + $response->getStatus(), + 'Assert mock status' + ); + // creation request id must start with partner id + $this->assertStringStartsWith( + $agcod->checkMe()['CONFIG']->getPartner(), + $response->getCreationRequestId(), + 'Assert creation request id starts with partner id' + ); + // gift card id is number + $this->assertIsNumeric( + $response->getId(), + 'Assert gift card id is numeric' + ); + } else { + $response = $agcod->cancelGiftCard( + $mock_response['creationRequestId'], + $mock_response['gcId'] + ); + $this->assertEquals( + $mock_response['status'], + $response->getStatus(), + 'Assert mock status' + ); + $this->assertEquals( + $mock_response['creationRequestId'], + $response->getCreationRequestId(), + 'Assert mock creation request id' + ); + $this->assertEquals( + $mock_response['gcId'], + $response->getId(), + 'Assert mock gift card id' + ); + } + } + + /** + * list of AWS mock codes for AWS side mock testing + * + * @return array + */ + public function awsIncentivesMockProvider(): array + { + return [ + 'successMock' => [ + 'creation_request_id' => 'F0000', + 'return_code' => '', + 'status' => 'SUCCESS' + ], + 'SimpleAmountIsNull' => [ + 'creation_request_id' => 'F1000', + 'return_code' => 'F100', + 'status' => 'FAILURE' + ], + 'InvalidAmountInput' => [ + 'creation_request_id' => 'F2003', + 'return_code' => 'F200', + 'status' => 'FAILURE' + ], + 'InvalidAmountValue' => [ + 'creation_request_id' => 'F2004', + 'return_code' => 'F200', + 'status' => 'FAILURE' + ], + 'InvalidCurrencyCodeInput' => [ + 'creation_request_id' => 'F2005', + 'return_code' => 'F200', + 'status' => 'FAILURE' + ], + 'CardActivatedWithDifferentRequestId' => [ + 'creation_request_id' => 'F2010', + 'return_code' => 'F200', + 'status' => 'FAILURE' + ], + 'MaxAmountExceeded' => [ + 'creation_request_id' => 'F2015', + 'return_code' => 'F200', + 'status' => 'FAILURE' + ], + 'CurrencyCodeMismatch' => [ + 'creation_request_id' => 'F2016', + 'return_code' => 'F200', + 'status' => 'FAILURE' + ], + 'FractionalAmountNotAllowed' => [ + 'creation_request_id' => 'F2017', + 'return_code' => 'F200', + 'status' => 'FAILURE' + ], + 'CancelRequestArrivedAfterTimeLimit' => [ + 'creation_request_id' => 'F2047', + 'return_code' => 'F200', + 'status' => 'FAILURE' + ], + 'InsufficientFunds' => [ + 'creation_request_id' => 'F3003', + 'return_code' => 'F300', + 'status' => 'FAILURE' + ], + 'AccountHasProblems' => [ + 'creation_request_id' => 'F3005', + 'return_code' => 'F300', + 'status' => 'FAILURE' + ], + 'CustomerSurpassedDailyVelocityLimit' => [ + 'creation_request_id' => 'F3010', + 'return_code' => 'F300', + 'status' => 'FAILURE' + ], + 'SystemTemporarilyUnavailable' => [ + 'creation_request_id' => 'F4000', + 'return_code' => 'F400', + 'status' => 'RESEND' + ], + 'UnknownError' => [ + 'creation_request_id' => 'F5000', + 'return_code' => 'F500', + 'status' => 'FAILURE' + ], + ]; + } + + /** + * NOTE: Must have a valid test user connection setup + * This only works with a valid server connection. + * Runs through AWS Incentives mock values and checks the return code and status + * + * @dataProvider awsIncentivesMockProvider + * @testdox AWS Incentives Mock $creation_request_id will be $expected_status with $expected_code [$_dataName] + * + * @return void + */ + public function testAwsIncentivesWithMocks( + string $creation_request_id, + string $expected_code, + string $expected_status, + ): void { + // reset _ENV for reading + $_ENV = []; + // read the .env file + $status = DotEnv::readEnvFile(__DIR__ . DIRECTORY_SEPARATOR . '..'); + // if loading failed, abort + if ($status != 0) { + // abort with error + $this->markTestSkipped( + 'Cannot read .env file needed for AWS mock tests: ' . $status + ); + } + // if no value set, set to 500 + $value = $_ENV['AWS_MOCK_VALUE'] ?? 500; + // run tests + try { + $aws_gcod = AmazonIncentives\AmazonIncentives::make()->buyGiftCard( + (float)$value, + $creation_request_id + ); + $this->assertEquals( + $expected_status, + $aws_gcod->getStatus(), + 'Assert status ok in AWS GCOD mocks' + ); + } catch (\Exception $e) { + $error = AmazonIncentives\Exceptions\AmazonErrors::decodeExceptionMessage($e->getMessage()); + $this->assertEquals( + [ + 'code' => $expected_code, + 'status' => $expected_status, + ], + [ + 'code' => $error['code'], + 'status' => $error['status'], + ], + 'Assert status failed in AWS GCOD mocks' + ); + } + // wait a moment between tests + sleep($this->mock_wait); + } + + /** + * Undocumented function + * + * @return array + */ + public function checkMeProvider(): array + { + // 0: .env file folder + // 1: .env file name (if not set use .env) + // 2: parameters that override _ENV variables + return [ + 'default all empty' => [ + 'use_env' => null, + 'env_file' => null, + 'parameters' => null, + ], + 'set parameters' => [ + 'env_folder' => null, + 'env_file' => null, + 'parameters' => [ + 'key' => 'key', + 'secret' => 'secret', + 'partner' => 'partner id', + 'endpoint' => 'https://endpoint.test.com', + 'currency' => 'currency', + 'debug' => true, + ], + 'expected' => [], + ], + 'load from env' => [ + 'env_folder' => __DIR__ . DIRECTORY_SEPARATOR . '..', + 'env_file' => null, + 'parameters' => null, + ], + 'load from env, but override parameter' => [ + 'env_folder' => __DIR__ . DIRECTORY_SEPARATOR . '..', + 'env_file' => null, + 'parameters' => [ + 'key' => 'key', + 'secret' => 'secret', + 'partner' => 'partner id', + 'endpoint' => 'https://endpoint.test.com', + 'currency' => 'currency', + ] + ] + // test missing parameter, set vie _ENV + ]; + } + + /** + * Check the checkMe function that will work with or without any settings + * passed on. + * This also tests basic loading + * - parseing for endoint as url + * - override check for _ENV vs parameter + * + * @cover ::checkMe + * @dataProvider checkMeProvider + * @testdox AmazonIncentives tests [$_dataName] + * + * @param string|null $env_folder + * @param string|null $env_file + * @param array|null $parameters + * @return void + */ + public function testCheckMe(?string $env_folder, ?string $env_file, ?array $parameters): void + { + // reset _ENV before each run to avoid nothing to load errors + $_ENV = []; + // env load status + $status = null; + if (!empty($env_folder)) { + if (!empty($env_file)) { + $status = DotEnv::readEnvFile($env_folder, $env_file); + } else { + $status = DotEnv::readEnvFile($env_folder); + } + } + if (!empty($parameters)) { + $aws = new AmazonIncentives\AmazonIncentives( + $parameters['key'], + $parameters['secret'], + $parameters['partner'], + $parameters['endpoint'], + $parameters['currency'], + $parameters['debug'] ?? null, + ); + } else { + $aws = new AmazonIncentives\AmazonIncentives(); + } + $aws_check_me = $aws->checkMe(); + // ENV must match _ENV vars if set + if (!empty($env_folder) && $status != 0) { + // abort with error + $this->markTestSkipped( + 'Cannot read .env file needed: ' . $status + ); + } elseif (!empty($env_folder)) { + $this->assertEquals( + $_ENV, + $aws_check_me['ENV'], + 'Assert _ENV set equal' + ); + } + // compare that data matches + // print "CM: " . print_r($aws_check_me, true) . "\n"; + // CONFIG must match to parameters or ENV, parsed host name check + $this->assertEquals( + // parameter > _ENV -> empty + !empty($parameters['partner']) ? + $parameters['partner'] : + $_ENV['AWS_GIFT_CARD_PARTNER_ID'] ?? '', + $aws_check_me['CONFIG']->getPartner(), + 'Assert config matching input' + ); + // KEY must match access_key/AWS_GIFT_CARD_KEY + $this->assertEquals( + $aws_check_me['CONFIG']->getAccessKey(), + $aws_check_me['KEY'], + 'Assert access key m' + ); + } +} + +// __END__ diff --git a/test/phpUnit/Hook/BypassFinalHook.php b/test/phpUnit/Hook/BypassFinalHook.php new file mode 100644 index 0000000..d36ef37 --- /dev/null +++ b/test/phpUnit/Hook/BypassFinalHook.php @@ -0,0 +1,21 @@ +