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/composer.lock b/composer.lock
index 6e9b29e..93f5eed 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,9 +4,63 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "412ca7fe7d62707c222169f4a7deeaec",
+ "content-hash": "717f24a1578a5d25a0522cfb908b13cc",
"packages": [],
"packages-dev": [
+ {
+ "name": "dg/bypass-finals",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dg/bypass-finals.git",
+ "reference": "fb62dc6ab1a097e234fa1567943d8e87ea4d0842"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dg/bypass-finals/zipball/fb62dc6ab1a097e234fa1567943d8e87ea4d0842",
+ "reference": "fb62dc6ab1a097e234fa1567943d8e87ea4d0842",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "nette/tester": "^2.3",
+ "phpstan/phpstan": "^0.12"
+ },
+ "default-branch": true,
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause",
+ "GPL-2.0",
+ "GPL-3.0"
+ ],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com"
+ }
+ ],
+ "description": "Removes final keyword from source code on-the-fly and allows mocking of final methods and classes",
+ "keywords": [
+ "finals",
+ "mocking",
+ "phpunit",
+ "testing",
+ "unit"
+ ],
+ "support": {
+ "issues": "https://github.com/dg/bypass-finals/issues",
+ "source": "https://github.com/dg/bypass-finals/tree/master"
+ },
+ "time": "2022-04-14T12:24:34+00:00"
+ },
{
"name": "doctrine/instantiator",
"version": "1.5.x-dev",
@@ -2090,7 +2144,8 @@
"aliases": [],
"minimum-stability": "dev",
"stability-flags": {
- "gullevek/dotenv": 20
+ "gullevek/dotenv": 20,
+ "dg/bypass-finals": 20
},
"prefer-stable": false,
"prefer-lowest": false,
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
index 381decd..214e6d0 100644
--- a/test/phpUnit/AmazonIncentivesTest.php
+++ b/test/phpUnit/AmazonIncentivesTest.php
@@ -2,10 +2,11 @@
declare(strict_types=1);
-namespace tests;
+namespace test\phpUnit;
use PHPUnit\Framework\TestCase;
-use gullevek\AmazonIncentives\AmazonIncentives;
+use PHPUnit\Framework\MockObject\MockObject;
+use gullevek\AmazonIncentives;
use gullevek\dotEnv\DotEnv;
/**
@@ -15,71 +16,932 @@ use gullevek\dotEnv\DotEnv;
*/
final class AmazonIncentivesTest extends TestCase
{
- public function amazonIncentivesProvider(): array
+ /** @var int wait tme in seconds between AWS side mock calls */
+ private $mock_wait = 1;
+
+ /**
+ * Client curl exception testing
+ *
+ * @testdox AWS Incentives curl exception handling
+ *
+ * @return void
+ */
+ public function testAwsIncentivesCurlException(): void
{
+ // this is the exceptio we want
+ $this->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 [
- 'empty' => [
+ // 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
*
- * @dataProvider amazonIncentivesProvider
- * @testdox AmazonIncentives tests [$_dataName]
+ * @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 testAmazonIncentives(?string $env_folder, ?string $env_file): 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
- // - cancelGiftCard: cancel gift card
- // - getAvailableFunds: get available fund
- // - getAmount
- // - getCurrency
- // - getTimestamp
- // try/catch
- // -decodeExceptionMessage (static)
- $this->markTestSkipped('Not yet implemented: AmazonIncentives');
+ // 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'
+ );
+ }
}
- public function checkMeProvider(): array
+ /**
+ * 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 [
- 'default' => [
- 'env_folder' => null,
- 'env_file' => null,
- 'expected' => [],
- ]
+ '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 $expected): void
+ public function testCheckMe(?string $env_folder, ?string $env_file, ?array $parameters): void
{
- $aws = new AmazonIncentives();
+ // 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";
+ // 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'
+ );
}
}
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 @@
+ $vendorDir . '/composer/InstalledVersions.php',
+ 'DG\\BypassFinals' => $vendorDir . '/dg/bypass-finals/src/BypassFinals.php',
'PHPUnit\\Exception' => $vendorDir . '/phpunit/phpunit/src/Exception.php',
'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ActualValueIsNotAnObjectException.php',
'PHPUnit\\Framework\\Assert' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert.php',
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index 5211bf2..55d32cd 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -76,6 +76,7 @@ class ComposerStaticInit0c8f6bec90a6d60040a922f19a1f0e64
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+ 'DG\\BypassFinals' => __DIR__ . '/..' . '/dg/bypass-finals/src/BypassFinals.php',
'PHPUnit\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Exception.php',
'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ActualValueIsNotAnObjectException.php',
'PHPUnit\\Framework\\Assert' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert.php',
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index e286566..a41a152 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -1,5 +1,62 @@
{
"packages": [
+ {
+ "name": "dg/bypass-finals",
+ "version": "dev-master",
+ "version_normalized": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dg/bypass-finals.git",
+ "reference": "fb62dc6ab1a097e234fa1567943d8e87ea4d0842"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dg/bypass-finals/zipball/fb62dc6ab1a097e234fa1567943d8e87ea4d0842",
+ "reference": "fb62dc6ab1a097e234fa1567943d8e87ea4d0842",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "nette/tester": "^2.3",
+ "phpstan/phpstan": "^0.12"
+ },
+ "time": "2022-04-14T12:24:34+00:00",
+ "default-branch": true,
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause",
+ "GPL-2.0",
+ "GPL-3.0"
+ ],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com"
+ }
+ ],
+ "description": "Removes final keyword from source code on-the-fly and allows mocking of final methods and classes",
+ "keywords": [
+ "finals",
+ "mocking",
+ "phpunit",
+ "testing",
+ "unit"
+ ],
+ "support": {
+ "issues": "https://github.com/dg/bypass-finals/issues",
+ "source": "https://github.com/dg/bypass-finals/tree/master"
+ },
+ "install-path": "../dg/bypass-finals"
+ },
{
"name": "doctrine/instantiator",
"version": "1.5.x-dev",
@@ -2184,6 +2241,7 @@
],
"dev": true,
"dev-package-names": [
+ "dg/bypass-finals",
"doctrine/instantiator",
"gullevek/dotenv",
"myclabs/deep-copy",
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
index 6c0a3e5..6b02720 100644
--- a/vendor/composer/installed.php
+++ b/vendor/composer/installed.php
@@ -5,11 +5,22 @@
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
- 'reference' => '1e836e9a2bd6e41c4555cf917fe645b453cb5b79',
+ 'reference' => 'fd5477269b9a133448cecb5854b784033d6c6c62',
'name' => 'gullevek/amazon-incentives',
'dev' => true,
),
'versions' => array(
+ 'dg/bypass-finals' => array(
+ 'pretty_version' => 'dev-master',
+ 'version' => 'dev-master',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../dg/bypass-finals',
+ 'aliases' => array(
+ 0 => '9999999-dev',
+ ),
+ 'reference' => 'fb62dc6ab1a097e234fa1567943d8e87ea4d0842',
+ 'dev_requirement' => true,
+ ),
'doctrine/instantiator' => array(
'pretty_version' => '1.5.x-dev',
'version' => '1.5.9999999.9999999-dev',
@@ -25,7 +36,7 @@
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
- 'reference' => '1e836e9a2bd6e41c4555cf917fe645b453cb5b79',
+ 'reference' => 'fd5477269b9a133448cecb5854b784033d6c6c62',
'dev_requirement' => false,
),
'gullevek/dotenv' => array(
diff --git a/vendor/dg/bypass-finals/composer.json b/vendor/dg/bypass-finals/composer.json
new file mode 100644
index 0000000..345d4b1
--- /dev/null
+++ b/vendor/dg/bypass-finals/composer.json
@@ -0,0 +1,26 @@
+{
+ "name": "dg/bypass-finals",
+ "description": "Removes final keyword from source code on-the-fly and allows mocking of final methods and classes",
+ "keywords": ["testing", "unit", "phpunit", "mocking", "finals"],
+ "license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com"
+ }
+ ],
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "nette/tester": "^2.3",
+ "phpstan/phpstan": "^0.12"
+ },
+ "autoload": {
+ "classmap": ["src/"]
+ },
+ "scripts": {
+ "phpstan": "phpstan analyse",
+ "tester": "tester tests -s"
+ }
+}
diff --git a/vendor/dg/bypass-finals/license.md b/vendor/dg/bypass-finals/license.md
new file mode 100644
index 0000000..03bf49f
--- /dev/null
+++ b/vendor/dg/bypass-finals/license.md
@@ -0,0 +1,49 @@
+Licenses
+========
+
+Good news! You may use Nette Tester under the terms of either
+the New BSD License or the GNU General Public License (GPL) version 2 or 3.
+
+
+
+New BSD License
+---------------
+
+Copyright (c) 2004, 2013 David Grudl (https://davidgrudl.com)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of "Nette Tester" nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+This software is provided by the copyright holders and contributors "as is" and
+any express or implied warranties, including, but not limited to, the implied
+warranties of merchantability and fitness for a particular purpose are
+disclaimed. In no event shall the copyright owner or contributors be liable for
+any direct, indirect, incidental, special, exemplary, or consequential damages
+(including, but not limited to, procurement of substitute goods or services;
+loss of use, data, or profits; or business interruption) however caused and on
+any theory of liability, whether in contract, strict liability, or tort
+(including negligence or otherwise) arising in any way out of the use of this
+software, even if advised of the possibility of such damage.
+
+
+
+GNU General Public License
+--------------------------
+
+GPL licenses are very very long, so instead of including them here we offer
+you URLs with full text:
+
+- [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html)
+- [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/vendor/dg/bypass-finals/readme.md b/vendor/dg/bypass-finals/readme.md
new file mode 100644
index 0000000..50f0734
--- /dev/null
+++ b/vendor/dg/bypass-finals/readme.md
@@ -0,0 +1,64 @@
+Bypass Finals
+=============
+
+[](https://packagist.org/packages/dg/bypass-finals)
+[](https://github.com/dg/bypass-finals/actions)
+[](https://github.com/dg/bypass-finals/releases)
+[](https://github.com/dg/bypass-finals/blob/master/license.md)
+
+
+Introduction
+------------
+
+Removes final keywords from source code on-the-fly and allows mocking of final methods and classes.
+It can be used together with any test tool such as PHPUnit, Mockery or [Nette Tester](https://tester.nette.org).
+
+
+Installation
+------------
+
+The recommended way to install is through Composer:
+
+```
+composer require dg/bypass-finals --dev
+```
+
+It requires PHP version 7.1 (or 5.6 in case of release 1.1) and supports PHP up to 8.1.
+
+
+Usage
+-----
+
+Simply call this:
+
+```php
+DG\BypassFinals::enable();
+```
+
+You need to enable it before the classes you want to remove the final are loaded. So call it as soon as possible,
+preferably right after `vendor/autoload.php` is loaded.
+
+Note that final internal PHP classes like `Closure` cannot be mocked.
+
+You can choose to only bypass finals in specific files or directories:
+
+```php
+DG\BypassFinals::setWhitelist([
+ '*/Nette/*',
+]);
+```
+
+This gives you finer control and can solve issues with certain frameworks and libraries.
+
+You can try to increase performance by using the cache (the directory must exist):
+
+```php
+DG\BypassFinals::$cacheDir = __DIR__ . '/tmp';
+```
+
+Support Project
+---------------
+
+Do you like BypassFinals?
+
+[](https://nette.org/make-donation?to=bypass-finals)
diff --git a/vendor/dg/bypass-finals/src/BypassFinals.php b/vendor/dg/bypass-finals/src/BypassFinals.php
new file mode 100644
index 0000000..9fde53f
--- /dev/null
+++ b/vendor/dg/bypass-finals/src/BypassFinals.php
@@ -0,0 +1,312 @@
+handle);
+ }
+
+
+ public function dir_opendir(string $path, int $options): bool
+ {
+ $this->handle = $this->context
+ ? $this->native('opendir', $path, $this->context)
+ : $this->native('opendir', $path);
+ return (bool) $this->handle;
+ }
+
+
+ public function dir_readdir()
+ {
+ return readdir($this->handle);
+ }
+
+
+ public function dir_rewinddir(): bool
+ {
+ return (bool) rewinddir($this->handle);
+ }
+
+
+ public function mkdir(string $path, int $mode, int $options): bool
+ {
+ $recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE);
+ return $this->context
+ ? $this->native('mkdir', $path, $mode, $recursive, $this->context)
+ : $this->native('mkdir', $path, $mode, $recursive);
+ }
+
+
+ public function rename(string $pathFrom, string $pathTo): bool
+ {
+ return $this->context
+ ? $this->native('rename', $pathFrom, $pathTo, $this->context)
+ : $this->native('rename', $pathFrom, $pathTo);
+ }
+
+
+ public function rmdir(string $path, int $options): bool
+ {
+ return $this->context
+ ? $this->native('rmdir', $path, $this->context)
+ : $this->native('rmdir', $path);
+ }
+
+
+ public function stream_cast(int $castAs)
+ {
+ return $this->handle;
+ }
+
+
+ public function stream_close(): void
+ {
+ fclose($this->handle);
+ }
+
+
+ public function stream_eof(): bool
+ {
+ return feof($this->handle);
+ }
+
+
+ public function stream_flush(): bool
+ {
+ return fflush($this->handle);
+ }
+
+
+ public function stream_lock(int $operation): bool
+ {
+ return $operation
+ ? flock($this->handle, $operation)
+ : true;
+ }
+
+
+ public function stream_metadata(string $path, int $option, $value): bool
+ {
+ switch ($option) {
+ case STREAM_META_TOUCH:
+ return $this->native('touch', $path, $value[0] ?? time(), $value[1] ?? time());
+ case STREAM_META_OWNER_NAME:
+ case STREAM_META_OWNER:
+ return $this->native('chown', $path, $value);
+ case STREAM_META_GROUP_NAME:
+ case STREAM_META_GROUP:
+ return $this->native('chgrp', $path, $value);
+ case STREAM_META_ACCESS:
+ return $this->native('chmod', $path, $value);
+ }
+
+ return false;
+ }
+
+
+ public function stream_open(string $path, string $mode, int $options, ?string &$openedPath): bool
+ {
+ $usePath = (bool) ($options & STREAM_USE_PATH);
+ if ($mode === 'rb' && pathinfo($path, PATHINFO_EXTENSION) === 'php' && self::isPathInWhiteList($path)) {
+ if (self::$prevWrapper) {
+ $content = null;
+ self::$prevWrapper->stream_open($path, $mode, $options, $openedPath);
+ while (!self::$prevWrapper->stream_eof()) {
+ $content .= self::$prevWrapper->stream_read(8192);
+ }
+
+ self::$prevWrapper->stream_close();
+ } else {
+ $content = $this->native('file_get_contents', $path, $usePath, $this->context);
+ }
+ if (!is_string($content)) {
+ return false;
+ }
+ $modified = self::cachedRemoveFinals($content);
+ if ($modified !== $content) {
+ $this->handle = tmpfile();
+ $this->native('fwrite', $this->handle, $modified);
+ $this->native('fseek', $this->handle, 0);
+ return true;
+ }
+ }
+ $this->handle = $this->context
+ ? $this->native('fopen', $path, $mode, $usePath, $this->context)
+ : $this->native('fopen', $path, $mode, $usePath);
+ return (bool) $this->handle;
+ }
+
+
+ public function stream_read(int $count)
+ {
+ return fread($this->handle, $count);
+ }
+
+
+ public function stream_seek(int $offset, int $whence = SEEK_SET): bool
+ {
+ return fseek($this->handle, $offset, $whence) === 0;
+ }
+
+
+ public function stream_set_option(int $option, int $arg1, ?int $arg2): bool
+ {
+ return false;
+ }
+
+
+ public function stream_stat()
+ {
+ return fstat($this->handle);
+ }
+
+
+ public function stream_tell()
+ {
+ return ftell($this->handle);
+ }
+
+
+ public function stream_truncate(int $newSize): bool
+ {
+ return ftruncate($this->handle, $newSize);
+ }
+
+
+ public function stream_write(string $data)
+ {
+ return fwrite($this->handle, $data);
+ }
+
+
+ public function unlink(string $path): bool
+ {
+ return $this->native('unlink', $path);
+ }
+
+
+ public function url_stat(string $path, int $flags)
+ {
+ $func = $flags & STREAM_URL_STAT_LINK ? 'lstat' : 'stat';
+ return $flags & STREAM_URL_STAT_QUIET
+ ? @$this->native($func, $path)
+ : $this->native($func, $path);
+ }
+
+
+ private function native(string $func)
+ {
+ stream_wrapper_restore(self::PROTOCOL);
+ try {
+ return $func(...array_slice(func_get_args(), 1));
+ } finally {
+ stream_wrapper_unregister(self::PROTOCOL);
+ stream_wrapper_register(self::PROTOCOL, self::class);
+ }
+ }
+
+
+ public static function cachedRemoveFinals(string $code): string
+ {
+ if (stripos($code, 'final') === false) {
+ // do nothing
+
+ } elseif (self::$cacheDir) {
+ $hash = sha1($code);
+ if ($file = @fopen(self::$cacheDir . '/' . $hash, 'r')) { // @ may not exist
+ flock($file, LOCK_SH);
+ if ($res = stream_get_contents($file)) {
+ return $res;
+ }
+ }
+ $code = self::removeFinals($code);
+ if ($file = @fopen(self::$cacheDir . '/' . $hash, 'x')) { // @ may exist
+ flock($file, LOCK_EX);
+ fwrite($file, $code);
+ }
+
+ } else {
+ $code = self::removeFinals($code);
+ }
+
+ return $code;
+ }
+
+
+ public static function removeFinals(string $code): string
+ {
+ try {
+ $tokens = token_get_all($code, TOKEN_PARSE);
+ } catch (\ParseError $e) {
+ return $code;
+ }
+
+ $code = '';
+ foreach ($tokens as $token) {
+ $code .= is_array($token)
+ ? ($token[0] === T_FINAL ? '' : $token[1])
+ : $token;
+ }
+
+ return $code;
+ }
+
+
+ private static function isPathInWhiteList(string $path): bool
+ {
+ $path = strtr($path, '\\', '/');
+ foreach (self::$pathWhitelist as $mask) {
+ if (fnmatch($mask, $path)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}