Completed
Pull Request — master (#178)
by ignace nyamagana
03:19
created

ExpressionTest::templateExpansionProvider()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 116

Duplication

Lines 9
Ratio 7.76 %

Importance

Changes 0
Metric Value
dl 9
loc 116
rs 8
c 0
b 0
f 0
cc 3
nc 3
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
/**
4
 * League.Uri (https://uri.thephpleague.com)
5
 *
6
 * (c) Ignace Nyamagana Butera <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace LeagueTest\Uri\UriTemplate;
15
16
use League\Uri\Exceptions\SyntaxError;
17
use League\Uri\Exceptions\TemplateCanNotBeExpanded;
18
use League\Uri\UriTemplate\Expression;
19
use League\Uri\UriTemplate\VariableBag;
20
use PHPUnit\Framework\TestCase;
21
use function var_export;
22
23
/**
24
 * @coversDefaultClass \League\Uri\UriTemplate\Expression
25
 */
26
final class ExpressionTest extends TestCase
27
{
28
    /**
29
     * @covers ::createFromString
30
     * @covers ::__construct
31
     * @covers ::toString
32
     * @covers ::variableNames
33
     * @covers ::setExpressionString
34
     * @covers ::setVariableNames
35
     *
36
     * @dataProvider providesValidNotation
37
     */
38
    public function testItCanBeInstantiatedWithAValidNotation(string $notation, array $variableNames): void
39
    {
40
        $expression = Expression::createFromString($notation);
41
        self::assertSame($notation, $expression->toString());
42
        self::assertSame($variableNames, $expression->variableNames());
43
    }
44
45
    public function providesValidNotation(): iterable
46
    {
47
        return [
48
            'level 1' => ['notation' => '{var}', 'variableNames' => ['var']],
49
            'level 2' => ['notation' => '{+var}', 'variableNames' => ['var']],
50
            'level 3.1' => ['notation' => '{#var}', 'variableNames' => ['var']],
51
            'level 3.2' => ['notation' => '{x,y}', 'variableNames' => ['x', 'y']],
52
            'level 3.3' => ['notation' => '{+x,hello,y}', 'variableNames' => ['x', 'hello', 'y']],
53
            'level 3.4' => ['notation' => '{#path,x}', 'variableNames' => ['path', 'x']],
54
            'level 3.5' => ['notation' => '{.x,y}', 'variableNames' => ['x', 'y']],
55
            'level 3.6' => ['notation' => '{/var,x}', 'variableNames' => ['var', 'x']],
56
            'level 3.7' => ['notation' => '{;x,y,empty}', 'variableNames' => ['x', 'y', 'empty']],
57
            'level 3.8' => ['notation' => '{?x,y,undef}', 'variableNames' => ['x', 'y', 'undef']],
58
            'level 3.9' => ['notation' => '{&x,y,empty}', 'variableNames' => ['x', 'y', 'empty']],
59
            'level 4.1' => ['notation' => '{+path:6}', 'variableNames' => ['path']],
60
            'level 4.2' => ['notation' => '{var:3}', 'variableNames' => ['var']],
61
            'level 4.3' => ['notation' => '{#keys*}', 'variableNames' => ['keys']],
62
            'level 4.4' => ['notation' => '{/var:1,var}', 'variableNames' => ['var']],
63
            'level 4.5' => ['notation' => '{;keys*}', 'variableNames' => ['keys']],
64
            'level 4.6' => ['notation' => '{?var:3}', 'variableNames' => ['var']],
65
            'level 4.7' => ['notation' => '{.null}', 'variableNames' => ['null']],
66
        ];
67
    }
68
69
    /**
70
     * @covers ::__set_state
71
     * @covers \League\Uri\UriTemplate\Template
72
     */
73 View Code Duplication
    public function testSetState(): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
74
    {
75
        $expressionString = '{;keys*}';
76
77
        $expression = Expression::createFromString($expressionString);
78
79
        self::assertEquals($expression, eval('return '.var_export($expression, true).';'));
80
    }
81
82
    /**
83
     * @covers ::createFromString
84
     *
85
     * @dataProvider providesInvalidExpression
86
     */
87
    public function testExpressionConstructFailsWithInvalidString(string $expression): void
88
    {
89
        self::expectException(SyntaxError::class);
90
91
        Expression::createFromString($expression);
92
    }
93
94
    public function providesInvalidExpression(): iterable
95
    {
96
        return [
97
            'missing content' => ['{}'],
98
            'missing delimiter' => ['foobar'],
99
            'reserved operator' => ['{|var*}'],
100
            'missing ending braces' => ['{/id*'],
101
            'missing starting braces' => ['/id*}'],
102
            'multiple starting operators' => ['{/?id}'],
103
            'invalid prefix' => ['{var:prefix}'],
104
            'multiple operator modifiers (1)' => ['{hello:2*}'] ,
105
            'duplicate operator' => ['{??hello}'] ,
106
            'reserved operator !' => ['{!hello}'] ,
107
            'space inside variable name' => ['{with space}'],
108
            'leading space in variable name (1)' => ['{ leading_space}'],
109
            'trailing space in variable name' => ['{trailing_space }'],
110
            'reserved operator =' => ['{=path}'] ,
111
            'forbidden operator $' => ['{$var}'],
112
            'reserved operator |' => ['{|var*}'],
113
            'using an operator modifier as an operator' => ['{*keys?}'],
114
            'variable name contains a reserved character (1)' => ['{?empty=default,var}'],
115
            'variable name contains invalid character (1)' => ['{-prefix|/-/|var}'],
116
            'variable name contains invalid prefix' => ['{example:color?}'],
117
            'variable name contains a reserved character (2)' => ['{?empty|foo=none}'],
118
            'variable name contains a reserved character (3)' => ['{#hello+}'],
119
            'variable name contains a reserved character (4)' => ['{hello+}'],
120
            'multiple operator modifiers (2)' => ['{;keys:1*}'],
121
            'variable name contains invalid character (2)' => ['{-join|&|var,list}'],
122
            'variable name contains invalid character (3)' => ['{~thing}'],
123
            'variable name contains invalid character (4)' => ['{default-graph-uri}'],
124
            'variable name contains invalid character (5)' => ['{?query,default-graph-uri}'],
125
            'variable name contains invalid character (6)' => ['{?query){&default-graph-uri*}'],
126
            'leading space in variable name (2)' => ['{?x, y}'],
127
        ];
128
    }
129
130
    /**
131
     * @covers ::expand
132
     * @covers ::replace
133
     * @covers ::inject
134
     * @covers ::replaceString
135
     * @covers ::replaceList
136
     * @covers ::decodeReserved
137
     * @covers ::isAssoc
138
     *
139
     * @dataProvider templateExpansionProvider
140
     */
141
    public function testExpandsUriTemplates(string $template, string $expectedUriString, array $variables): void
142
    {
143
        self::assertSame($expectedUriString, Expression::createFromString($template)->expand(new VariableBag($variables)));
144
    }
145
146
    public function templateExpansionProvider(): iterable
147
    {
148
        $variables = [
149
            'var'   => 'value',
150
            'hello' => 'Hello World!',
151
            'empty' => '',
152
            'path'  => '/foo/bar',
153
            'x'     => '1024',
154
            'y'     => '768',
155
            'null'  => null,
156
            'list'  => ['red', 'green', 'blue'],
157
            'keys'  => [
158
                'semi'  => ';',
159
                'dot'   => '.',
160
                'comma' => ',',
161
            ],
162
            'empty_keys' => [],
163
            'bool' => true,
164
        ];
165
166
        $templateAndExpansionData = [
167
            'level 1' => [
168
                ['{var}',          'value'],
169
                ['{hello}',        'Hello%20World%21'],
170
                ['{bool}',         '1'],
171
            ],
172
            'level 2' => [
173
                ['{+var}',         'value'],
174
                ['{+hello}',       'Hello%20World!'],
175
                ['{+path}',        '/foo/bar'],
176
            ],
177
            'level 3' => [
178
                ['{#var}',          '#value'],
179
                ['{#hello}',        '#Hello%20World!'],
180
                ['{x,y}',           '1024,768'],
181
                ['{x,hello,y}',     '1024,Hello%20World%21,768'],
182
                ['{+x,hello,y}',    '1024,Hello%20World!,768'],
183
                ['{+path,x}',       '/foo/bar,1024'],
184
                ['{#x,hello,y}',    '#1024,Hello%20World!,768'],
185
                ['{#path,x}',       '#/foo/bar,1024'],
186
                ['{.var}',          '.value'],
187
                ['{.x,y}',          '.1024.768'],
188
                ['{/var}',          '/value'],
189
                ['{/var,x}',        '/value/1024'],
190
                ['{;x,y}',          ';x=1024;y=768'],
191
                ['{;x,y,empty}',    ';x=1024;y=768;empty'],
192
                ['{?x,y}',          '?x=1024&y=768'],
193
                ['{?x,y,empty}',    '?x=1024&y=768&empty='],
194
                ['{?x,y,undef}',    '?x=1024&y=768'],
195
                ['{&x}',            '&x=1024'],
196
                ['{&x,y,empty}',    '&x=1024&y=768&empty='],
197
            ],
198
            'level 4' => [
199
                ['{var:3}',         'val'],
200
                ['{var:30}',        'value'],
201
                ['{list}',          'red,green,blue'],
202
                ['{list*}',         'red,green,blue'],
203
                ['{keys}',          'semi,%3B,dot,.,comma,%2C'],
204
                ['{keys*}',         'semi=%3B,dot=.,comma=%2C'],
205
                ['{+path:6}',       '/foo/b'],
206
                ['{+list}',         'red,green,blue'],
207
                ['{+list*}',        'red,green,blue'],
208
                ['{+keys}',         'semi,;,dot,.,comma,,'],
209
                ['{+keys*}',        'semi=;,dot=.,comma=,'],
210
                ['{#path:6}',       '#/foo/b'],
211
                ['{#list}',         '#red,green,blue'],
212
                ['{#list*}',        '#red,green,blue'],
213
                ['{#keys}',         '#semi,;,dot,.,comma,,'],
214
                ['{#keys*}',        '#semi=;,dot=.,comma=,'],
215
                ['{.var:3}',       '.val'],
216
                ['{.list}',        '.red,green,blue'],
217
                ['{.list*}',       '.red.green.blue'],
218
                ['{.keys}',        '.semi,%3B,dot,.,comma,%2C'],
219
                ['{.keys*}',       '.semi=%3B.dot=..comma=%2C'],
220
                ['{/var:1,var}',    '/v/value'],
221
                ['{/list}',         '/red,green,blue'],
222
                ['{/list*}',        '/red/green/blue'],
223
                ['{/list*,path:4}', '/red/green/blue/%2Ffoo'],
224
                ['{/keys}',             '/semi,%3B,dot,.,comma,%2C'],
225
                ['{/keys*}',            '/semi=%3B/dot=./comma=%2C'],
226
                ['{;hello:5}',          ';hello=Hello'],
227
                ['{;list}',             ';list=red,green,blue'],
228
                ['{;list*}',            ';list=red;list=green;list=blue'],
229
                ['{;keys}',             ';keys=semi,%3B,dot,.,comma,%2C'],
230
                ['{;keys*}',            ';semi=%3B;dot=.;comma=%2C'],
231
                ['{?var:3}',            '?var=val'],
232
                ['{?list}',             '?list=red,green,blue'],
233
                ['{?list*}',            '?list=red&list=green&list=blue'],
234
                ['{?keys}',             '?keys=semi,%3B,dot,.,comma,%2C'],
235
                ['{?keys*}',            '?semi=%3B&dot=.&comma=%2C'],
236
                ['{&var:3}',            '&var=val'],
237
                ['{&list}',             '&list=red,green,blue'],
238
                ['{&list*}',            '&list=red&list=green&list=blue'],
239
                ['{&keys}',             '&keys=semi,%3B,dot,.,comma,%2C'],
240
                ['{&keys*}',            '&semi=%3B&dot=.&comma=%2C'],
241
                ['{.null}',            ''],
242
                ['{.null,var}',        '.value'],
243
                ['{.empty_keys*}',     ''],
244
                ['{.empty_keys}',      ''],
245
            ],
246
            'extra' => [
247
                // Test that missing expansions are skipped
248
                ['{&missing*}', ''],
249
            ],
250
        ];
251
252 View Code Duplication
        foreach ($templateAndExpansionData as $specification => $tests) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
253
            foreach ($tests as $offset => $test) {
254
                yield $specification.' test '.$offset => [
255
                    'template' => $test[0],
256
                    'expectedUriString' => $test[1],
257
                    'variables' => $variables,
258
                ];
259
            }
260
        }
261
    }
262
263
    /**
264
     * @covers ::replaceList
265
     * @covers \League\Uri\Exceptions\TemplateCanNotBeExpanded
266
     *
267
     * @dataProvider invalidModifierToApply
268
     */
269
    public function testExpandThrowsExceptionIfTheModifierCanNotBeApplied(string $expression, array $variables): void
270
    {
271
        self::expectException(TemplateCanNotBeExpanded::class);
272
273
        Expression::createFromString($expression)->expand(new VariableBag($variables));
274
    }
275
276
    /**
277
     * Following negative tests with wrong variable can only be detected at runtime.
278
     *
279
     * @see https://github.com/uri-templates/uritemplate-test/blob/master/negative-tests.json
280
     */
281
    public function invalidModifierToApply(): iterable
282
    {
283
        return [
284
            'can not apply a modifier on a hash value (1)' => [
285
                'expression' => '{keys:1}',
286
                'variables' => [
287
                    'keys' => [
288
                        'semi' => ';',
289
                        'dot' => '.',
290
                        'comma' => ',',
291
                    ],
292
                ],
293
            ],
294
            'can not apply a modifier on a hash value (2)' => [
295
                'expression' => '{+keys:1}',
296
                'variables' => [
297
                    'keys' => [
298
                        'semi' => ';',
299
                        'dot' => '.',
300
                        'comma' => ',',
301
                    ],
302
                ],
303
            ],
304
        ];
305
    }
306
}
307