DatatransHandlerTest::providerProcess()   B
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 237
Code Lines 154

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 154
dl 0
loc 237
rs 8
c 0
b 0
f 0
cc 1
nc 1
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace ApplicationTest\Handler;
6
7
use Application\DBAL\Types\MessageTypeType;
8
use Application\Enum\OrderStatus;
9
use Application\Handler\DatatransHandler;
10
use Application\Model\Message;
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
51
        $messageQueuer = new MessageQueuer(
52
            $entityManager,
53
            $messageRenderer,
54
            ['email' => ['admins' => ['[email protected]']]],
55
        );
56
57
        $handler = new DatatransHandler(_em(), $renderer, [], $mailer, $messageQueuer);
58
59
        // Before we have no notifications at all
60
        self::assertFalse($this->hasUserNotification());
61
        self::assertFalse($this->hasAdminNotification());
62
63
        $handler->handle($request);
64
65
        // After we have exactly 1 notification for user and 1 for admin
66
        self::assertTrue($this->hasUserNotification());
67
        self::assertTrue($this->hasAdminNotification());
68
    }
69
70
    private function hasUserNotification(): bool
71
    {
72
        return $this->hasMessage('[email protected]', MessageTypeType::USER_VALIDATED_ORDER);
73
    }
74
75
    private function hasAdminNotification(): bool
76
    {
77
        return $this->hasMessage('[email protected]', MessageTypeType::ADMIN_VALIDATED_ORDER);
78
    }
79
80
    private function hasMessage(string $email, string $type): bool
81
    {
82
        $sql = "SELECT COUNT(*) = 1 FROM message WHERE email = '$email' AND type = '$type'";
83
        $result = (bool) _em()->getConnection()->fetchOne($sql);
84
85
        return $result;
86
    }
87
88
    /**
89
     * @dataProvider providerProcess
90
     */
91
    public function testProcess(?array $data, array $expectedViewModel, bool $expectedWebTemporaryAccess): void
92
    {
93
        // Message always include input data
94
        $expectedViewModel['message']['detail'] = $data ?? [];
95
        $renderer = $this->createMock(TemplateRendererInterface::class);
96
        $renderer->expects(self::once())->method('render')->with('app::datatrans', $expectedViewModel)->willReturn('');
97
98
        $request = new ServerRequest();
99
        $request = $request->withParsedBody($data);
100
101
        $config = [
102
            'key' => '1a03b7bcf2752c8c8a1b46616b0c12658d2c7643403e655450bedb7c78bb2d2f659c2ff4e647e4ea72d37ef6745ebda6733c7b859439107069f291cda98f4844',
103
        ];
104
105
        $mailer = $this->createMock(Mailer::class);
106
107
        $messageQueuer = $this->createMock(MessageQueuer::class);
108
        $messageQueuer->expects(self::any())
109
            ->method('getAllEmailsToNotify')
110
            ->willReturn(['[email protected]']);
111
        if ($expectedViewModel['message']['status'] === 'success') {
112
            $messageQueuer->expects(self::once())->method('queueUserValidatedOrder')->willReturn(new Message());
113
            $messageQueuer->expects(self::once())->method('queueAdminValidatedOrder')->willReturn(new Message());
114
        }
115
116
        $handler = new DatatransHandler(_em(), $renderer, $config, $mailer, $messageQueuer);
117
        $handler->handle($request);
118
119
        $orderId = $data['refno'] ?? null;
120
        if ($orderId) {
121
            $expectedStatus = $expectedViewModel['message']['status'] === 'success' || $orderId === '16002' ? OrderStatus::Validated : OrderStatus::Pending;
122
            $actualStatus = _em()->getConnection()->fetchOne('SELECT status FROM `order` WHERE id = ' . $orderId);
123
            self::assertSame($expectedStatus->value, $actualStatus);
124
125
            $actualWebTemporaryAccess = _em()->getConnection()->fetchOne('SELECT web_temporary_access FROM `user` INNER JOIN `order` ON `user`.id = `order`.owner_id WHERE `order`.id = ' . $orderId);
126
            self::assertEquals($expectedWebTemporaryAccess ? '1' : '0', $actualWebTemporaryAccess);
127
        }
128
129
        // @phpstan-ignore-next-line
130
        self::assertTrue(true); // Workaround when we only assert via mock
131
    }
132
133
    public function providerProcess(): iterable
134
    {
135
        yield 'normal' => [
136
            [
137
                'uppTransactionId' => '123456789012345678',
138
                'status' => 'success',
139
                'refno' => '16001',
140
                'amount' => '1000',
141
                'currency' => 'CHF',
142
                'responseMessage' => 'Payment was successful',
143
                'sign' => 'c0a817ad85a2bc830f8fd9d4f1cd0f3a2a8757239f98ec1700a182e7f182c6a7',
144
            ],
145
            [
146
                'message' => [
147
                    'status' => 'success',
148
                    'message' => 'Payment was successful',
149
                ],
150
            ],
151
            false,
152
        ];
153
        yield 'invalid HMAC signature' => [
154
            [
155
                'uppTransactionId' => '123456789012345678',
156
                'status' => 'success',
157
                'refno' => '16001',
158
                'amount' => '1000',
159
                'currency' => 'CHF',
160
                'responseMessage' => 'Payment was successful',
161
                'sign' => 'a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0',
162
            ],
163
            [
164
                'message' => [
165
                    'status' => 'error',
166
                    'message' => 'Invalid HMAC signature',
167
                ],
168
            ],
169
            false,
170
        ];
171
        yield 'missing HMAC signature' => [
172
            [
173
                'uppTransactionId' => '123456789012345678',
174
                'status' => 'success',
175
                'refno' => '16001',
176
                'amount' => '1000',
177
                'currency' => 'CHF',
178
                'responseMessage' => 'Payment was successful',
179
            ],
180
            [
181
                'message' => [
182
                    'status' => 'error',
183
                    'message' => 'Missing HMAC signature',
184
                ],
185
            ],
186
            false,
187
        ];
188
        yield 'error' => [
189
            [
190
                'uppTransactionId' => '876543210987654321',
191
                'status' => 'error',
192
                'refno' => '16001',
193
                'errorMessage' => 'Dear Sir/Madam, Fire! fire! help me! All the best, Maurice Moss.',
194
                'sign' => '8015e6fef2caf41bb11cd2f54077bedb6339bd1aecf418091f77662ee13f81eb',
195
            ],
196
            [
197
                'message' => [
198
                    'status' => 'error',
199
                    'message' => 'Dear Sir/Madam, Fire! fire! help me! All the best, Maurice Moss.',
200
                ],
201
            ],
202
            false,
203
        ];
204
        yield 'cancel' => [
205
            [
206
                'uppTransactionId' => '876543210987654321',
207
                'status' => 'cancel',
208
                'refno' => '16001',
209
                'sign' => '8015e6fef2caf41bb11cd2f54077bedb6339bd1aecf418091f77662ee13f81eb',
210
            ],
211
            [
212
                'message' => [
213
                    'status' => 'cancel',
214
                    'message' => 'Cancelled',
215
                ],
216
            ],
217
            false,
218
        ];
219
        yield 'invalid body' => [
220
            null,
221
            [
222
                'message' => [
223
                    'status' => 'error',
224
                    'message' => 'Parsed body is expected to be an array but got: NULL',
225
                ],
226
            ],
227
            false,
228
        ];
229
        yield 'invalid status' => [
230
            [
231
                'uppTransactionId' => '123456789012345678',
232
                'status' => 'non-existing-status',
233
                'refno' => '16001',
234
                'amount' => '10000',
235
                'currency' => 'CHF',
236
                'responseMessage' => 'Payment was successful',
237
                'sign' => '811cb0cd3333311a242adb9907052b74d1c462fb94b071ef4d8790faa7f7fd72',
238
            ],
239
            [
240
                'message' => [
241
                    'status' => 'error',
242
                    'message' => 'Unsupported status in Datatrans data: non-existing-status',
243
                ],
244
            ],
245
            false,
246
        ];
247
        yield 'non-existing order' => [
248
            [
249
                'uppTransactionId' => '123456789012345678',
250
                'status' => 'success',
251
                'amount' => '10000',
252
                'currency' => 'CHF',
253
                'responseMessage' => 'Payment was successful',
254
                'sign' => 'd7f9a1716b1538e2cd8f239230526435bc0dcf289a1769f7426badcbaa0efbc8',
255
            ],
256
            [
257
                'message' => [
258
                    'status' => 'error',
259
                    'message' => 'Cannot validate an order without a valid order ID',
260
                ],
261
            ],
262
            false,
263
        ];
264
        yield 'non-existing amount' => [
265
            [
266
                'uppTransactionId' => '123456789012345678',
267
                'status' => 'success',
268
                'refno' => '16001',
269
                'currency' => 'CHF',
270
                'responseMessage' => 'Payment was successful',
271
                'sign' => 'd3f6f15797038879787e6d79e8041a7e9a9cb5cd1d7c5dabdd62ac8d3052db34',
272
            ],
273
            [
274
                'message' => [
275
                    'status' => 'error',
276
                    'message' => 'Cannot validate an order without an amount',
277
                ],
278
            ],
279
            false,
280
        ];
281
        yield 'invalid currency' => [
282
            [
283
                'uppTransactionId' => '123456789012345678',
284
                'status' => 'success',
285
                'refno' => '16001',
286
                'amount' => '10000',
287
                'currency' => 'USD',
288
                'responseMessage' => 'Payment was successful',
289
                'sign' => '8583b8d14fb4efa85d905ad8646074ce3ce1abe16464299e116e4cb68dd44752',
290
            ],
291
            [
292
                'message' => [
293
                    'status' => 'error',
294
                    'message' => 'Can only accept payment in CHF or EUR, but got: USD',
295
                ],
296
            ],
297
            false,
298
        ];
299
        yield 'incorrect amount' => [
300
            [
301
                'uppTransactionId' => '123456789012345678',
302
                'status' => 'success',
303
                'refno' => '16001',
304
                'amount' => '1111',
305
                'currency' => 'CHF',
306
                'responseMessage' => 'Payment was successful',
307
                'sign' => '2f2bb61733d8b95101c8c938290acf0efb17daf359266262d73b67165c3ce30b',
308
            ],
309
            [
310
                'message' => [
311
                    'status' => 'error',
312
                    'message' => 'Cannot validate an order with incorrect balance. Expected 10.00 CHF, or 0.00 EUR, but got: 11.11 CHF',
313
                ],
314
            ],
315
            false,
316
        ];
317
        yield 'incorrect payment method' => [
318
            [
319
                'uppTransactionId' => '123456789012345678',
320
                'status' => 'success',
321
                'refno' => '16003',
322
                'amount' => '10000',
323
                'currency' => 'CHF',
324
                'responseMessage' => 'Payment was successful',
325
                'sign' => '3111e1c3e63a77a13d9ae067690d52523c00c992b7aba262ab41de7f6e81bdad',
326
            ],
327
            [
328
                'message' => [
329
                    'status' => 'error',
330
                    'message' => 'Cannot validate an order whose payment method is: ebanking',
331
                ],
332
            ],
333
            false,
334
        ];
335
        yield 'incorrect status' => [
336
            [
337
                'uppTransactionId' => '123456789012345678',
338
                'status' => 'success',
339
                'refno' => '16002',
340
                'amount' => '10000',
341
                'currency' => 'CHF',
342
                'responseMessage' => 'Payment was successful',
343
                'sign' => 'f19af94a1cafd91e8da390308e2d4a1f8bfc4ac1e2e18cf2963e1de63cec37f8',
344
            ],
345
            [
346
                'message' => [
347
                    'status' => 'error',
348
                    'message' => 'Cannot validate an order which is already validated',
349
                ],
350
            ],
351
            false,
352
        ];
353
        yield 'validating numeric subscription will give webTemporaryAccess' => [
354
            [
355
                'uppTransactionId' => '123456789012345678',
356
                'status' => 'success',
357
                'refno' => '16004',
358
                'amount' => '8000',
359
                'currency' => 'CHF',
360
                'responseMessage' => 'Payment was successful',
361
                'sign' => 'a54523b461058df8d61cdbe2328bcd161bae411ec9e6206bbf4da773e3c88493',
362
            ],
363
            [
364
                'message' => [
365
                    'status' => 'success',
366
                    'message' => 'Payment was successful',
367
                ],
368
            ],
369
            true,
370
        ];
371
    }
372
}
373