Passed
Push — master ( 38d4f3...30ab2e )
by Adrien
14:46
created

SignedQueryMiddlewareTest::dataProviderQuery()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 108
Code Lines 76

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 76
c 1
b 0
f 0
dl 0
loc 108
rs 8.5236
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 EcodevTests\Felix\Middleware;
6
7
use Cake\Chronos\Chronos;
8
use Ecodev\Felix\Middleware\SignedQueryMiddleware;
9
use Laminas\Diactoros\CallbackStream;
10
use Laminas\Diactoros\Response;
11
use Laminas\Diactoros\ServerRequest;
12
use PHPUnit\Framework\TestCase;
13
use Psr\Http\Server\RequestHandlerInterface;
14
15
class SignedQueryMiddlewareTest extends TestCase
16
{
17
    protected function setUp(): void
18
    {
19
        Chronos::setTestNow((new Chronos('2020-01-02T12:30', 'Europe/Zurich')));
20
    }
21
22
    protected function tearDown(): void
23
    {
24
        Chronos::setTestNow();
25
    }
26
27
    /**
28
     * @dataProvider dataProviderQuery
29
     */
30
    public function testRequiredSignedQuery(array $keys, string $body, null|array $parsedBody, string $signature, string $expectExceptionMessage = ''): void
31
    {
32
        $this->process($keys, true, $body, $parsedBody, $signature, $expectExceptionMessage);
33
    }
34
35
    /**
36
     * @dataProvider dataProviderQuery
37
     */
38
    public function testNonRequiredSignedQuery(array $keys, string $body, null|array $parsedBody, string $signature): void
39
    {
40
        $this->process($keys, false, $body, $parsedBody, $signature, '');
41
    }
42
43
    public function testThrowIfNoKeys(): void
44
    {
45
        $this->expectExceptionMessage('Signed queries are required, but no keys are configured');
46
        new SignedQueryMiddleware([]);
47
    }
48
49
    private function process(array $keys, bool $required, string $body, null|array $parsedBody, string $signature, string $expectExceptionMessage): void
50
    {
51
        $request = new ServerRequest();
52
        $request = $request->withBody(new CallbackStream(fn () => $body))->withParsedBody($parsedBody);
53
54
        if ($signature) {
55
            $request = $request->withHeader('Authorization', $signature);
56
        }
57
58
        $handler = $this->createMock(RequestHandlerInterface::class);
59
        $handler->expects($expectExceptionMessage ? self::never() : self::once())
60
            ->method('handle')
61
            ->willReturn(new Response());
62
63
        $middleware = new SignedQueryMiddleware($keys, $required);
64
65
        if ($expectExceptionMessage) {
66
            $this->expectExceptionMessage($expectExceptionMessage);
67
        }
68
69
        $middleware->process($request, $handler);
70
    }
71
72
    public function dataProviderQuery(): iterable
73
    {
74
        $key1 = 'my-secret-1';
75
        $key2 = 'my-secret-2';
76
77
        yield 'simple' => [
78
            [$key1],
79
            '{"operationName":"CurrentUser","variables":{},"query":"query CurrentUser { viewer { id }}',
80
            null,
81
            'v1.1577964600.a4d664cd3d9903e4fecf6f9f671ad953586a7faeb16e67c306fd9f29999dfdd7',
82
        ];
83
84
        yield 'simple but wrong key' => [
85
            [$key2],
86
            '{"operationName":"CurrentUser","variables":{},"query":"query CurrentUser { viewer { id }}',
87
            null,
88
            'v1.1577964600.a4d664cd3d9903e4fecf6f9f671ad953586a7faeb16e67c306fd9f29999dfdd7',
89
            'Invalid signed query',
90
        ];
91
92
        yield 'simple with all keys' => [
93
            [$key2, $key1],
94
            '{"operationName":"CurrentUser","variables":{},"query":"query CurrentUser { viewer { id }}',
95
            null,
96
            'v1.1577964600.a4d664cd3d9903e4fecf6f9f671ad953586a7faeb16e67c306fd9f29999dfdd7',
97
        ];
98
99
        yield 'simple but slightly in the past' => [
100
            [$key1],
101
            '{"operationName":"CurrentUser","variables":{},"query":"query CurrentUser { viewer { id }}',
102
            null,
103
            'v1.1577951100.7d3b639703584e3ea4c68b30a37b56bcf94d19ccdc11c7f05a737c4e7e663a6c',
104
        ];
105
106
        yield 'simple but too much in the past' => [
107
            [$key1],
108
            '{"operationName":"CurrentUser","variables":{},"query":"query CurrentUser { viewer { id }}',
109
            null,
110
            'v1.1577951099.' . str_repeat('a', 64),
111
            'Signed query is expired',
112
        ];
113
114
        yield 'simple but slightly in the future' => [
115
            [$key1],
116
            '{"operationName":"CurrentUser","variables":{},"query":"query CurrentUser { viewer { id }}',
117
            null,
118
            'v1.1577978100.b6fb50cd1aa3974ec9df0c320bf32ff58a28f6fc2040aa13e529a7ef57212e49',
119
        ];
120
121
        yield 'simple but too much in the future' => [
122
            [$key1],
123
            '{"operationName":"CurrentUser","variables":{},"query":"query CurrentUser { viewer { id }}',
124
            null,
125
            'v1.1577978101.' . str_repeat('a', 64),
126
            'Signed query is expired',
127
        ];
128
129
        yield 'batching' => [
130
            [$key1],
131
            '[{"operationName":"CurrentUser","variables":{},"query":"query CurrentUser { viewer { id }},{"operationName":"Configuration","variables":{"key":"announcement-active"},"query":"query Configuration($key: String!) { configuration(key: $key)}"}]',
132
            null,
133
            'v1.1577964600.566fafed794d956d662662b0df3d88e5c0a1e52e19111c08cc122f64a54bd8ec',
134
135
        ];
136
137
        yield 'file upload' => [
138
            [$key1],
139
            '',
140
            [
141
                'operations' => '{"operationName":"CreateImage","variables":{"input":{"file":null}},"query":"mutation CreateImage($input: ImageInput!) { createImage(input: $input) { id }}"}',
142
                'map' => '{"1":["variables.input.file"]}',
143
            ],
144
            'v1.1577964600.69dd1f396016e284afb221966ae5e61323a23222f2ad2a5086e4ba2354f99e58',
145
        ];
146
147
        yield 'file upload will ignore map and uploaded file to sign' => [
148
            [$key1],
149
            '',
150
            [
151
                'operations' => '{"operationName":"CreateImage","variables":{"input":{"file":null}},"query":"mutation CreateImage($input: ImageInput!) { createImage(input: $input) { id }}"}',
152
                'map' => 'different map',
153
                1 => 'fake file',
154
            ],
155
            'v1.1577964600.69dd1f396016e284afb221966ae5e61323a23222f2ad2a5086e4ba2354f99e58',
156
        ];
157
158
        yield 'no header' => [
159
            [$key1],
160
            '{"operationName":"CurrentUser","variables":{},"query":"query CurrentUser { viewer { id }}',
161
            null,
162
            '',
163
            'Missing `Authorization` HTTP header in signed query',
164
        ];
165
166
        yield 'invalid header' => [
167
            [$key1],
168
            '{"operationName":"CurrentUser","variables":{},"query":"query CurrentUser { viewer { id }}',
169
            null,
170
            'foo',
171
            'Invalid `Authorization` HTTP header in signed query',
172
        ];
173
174
        yield 'no graphql operations' => [
175
            [$key1],
176
            '',
177
            null,
178
            'v1.1577964600.' . str_repeat('a', 64),
179
            'Could not find GraphQL operations in request',
180
        ];
181
    }
182
}
183