Skip to content

Commit 3997b57

Browse files
PRE-3278: adding Unit testing for the plugin
1 parent 0e33439 commit 3997b57

15 files changed

Lines changed: 2832 additions & 152 deletions

.github/workflows/ci.yml

Lines changed: 2 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -10,160 +10,13 @@ name: CI
1010

1111
jobs:
1212
quality:
13-
name: PHP Quality
14-
runs-on: ubuntu-latest
15-
env:
16-
APP_ENV: test
17-
steps:
18-
-
19-
uses: actions/checkout@v4
20-
with:
21-
fetch-depth: 0
22-
-
23-
name: 'Setup PHP'
24-
uses: shivammathur/setup-php@v2
25-
with:
26-
php-version: '8.2'
27-
ini-values: date.timezone=UTC
28-
extensions: intl
29-
tools: symfony
30-
coverage: none
31-
-
32-
name: 'Composer - Get Cache Directory'
33-
id: composer-cache
34-
run: 'echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT'
35-
-
36-
name: 'Composer - Set cache'
37-
uses: actions/cache@v4
38-
with:
39-
path: '${{ steps.composer-cache.outputs.dir }}'
40-
key: 'php-8.2-symfony-6.4-composer-${{ hashFiles(''**/composer.json'') }}'
41-
restore-keys: 'php-8.2-symfony-6.4-composer-'
42-
-
43-
name: 'Composer - Github Auth'
44-
run: 'composer config -g github-oauth.github.com ${{ github.token }}'
45-
-
46-
name: 'Composer - Remove version field for validation'
47-
run: |
48-
jq 'del(.version)' composer.json > composer.json.tmp
49-
mv composer.json.tmp composer.json
50-
-
51-
name: 'Composer - Validate'
52-
run: 'composer validate --strict'
53-
-
54-
name: 'Composer - Restrict Symfony version'
55-
run: 'composer config extra.symfony.require "^6.4"'
56-
-
57-
name: 'Composer - Install dependencies'
58-
run: 'composer install --no-progress'
59-
id: end-of-setup
60-
-
61-
name: 'ECS - Coding Standard'
62-
run: 'composer ecs'
63-
if: 'always() && steps.end-of-setup.outcome == ''success'''
64-
-
65-
name: 'PHPStan - Static Analysis'
66-
run: 'composer phpstan'
67-
if: 'always() && steps.end-of-setup.outcome == ''success'''
13+
uses: payplug/template-ci/.github/workflows/sylius_quality.yml@main
6814

6915
sylius-matrix:
70-
name: 'PHPUnit-Behat (PHP ${{ matrix.php }} Sylius ${{ matrix.sylius }} Symfony ${{ matrix.symfony }})'
71-
runs-on: ubuntu-latest
7216
needs: [quality]
7317
if: github.base_ref == 'test-develop-ci'
74-
strategy:
75-
fail-fast: false
76-
matrix:
77-
php:
78-
- 8.2
79-
- 8.4
80-
sylius:
81-
- 2.1.0
82-
- 2.0.0
83-
symfony:
84-
- 6.4
85-
- 7.3
86-
node:
87-
- 20.x
88-
env:
89-
APP_ENV: test
90-
package-name: payplug/sylius-payplug-plugin
91-
steps:
92-
-
93-
name: 'Setup PHP'
94-
uses: shivammathur/setup-php@v2
95-
with:
96-
php-version: '${{ matrix.php }}'
97-
ini-values: date.timezone=UTC
98-
extensions: intl
99-
tools: symfony
100-
coverage: none
101-
-
102-
name: 'Setup Node'
103-
uses: actions/setup-node@v4
104-
with:
105-
node-version: '${{ matrix.node }}'
106-
-
107-
uses: actions/checkout@v4
108-
-
109-
name: 'Composer - Get Cache Directory'
110-
id: composer-cache
111-
run: 'echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT'
112-
-
113-
name: 'Composer - Set cache'
114-
uses: actions/cache@v4
115-
id: cache-composer
116-
with:
117-
path: '${{ steps.composer-cache.outputs.dir }}'
118-
key: 'php-${{ matrix.php }}-sylius-${{ matrix.sylius }}-symfony-${{ matrix.symfony }}-composer-${{ hashFiles(''**/composer.json'') }}'
119-
restore-keys: 'php-${{ matrix.php }}-sylius-${{ matrix.sylius }}-symfony-${{ matrix.symfony }}-composer-'
120-
-
121-
name: 'Composer - Create cache directory'
122-
run: 'mkdir -p /home/runner/.composer/cache'
123-
if: 'steps.cache-composer.outputs.cache-hit != ''true'''
124-
-
125-
name: 'Composer - Github Auth'
126-
run: 'composer config -g github-oauth.github.com ${{ github.token }}'
127-
-
128-
name: 'Yarn - Get cache directory'
129-
id: yarn-cache
130-
run: 'echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT'
131-
-
132-
name: 'Yarn - Set Cache'
133-
uses: actions/cache@v4
134-
with:
135-
path: '${{ steps.yarn-cache.outputs.dir }}'
136-
key: 'node-${{ matrix.node }}-yarn-${{ hashFiles(''**/package.json **/yarn.lock'') }}'
137-
restore-keys: "node-${{ matrix.node }}-yarn-\n"
138-
-
139-
name: 'Install Sylius-Standard and Plugin'
140-
run: 'make install -e SYLIUS_VERSION=${{ matrix.sylius }} SYMFONY_VERSION=${{ matrix.symfony }}'
141-
id: end-of-setup-sylius
142-
-
143-
name: 'Doctrine Schema Validate'
144-
run: 'vendor/bin/console doctrine:schema:validate --skip-sync'
145-
-
146-
name: 'Run PHPUnit'
147-
run: 'make phpunit'
148-
if: 'always() && steps.end-of-setup-sylius.outcome == ''success'''
149-
-
150-
uses: actions/upload-artifact@v4
151-
if: failure()
152-
with:
153-
name: 'logs-php${{ matrix.php }}-sylius${{ matrix.sylius }}-sf${{ matrix.symfony }}'
154-
path: ./tests/TestApplication/etc/build
155-
services:
156-
mariadb:
157-
image: 'mariadb:10.4.11'
158-
ports:
159-
- '3306:3306'
160-
env:
161-
MYSQL_ALLOW_EMPTY_PASSWORD: true
162-
options: '--health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3'
18+
uses: payplug/template-ci/.github/workflows/sylius_phpunit.yml@main
16319

164-
# -----------------------------------------------------------------------
165-
# 4. SONARCLOUD — code quality analysis (develop PRs only)
166-
# -----------------------------------------------------------------------
16720
sonarcloud:
16821
if: always() && !failure() && !cancelled() && github.base_ref == 'test-develop-ci'
16922
needs: sylius-matrix

.github/workflows/release.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Release — RC
2+
3+
'on':
4+
push:
5+
tags:
6+
- '*-rc*'
7+
8+
jobs:
9+
quality:
10+
uses: payplug/template-ci/.github/workflows/sylius_quality.yml@main
11+
12+
phpunit:
13+
needs: [quality]
14+
uses: payplug/template-ci/.github/workflows/sylius_phpunit.yml@main
15+
16+
github-release:
17+
name: GitHub Pre-Release
18+
needs: phpunit
19+
runs-on: ubuntu-latest
20+
permissions:
21+
contents: write
22+
steps:
23+
-
24+
uses: actions/checkout@v4
25+
with:
26+
fetch-depth: 0
27+
-
28+
name: Create GitHub pre-release
29+
run: |
30+
gh release create "$GITHUB_REF_NAME" \
31+
--title "$GITHUB_REF_NAME" \
32+
--generate-notes \
33+
--prerelease
34+
env:
35+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ CLAUDE.md
2424
.DS_Store
2525
.claude
2626
.review
27+
.phpunit.result.cache

src/PaymentProcessing/AbortPaymentProcessor.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace PayPlug\SyliusPayPlugPlugin\PaymentProcessing;
66

77
use Payplug\Exception\HttpException;
8-
use PayPlug\SyliusPayPlugPlugin\ApiClient\PayPlugApiClientFactory;
8+
use PayPlug\SyliusPayPlugPlugin\ApiClient\PayPlugApiClientFactoryInterface;
99
use Sylius\Component\Core\Model\PaymentInterface;
1010
use Sylius\Component\Payment\PaymentTransitions;
1111
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
@@ -16,7 +16,7 @@
1616
class AbortPaymentProcessor
1717
{
1818
public function __construct(
19-
private PayPlugApiClientFactory $payplugApiClientFactory,
19+
private PayPlugApiClientFactoryInterface $payplugApiClientFactory,
2020
) {
2121
}
2222

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\PayPlug\SyliusPayPlugPlugin\PHPUnit\Action;
6+
7+
use ArrayObject;
8+
use PayPlug\SyliusPayPlugPlugin\Action\ConvertPaymentAction;
9+
use PayPlug\SyliusPayPlugPlugin\ApiClient\PayPlugApiClientInterface;
10+
use PayPlug\SyliusPayPlugPlugin\Creator\PayPlugPaymentDataCreator;
11+
use Payum\Core\Request\Convert;
12+
use PHPUnit\Framework\MockObject\MockObject;
13+
use PHPUnit\Framework\TestCase;
14+
use Sylius\Component\Core\Model\PaymentInterface;
15+
16+
final class ConvertPaymentActionTest extends TestCase
17+
{
18+
private PayPlugPaymentDataCreator&MockObject $paymentDataCreator;
19+
20+
private PayPlugApiClientInterface&MockObject $apiClient;
21+
22+
private ConvertPaymentAction $action;
23+
24+
protected function setUp(): void
25+
{
26+
$this->paymentDataCreator = $this->createMock(PayPlugPaymentDataCreator::class);
27+
$this->apiClient = $this->createMock(PayPlugApiClientInterface::class);
28+
29+
$this->action = new ConvertPaymentAction($this->paymentDataCreator);
30+
$this->action->setApi($this->apiClient);
31+
}
32+
33+
// -------------------------------------------------------------------------
34+
// supports()
35+
// -------------------------------------------------------------------------
36+
37+
/**
38+
* Passes a Convert request with a PaymentInterface source and 'array' as the target format.
39+
* Verifies supports() returns true.
40+
*/
41+
public function testSupports_withConvertRequestAndPaymentSource_returnsTrue(): void
42+
{
43+
$request = $this->createMock(Convert::class);
44+
$request->method('getSource')->willReturn($this->createMock(PaymentInterface::class));
45+
$request->method('getTo')->willReturn('array');
46+
47+
self::assertTrue($this->action->supports($request));
48+
}
49+
50+
/**
51+
* Source is a plain stdClass instead of a PaymentInterface.
52+
* Verifies supports() returns false.
53+
*/
54+
public function testSupports_withConvertRequestButNonPaymentSource_returnsFalse(): void
55+
{
56+
$request = $this->createMock(Convert::class);
57+
$request->method('getSource')->willReturn(new \stdClass());
58+
$request->method('getTo')->willReturn('array');
59+
60+
self::assertFalse($this->action->supports($request));
61+
}
62+
63+
/**
64+
* Source is a valid PaymentInterface but the target format is 'json' instead of 'array'.
65+
* Verifies supports() returns false.
66+
*/
67+
public function testSupports_withConvertRequestButWrongTo_returnsFalse(): void
68+
{
69+
$request = $this->createMock(Convert::class);
70+
$request->method('getSource')->willReturn($this->createMock(PaymentInterface::class));
71+
$request->method('getTo')->willReturn('json');
72+
73+
self::assertFalse($this->action->supports($request));
74+
}
75+
76+
// -------------------------------------------------------------------------
77+
// execute() — delegates to creator and sets result
78+
// -------------------------------------------------------------------------
79+
80+
/**
81+
* Calls execute() and verifies the action delegates to PayPlugPaymentDataCreator::create() exactly once.
82+
* The ArrayObject returned by the creator is converted to an array and passed to setResult().
83+
*/
84+
public function testExecute_delegatesToCreatorAndSetsResult(): void
85+
{
86+
$payment = $this->createMock(PaymentInterface::class);
87+
88+
$createdDetails = new ArrayObject([
89+
'amount' => 1500,
90+
'currency' => 'EUR',
91+
]);
92+
93+
$this->paymentDataCreator
94+
->expects(self::once())
95+
->method('create')
96+
->with($payment)
97+
->willReturn($createdDetails)
98+
;
99+
100+
$request = $this->createMock(Convert::class);
101+
$request->method('getSource')->willReturn($payment);
102+
$request->method('getTo')->willReturn('array');
103+
$request->expects(self::once())
104+
->method('setResult')
105+
->with(['amount' => 1500, 'currency' => 'EUR'])
106+
;
107+
108+
$this->action->execute($request);
109+
}
110+
111+
// -------------------------------------------------------------------------
112+
// execute() — result contains all fields from creator
113+
// -------------------------------------------------------------------------
114+
115+
/**
116+
* The creator returns an ArrayObject with multiple nested fields (amount, payment_method, metadata).
117+
* Verifies all fields are present and correctly typed in the array passed to setResult().
118+
*/
119+
public function testExecute_passesAllCreatorFieldsToResult(): void
120+
{
121+
$payment = $this->createMock(PaymentInterface::class);
122+
123+
$createdDetails = new ArrayObject([
124+
'amount' => 2000,
125+
'currency' => 'EUR',
126+
'payment_method' => 'bancontact',
127+
'metadata' => ['customer_id' => 42, 'order_number' => 'ORD-99'],
128+
]);
129+
130+
$this->paymentDataCreator->method('create')->willReturn($createdDetails);
131+
132+
$capturedResult = null;
133+
$request = $this->createMock(Convert::class);
134+
$request->method('getSource')->willReturn($payment);
135+
$request->method('getTo')->willReturn('array');
136+
$request->method('setResult')->willReturnCallback(function (array $result) use (&$capturedResult) {
137+
$capturedResult = $result;
138+
});
139+
140+
$this->action->execute($request);
141+
142+
self::assertSame(2000, $capturedResult['amount']);
143+
self::assertSame('bancontact', $capturedResult['payment_method']);
144+
self::assertSame(42, $capturedResult['metadata']['customer_id']);
145+
}
146+
}

0 commit comments

Comments
 (0)