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 @@
+