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

ExpressionTest   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 281
Duplicated Lines 6.05 %

Coupling/Cohesion

Components 0
Dependencies 3

Importance

Changes 0
Metric Value
wmc 11
lcom 0
cbo 3
dl 17
loc 281
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A testItCanBeInstantiatedWithAValidNotation() 0 6 1
A providesValidNotation() 0 23 1
A testSetState() 8 8 1
A testExpressionConstructFailsWithInvalidString() 0 6 1
A providesInvalidExpression() 0 35 1
A testExpandsUriTemplates() 0 4 1
B templateExpansionProvider() 9 116 3
A testExpandThrowsExceptionIfTheModifierCanNotBeApplied() 0 6 1
A invalidModifierToApply() 0 25 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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