-
Notifications
You must be signed in to change notification settings - Fork 3
Doug/abn 287 error handling #77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
d5ca31d
ABN-287/feat: add PHPUnit test infrastructure and unit tests
dgjlindsay 019a688
ABN-287/test: document env var precedence over explicit mode parameter
dgjlindsay 779565e
ABN-287/fix: pin PHPUnit phar version and verify SHA256 checksum
dgjlindsay 51c17f3
ABN-287/test: add E2E test suite for Adapter against staging API
dgjlindsay 11a800a
ABN-287/test: commit stubs, unit tests and docs from previous session
dgjlindsay 8ac0cec
chore: remove session artifacts from public repo
dgjlindsay File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| name: PHPUnit | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
| branches: [main] | ||
|
|
||
| jobs: | ||
| test: | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| matrix: | ||
| php-version: ['7.4', '8.1', '8.2'] | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up PHP ${{ matrix.php-version }} | ||
| uses: shivammathur/setup-php@v2 | ||
| with: | ||
| php-version: ${{ matrix.php-version }} | ||
|
|
||
| - name: Download PHPUnit | ||
| env: | ||
| PHPUNIT_VERSION: '9.6.34' | ||
| PHPUNIT_SHA256: 'e7264ae61fe58a487c2bd741905b85940d8fbc2b32cf4a279949b6d9a172a06a' | ||
| run: | | ||
| php -r "copy('https://phar.phpunit.de/phpunit-${PHPUNIT_VERSION}.phar', 'phpunit.phar');" | ||
| echo "${PHPUNIT_SHA256} phpunit.phar" | sha256sum -c - | ||
|
|
||
| - name: Run tests | ||
| run: php phpunit.phar |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| <?php | ||
| declare(strict_types=1); | ||
|
|
||
| namespace Two\Gateway\Test\E2E\Service\Api; | ||
|
|
||
| use PHPUnit\Framework\TestCase; | ||
| use Two\Gateway\Api\Config\RepositoryInterface as ConfigRepository; | ||
| use Two\Gateway\Api\Log\RepositoryInterface as LogRepository; | ||
| use Two\Gateway\Service\Api\Adapter; | ||
| use Two\Gateway\Test\E2E\Http\RealCurl; | ||
|
|
||
| /** | ||
| * End-to-end tests for Service\Api\Adapter against the real Two API. | ||
| * Defaults to staging unless TWO_API_BASE_URL overrides it. | ||
| * | ||
| * Run via: TWO_API_KEY=xxx make test-e2e | ||
| */ | ||
| class ApiAdapterTest extends TestCase | ||
| { | ||
| private Adapter $adapter; | ||
|
|
||
| protected function setUp(): void | ||
| { | ||
| $apiKey = (string)getenv('TWO_API_KEY'); | ||
| $baseUrl = getenv('TWO_API_BASE_URL') ?: 'https://api.staging.two.inc'; | ||
|
|
||
| $config = $this->createMock(ConfigRepository::class); | ||
| $config->method('getCheckoutApiUrl')->willReturn($baseUrl); | ||
| $config->method('addVersionDataInURL')->willReturnArgument(0); | ||
| $config->method('getApiKey')->willReturn($apiKey); | ||
|
|
||
| $log = $this->createMock(LogRepository::class); | ||
|
|
||
| $this->adapter = new Adapter($config, new RealCurl(), $log); | ||
| } | ||
|
|
||
| public function testApiKeyIsValid(): void | ||
| { | ||
| $result = $this->adapter->execute('/v1/merchant/verify_api_key', [], 'GET'); | ||
|
|
||
| $this->assertIsArray($result); | ||
| $this->assertArrayNotHasKey('error_code', $result); | ||
| } | ||
|
|
||
| public function testInvalidApiKeyReturns401WithStructuredError(): void | ||
| { | ||
| $baseUrl = getenv('TWO_API_BASE_URL') ?: 'https://api.staging.two.inc'; | ||
|
|
||
| $config = $this->createMock(ConfigRepository::class); | ||
| $config->method('getCheckoutApiUrl')->willReturn($baseUrl); | ||
| $config->method('addVersionDataInURL')->willReturnArgument(0); | ||
| $config->method('getApiKey')->willReturn('invalid-key'); | ||
|
|
||
| $log = $this->createMock(LogRepository::class); | ||
|
|
||
| $adapter = new Adapter($config, new RealCurl(), $log); | ||
| $result = $adapter->execute('/v1/merchant/verify_api_key', [], 'GET'); | ||
|
|
||
| $this->assertEquals(401, $result['http_status']); | ||
| } | ||
|
|
||
| public function testBadOrderPayloadReturnsStructuredError(): void | ||
| { | ||
| $result = $this->adapter->execute('/v1/order', []); | ||
|
|
||
| $this->assertArrayHasKey('http_status', $result); | ||
| $this->assertGreaterThanOrEqual(400, $result['http_status']); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| <?php | ||
| declare(strict_types=1); | ||
|
|
||
| namespace Two\Gateway\Test\E2E\Http; | ||
|
|
||
| use Magento\Framework\HTTP\Client\Curl; | ||
|
|
||
| /** | ||
| * Real HTTP implementation of the Curl interface for E2E tests. | ||
| * Mirrors the interface of the Magento Curl stub so it can be injected | ||
| * into Service\Api\Adapter without modification. | ||
| */ | ||
| class RealCurl extends Curl | ||
| { | ||
| private array $headers = []; | ||
| private array $options = []; | ||
| private string $body = ''; | ||
| private int $status = 0; | ||
| private array $responseHeaders = []; | ||
|
|
||
| public function addHeader(string $name, $value): void | ||
| { | ||
| $this->headers[] = "$name: $value"; | ||
| } | ||
|
|
||
| public function setOption(int $option, $value): void | ||
| { | ||
| $this->options[$option] = $value; | ||
| } | ||
|
|
||
| public function post(string $url, $params): void | ||
| { | ||
| $this->doRequest($url, [ | ||
| CURLOPT_POST => true, | ||
| CURLOPT_POSTFIELDS => $params, | ||
| ]); | ||
| } | ||
|
|
||
| public function get(string $url): void | ||
| { | ||
| $this->doRequest($url, [ | ||
| CURLOPT_HTTPGET => true, | ||
| ]); | ||
| } | ||
|
|
||
| public function getBody(): string | ||
| { | ||
| return $this->body; | ||
| } | ||
|
|
||
| public function getStatus(): int | ||
| { | ||
| return $this->status; | ||
| } | ||
|
|
||
| public function getHeaders(): array | ||
| { | ||
| return $this->responseHeaders; | ||
| } | ||
|
|
||
| private function doRequest(string $url, array $extraOptions): void | ||
| { | ||
| $ch = curl_init($url); | ||
|
|
||
| $responseHeaders = []; | ||
| $curlOptions = $this->options + $extraOptions + [ | ||
| CURLOPT_HTTPHEADER => $this->headers, | ||
| CURLOPT_HEADERFUNCTION => function ($ch, $header) use (&$responseHeaders) { | ||
| $len = strlen($header); | ||
| $parts = explode(':', $header, 2); | ||
| if (count($parts) === 2) { | ||
| $responseHeaders[strtolower(trim($parts[0]))] = trim($parts[1]); | ||
| } | ||
| return $len; | ||
| }, | ||
| ]; | ||
|
|
||
| curl_setopt_array($ch, $curlOptions); | ||
|
|
||
| $this->body = (string)(curl_exec($ch) ?: ''); | ||
| $this->status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); | ||
| $this->responseHeaders = $responseHeaders; | ||
|
|
||
| curl_close($ch); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| <?php | ||
| declare(strict_types=1); | ||
|
|
||
| namespace Magento\Bundle\Model\Product; | ||
|
|
||
| /** | ||
| * Minimal Price stub — provides the pricing-type constants | ||
| * used by Service\Order::shouldSkip(). | ||
| */ | ||
| class Price | ||
| { | ||
| public const PRICE_TYPE_FIXED = 0; | ||
| public const PRICE_TYPE_DYNAMIC = 1; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| <?php | ||
| declare(strict_types=1); | ||
|
|
||
| namespace Magento\Framework\HTTP\Client; | ||
|
|
||
| /** | ||
| * Minimal Curl stub for unit tests. | ||
| * | ||
| * The catch-all autoloader creates an empty class, but PHPUnit can't | ||
| * mock methods that don't exist. This stub declares the methods used | ||
| * by Service\Api\Adapter. | ||
| */ | ||
| class Curl | ||
| { | ||
| public function addHeader(string $name, $value): void | ||
| { | ||
| } | ||
|
|
||
| public function setOption(int $option, $value): void | ||
| { | ||
| } | ||
|
|
||
| public function post(string $url, $params): void | ||
| { | ||
| } | ||
|
|
||
| public function get(string $url): void | ||
| { | ||
| } | ||
|
|
||
| public function getBody(): string | ||
| { | ||
| return ''; | ||
| } | ||
|
|
||
| public function getStatus(): int | ||
| { | ||
| return 0; | ||
| } | ||
|
|
||
| public function getHeaders(): array | ||
| { | ||
| return []; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| <?php | ||
| declare(strict_types=1); | ||
|
|
||
| namespace Magento\Framework\Exception; | ||
|
|
||
| use Magento\Framework\Phrase; | ||
|
|
||
| /** | ||
| * Minimal LocalizedException stub for unit tests. | ||
| * | ||
| * Must extend \Exception so it's throwable. Constructor accepts a Phrase | ||
| * and passes the rendered string to the parent Exception. | ||
| */ | ||
| class LocalizedException extends \Exception | ||
| { | ||
| /** @var Phrase */ | ||
| private $phrase; | ||
|
|
||
| public function __construct(Phrase $phrase, ?\Exception $cause = null, int $code = 0) | ||
| { | ||
| $this->phrase = $phrase; | ||
| parent::__construct($phrase->render(), $code, $cause); | ||
| } | ||
|
|
||
| public function getLogMessage(): string | ||
| { | ||
| return $this->phrase->render(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| <?php | ||
| declare(strict_types=1); | ||
|
|
||
| namespace Magento\Framework; | ||
|
|
||
| /** | ||
| * Minimal Phrase stub for unit tests. | ||
| * | ||
| * Supports %1, %2, … placeholder replacement matching Magento's | ||
| * real Phrase behaviour. | ||
| */ | ||
| class Phrase | ||
| { | ||
| /** @var string */ | ||
| private $text; | ||
|
|
||
| /** @var array */ | ||
| private $arguments; | ||
|
|
||
| public function __construct(string $text, array $arguments = []) | ||
| { | ||
| $this->text = $text; | ||
| $this->arguments = $arguments; | ||
| } | ||
|
|
||
| public function render(): string | ||
| { | ||
| $result = $this->text; | ||
| foreach ($this->arguments as $index => $value) { | ||
| $result = str_replace('%' . ($index + 1), (string)$value, $result); | ||
| } | ||
| return $result; | ||
| } | ||
|
|
||
| public function __toString(): string | ||
| { | ||
| return $this->render(); | ||
| } | ||
|
|
||
| public function getText(): string | ||
| { | ||
| return $this->text; | ||
| } | ||
|
|
||
| public function getArguments(): array | ||
| { | ||
| return $this->arguments; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.