Passed
Push — master ( 5fc488...70f83a )
by Adrien
08:01
created

DatatransHandlerTest   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 315
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 10
eloc 188
dl 0
loc 315
rs 10
c 1
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A hasAdminNotification() 0 3 1
A testProcess() 0 36 5
A hasMessage() 0 6 1
A testNotifications() 0 45 1
A hasUserNotification() 0 3 1
B providerProcess() 0 205 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ApplicationTest\Handler;
6
7
use Application\DBAL\Types\MessageTypeType;
8
use Application\Handler\DatatransHandler;
9
use Application\Model\Message;
10
use Application\Model\Order;
11
use Application\Service\MessageQueuer;
12
use ApplicationTest\Traits\TestWithTransactionAndUser;
13
use Doctrine\ORM\EntityManager;
14
use Ecodev\Felix\Service\Mailer;
15
use Ecodev\Felix\Service\MessageRenderer;
16
use Laminas\Diactoros\ServerRequest;
17
use Mezzio\Template\TemplateRendererInterface;
18
use PHPUnit\Framework\TestCase;
19
20
class DatatransHandlerTest extends TestCase
21
{
22
    use TestWithTransactionAndUser;
23
24
    public function testNotifications(): void
25
    {
26
        // Valid request
27
        $request = new ServerRequest();
28
        $request = $request->withParsedBody([
29
            'uppTransactionId' => '123456789012345678',
30
            'status' => 'success',
31
            'refno' => '16001',
32
            'amount' => '1000',
33
            'currency' => 'CHF',
34
            'responseMessage' => 'Payment was successful',
35
        ]);
36
37
        // Mailer will not actually send the message, but it will still flush it in DB as usual
38
        $mailer = $this->createMock(Mailer::class);
39
        $mailer->expects(self::exactly(2))
40
            ->method('sendMessageAsync')
41
            ->willReturnCallback(function (): void {
42
                _em()->flush();
43
            });
44
45
        global $container;
46
        $renderer = $container->get(TemplateRendererInterface::class);
47
48
        $entityManager = $container->get(EntityManager::class);
49
        $messageRenderer = $container->get(MessageRenderer::class);
50
        $config = $container->get('config');
0 ignored issues
show
Unused Code introduced by
The assignment to $config is dead and can be removed.
Loading history...
51
52
        $messageQueuer = new MessageQueuer(
53
            $entityManager,
54
            $messageRenderer,
55
            ['email' => ['admins' => ['[email protected]']]],
56
        );
57
58
        $handler = new DatatransHandler(_em(), $renderer, [], $mailer, $messageQueuer);
59
60
        // Before we have no notifications at all
61
        self::assertFalse($this->hasUserNotification());
62
        self::assertFalse($this->hasAdminNotification());
63
64
        $handler->handle($request);
65
66
        // After we have exactly 1 notification for user and 1 for admin
67
        self::assertTrue($this->hasUserNotification());
68
        self::assertTrue($this->hasAdminNotification());
69
    }
70
71
    private function hasUserNotification(): bool
72
    {
73
        return $this->hasMessage('[email protected]', MessageTypeType::USER_VALIDATED_ORDER);
74
    }
75
76
    private function hasAdminNotification(): bool
77
    {
78
        return $this->hasMessage('[email protected]', MessageTypeType::ADMIN_VALIDATED_ORDER);
79
    }
80
81
    private function hasMessage(string $email, string $type): bool
82
    {
83
        $sql = "SELECT COUNT(*) = 1 FROM message WHERE email = '$email' AND type = '$type'";
84
        $result = (bool) _em()->getConnection()->fetchOne($sql);
85
86
        return $result;
87
    }
88
89
    /**
90
     * @dataProvider providerProcess
91
     */
92
    public function testProcess(?array $data, array $expectedViewModel): void
93
    {
94
        // Message always include input data
95
        $expectedViewModel['message']['detail'] = $data ?? [];
96
        $renderer = $this->createMock(TemplateRendererInterface::class);
97
        $renderer->expects(self::once())->method('render')->with('app::datatrans', $expectedViewModel)->willReturn('');
98
99
        $request = new ServerRequest();
100
        $request = $request->withParsedBody($data);
101
102
        $config = [
103
            'key' => '1a03b7bcf2752c8c8a1b46616b0c12658d2c7643403e655450bedb7c78bb2d2f659c2ff4e647e4ea72d37ef6745ebda6733c7b859439107069f291cda98f4844',
104
        ];
105
106
        $mailer = $this->createMock(Mailer::class);
107
108
        $messageQueuer = $this->createMock(MessageQueuer::class);
109
        $messageQueuer->expects(self::any())
110
            ->method('getAllEmailsToNotify')
111
            ->willReturn(['[email protected]']);
112
        if ($expectedViewModel['message']['status'] === 'success') {
113
            $messageQueuer->expects(self::once())->method('queueUserValidatedOrder')->willReturn(new Message());
114
            $messageQueuer->expects(self::once())->method('queueAdminValidatedOrder')->willReturn(new Message());
115
        }
116
117
        $handler = new DatatransHandler(_em(), $renderer, $config, $mailer, $messageQueuer);
118
        $handler->handle($request);
119
120
        $orderId = $data['refno'] ?? null;
121
        if ($orderId) {
122
            $expectedStatus = $expectedViewModel['message']['status'] === 'success' || $orderId === '16002' ? Order::STATUS_VALIDATED : Order::STATUS_PENDING;
123
            $actualStatus = _em()->getConnection()->fetchColumn('SELECT status FROM `order` WHERE id = ' . $orderId);
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Connection::fetchColumn() has been deprecated: Use fetchOne() instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

123
            $actualStatus = /** @scrutinizer ignore-deprecated */ _em()->getConnection()->fetchColumn('SELECT status FROM `order` WHERE id = ' . $orderId);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
124
            self::assertSame($expectedStatus, $actualStatus);
125
        }
126
127
        self::assertTrue(true); // Workaround when we only assert via mock
128
    }
129
130
    public function providerProcess(): array
131
    {
132
        return [
133
            'normal' => [
134
                [
135
                    'uppTransactionId' => '123456789012345678',
136
                    'status' => 'success',
137
                    'refno' => '16001',
138
                    'amount' => '1000',
139
                    'currency' => 'CHF',
140
                    'responseMessage' => 'Payment was successful',
141
                    'sign' => 'c0a817ad85a2bc830f8fd9d4f1cd0f3a2a8757239f98ec1700a182e7f182c6a7',
142
                ],
143
                [
144
                    'message' => [
145
                        'status' => 'success',
146
                        'message' => 'Payment was successful',
147
                    ],
148
                ],
149
            ],
150
            'invalid HMAC signature' => [
151
                [
152
                    'uppTransactionId' => '123456789012345678',
153
                    'status' => 'success',
154
                    'refno' => '16001',
155
                    'amount' => '1000',
156
                    'currency' => 'CHF',
157
                    'responseMessage' => 'Payment was successful',
158
                    'sign' => 'a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0',
159
                ],
160
                [
161
                    'message' => [
162
                        'status' => 'error',
163
                        'message' => 'Invalid HMAC signature',
164
                    ],
165
                ],
166
            ],
167
            'missing HMAC signature' => [
168
                [
169
                    'uppTransactionId' => '123456789012345678',
170
                    'status' => 'success',
171
                    'refno' => '16001',
172
                    'amount' => '1000',
173
                    'currency' => 'CHF',
174
                    'responseMessage' => 'Payment was successful',
175
                ],
176
                [
177
                    'message' => [
178
                        'status' => 'error',
179
                        'message' => 'Missing HMAC signature',
180
                    ],
181
                ],
182
            ],
183
            'error' => [
184
                [
185
                    'uppTransactionId' => '876543210987654321',
186
                    'status' => 'error',
187
                    'refno' => '16001',
188
                    'errorMessage' => 'Dear Sir/Madam, Fire! fire! help me! All the best, Maurice Moss.',
189
                    'sign' => '8015e6fef2caf41bb11cd2f54077bedb6339bd1aecf418091f77662ee13f81eb',
190
                ],
191
                [
192
                    'message' => [
193
                        'status' => 'error',
194
                        'message' => 'Dear Sir/Madam, Fire! fire! help me! All the best, Maurice Moss.',
195
                    ],
196
                ],
197
            ],
198
            'cancel' => [
199
                [
200
                    'uppTransactionId' => '876543210987654321',
201
                    'status' => 'cancel',
202
                    'refno' => '16001',
203
                    'sign' => '8015e6fef2caf41bb11cd2f54077bedb6339bd1aecf418091f77662ee13f81eb',
204
                ],
205
                [
206
                    'message' => [
207
                        'status' => 'cancel',
208
                        'message' => 'Cancelled',
209
                    ],
210
                ],
211
            ],
212
            'invalid body' => [
213
                null,
214
                [
215
                    'message' => [
216
                        'status' => 'error',
217
                        'message' => 'Parsed body is expected to be an array but got: NULL',
218
                    ],
219
                ],
220
            ],
221
            'invalid status' => [
222
                [
223
                    'uppTransactionId' => '123456789012345678',
224
                    'status' => 'non-existing-status',
225
                    'refno' => '16001',
226
                    'amount' => '10000',
227
                    'currency' => 'CHF',
228
                    'responseMessage' => 'Payment was successful',
229
                    'sign' => '811cb0cd3333311a242adb9907052b74d1c462fb94b071ef4d8790faa7f7fd72',
230
                ],
231
                [
232
                    'message' => [
233
                        'status' => 'error',
234
                        'message' => 'Unsupported status in Datatrans data: non-existing-status',
235
                    ],
236
                ],
237
            ],
238
            'non-existing order' => [
239
                [
240
                    'uppTransactionId' => '123456789012345678',
241
                    'status' => 'success',
242
                    'amount' => '10000',
243
                    'currency' => 'CHF',
244
                    'responseMessage' => 'Payment was successful',
245
                    'sign' => 'd7f9a1716b1538e2cd8f239230526435bc0dcf289a1769f7426badcbaa0efbc8',
246
                ],
247
                [
248
                    'message' => [
249
                        'status' => 'error',
250
                        'message' => 'Cannot validate an order without a valid order ID',
251
                    ],
252
                ],
253
            ],
254
            'non-existing amount' => [
255
                [
256
                    'uppTransactionId' => '123456789012345678',
257
                    'status' => 'success',
258
                    'refno' => '16001',
259
                    'currency' => 'CHF',
260
                    'responseMessage' => 'Payment was successful',
261
                    'sign' => 'd3f6f15797038879787e6d79e8041a7e9a9cb5cd1d7c5dabdd62ac8d3052db34',
262
                ],
263
                [
264
                    'message' => [
265
                        'status' => 'error',
266
                        'message' => 'Cannot validate an order without an amount',
267
                    ],
268
                ],
269
            ],
270
            'invalid currency' => [
271
                [
272
                    'uppTransactionId' => '123456789012345678',
273
                    'status' => 'success',
274
                    'refno' => '16001',
275
                    'amount' => '10000',
276
                    'currency' => 'USD',
277
                    'responseMessage' => 'Payment was successful',
278
                    'sign' => '8583b8d14fb4efa85d905ad8646074ce3ce1abe16464299e116e4cb68dd44752',
279
                ],
280
                [
281
                    'message' => [
282
                        'status' => 'error',
283
                        'message' => 'Can only accept payment in CHF or EUR, but got: USD',
284
                    ],
285
                ],
286
            ],
287
            'incorrect amount' => [
288
                [
289
                    'uppTransactionId' => '123456789012345678',
290
                    'status' => 'success',
291
                    'refno' => '16001',
292
                    'amount' => '1111',
293
                    'currency' => 'CHF',
294
                    'responseMessage' => 'Payment was successful',
295
                    'sign' => '2f2bb61733d8b95101c8c938290acf0efb17daf359266262d73b67165c3ce30b',
296
                ],
297
                [
298
                    'message' => [
299
                        'status' => 'error',
300
                        'message' => 'Cannot validate an order with incorrect balance. Expected 10.00 CHF, or 0.00 EUR, but got: 11.11 CHF',
301
                    ],
302
                ],
303
            ],
304
            'incorrect payment method' => [
305
                [
306
                    'uppTransactionId' => '123456789012345678',
307
                    'status' => 'success',
308
                    'refno' => '16003',
309
                    'amount' => '10000',
310
                    'currency' => 'CHF',
311
                    'responseMessage' => 'Payment was successful',
312
                    'sign' => '3111e1c3e63a77a13d9ae067690d52523c00c992b7aba262ab41de7f6e81bdad',
313
                ],
314
                [
315
                    'message' => [
316
                        'status' => 'error',
317
                        'message' => 'Cannot validate an order whose payment method is: ebanking',
318
                    ],
319
                ],
320
            ],
321
            'incorrect status' => [
322
                [
323
                    'uppTransactionId' => '123456789012345678',
324
                    'status' => 'success',
325
                    'refno' => '16002',
326
                    'amount' => '10000',
327
                    'currency' => 'CHF',
328
                    'responseMessage' => 'Payment was successful',
329
                    'sign' => 'f19af94a1cafd91e8da390308e2d4a1f8bfc4ac1e2e18cf2963e1de63cec37f8',
330
                ],
331
                [
332
                    'message' => [
333
                        'status' => 'error',
334
                        'message' => 'Cannot validate an order which is already validated',
335
                    ],
336
                ],
337
            ],
338
        ];
339
    }
340
}
341