JSONTextQueryTest   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 439
Duplicated Lines 1.82 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 9
lcom 1
cbo 2
dl 8
loc 439
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 8 8 2
A setUp() 0 6 1
B testQueryWithMatchOnInt() 0 70 1
B testQueryWithMatchOnStr() 0 94 1
B testQueryWithMatchOnPath() 0 129 1
B testQueryWithMatchOnExpr() 0 58 1
A testQueryWithInvalidArgs() 0 12 1
A getFixture() 0 5 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
 * @package silverstripe-jsontext
5
 * @subpackage fields
6
 * @author Russell Michell <[email protected]>
7
 * @todo Add tests where source data is a JSON array, not just a JSON object
8
 *
9
 *
10
 */
11
12
use PhpTek\JSONText\ORM\FieldType\JSONText;
13
use PhpTek\JSONText\Exception\JSONTextException;
14
use SilverStripe\Dev\SapphireTest;
15
use SilverStripe\ORM\FieldType\DBBoolean;
16
use SilverStripe\ORM\FieldType\DBFloat;
17
use SilverStripe\ORM\FieldType\DBVarchar;
18
use SilverStripe\ORM\FieldType\DBInt;
19
20
class JSONTextQueryTest extends SapphireTest
21
{
22
    /**
23
     * @var array
24
     */
25
    protected $fixtures = [
26
        'array'     => 'fixtures/json/array.json',
27
        'object'    => 'fixtures/json/object.json',
28
    ];
29
30
    /**
31
     * @var JSONText
32
     */
33
    protected $sut;
34
35
    /**
36
     * JSONTextTest constructor.
37
     *
38
     * Modify fixtures property to be able to run on PHP <5.6 without use of constant in class property which 5.6+ allows
39
     */
40 View Code Duplication
    public function __construct()
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...
41
    {
42
        foreach($this->fixtures as $name => $path) {
43
            $this->fixtures[$name] = realpath(__DIR__) . '/' . $path;
44
        }
45
        
46
        parent::__construct();
47
    }
48
49
    /**
50
     * Setup the System Under Test for this test suite.
51
     */
52
    public function setUp()
53
    {
54
        parent::setUp();
55
56
        $this->sut = JSONText::create('MyJSON');
57
    }
58
59
    /**
60
     * Tests query() by means of the integer Postgres Int match operator: '->'
61
     *
62
     * @todo Use same source data instead of repeating..
63
     */
64
    public function testQueryWithMatchOnInt()
65
    {
66
        $field = $this->sut;
67
68
        // Data Source: Array
69
        // Return Type: ARRAY
70
        // Operator: "->" (Int)
71
        $field->setReturnType('array');
72
        $field->setValue($this->getFixture('array'));
73
        $this->assertEquals([2 => 'trabant'], $field->query('->', 2));
74
75
        // Data Source: Array
76
        // Return Type: JSON
77
        // Operator: "->" (Int)
78
        $field->setReturnType('json');
79
        $field->setValue($this->getFixture('array'));
80
        $this->assertEquals('{"2":"trabant"}', $field->query('->', 2));
81
        $this->assertEquals('{"5":101}', $field->query('->', 5));
82
83
        // Data Source: Array
84
        // Return Type: SILVERSTRIPE
85
        // Operator: "->" (Int)
86
        // SS Type: Float
87
        $field->setReturnType('silverstripe');
88
        $field->setValue($this->getFixture('array'));
89
        $this->assertInternalType('array', $field->query('->', 3));
90
        $this->assertInstanceOf(DBFloat::class, $field->query('->', 3)[3]);
91
        $this->assertEquals(44.6, $field->query('->', 3)[3]->getValue());
92
93
        // Data Source: Array
94
        // Return Type: SILVERSTRIPE
95
        // Operator: "->" (Int)
96
        // SS Type: Boolean
97
        $field->setReturnType('silverstripe');
98
        $field->setValue($this->getFixture('array'));
99
        $this->assertInternalType('array', $field->query('->', 1));
100
        $this->assertInstanceOf(DBBoolean::class, $field->query('->', 1)[1]);
101
        $this->assertEquals(1, $field->query('->', 1)[1]->getValue());
102
103
        // Data Source: Array
104
        // Return Type: SILVERSTRIPE
105
        // Operator: "->" (Int)
106
        // SS Type: Int
107
        $field->setReturnType('silverstripe');
108
        $field->setValue($this->getFixture('array'));
109
        $this->assertInternalType('array', $field->query('->', 5));
110
        $this->assertInstanceOf(DBInt::class, $field->query('->', 5)[5]);
111
        $this->assertEquals(101, $field->query('->', 5)[5]->getValue());
112
113
        // Data Source: Array
114
        // Return Type: SILVERSTRIPE
115
        // Operator: "->" (Int)
116
        // SS Type: Varchar
117
        $field->setReturnType('silverstripe');
118
        $field->setValue($this->getFixture('array'));
119
        $this->assertInternalType('array', $field->query('->', 4));
120
        $this->assertInstanceOf(DBVarchar::class, $field->query('->', 4)[4]);
121
        $this->assertEquals('buick', $field->query('->', 4)[4]->getValue());
122
123
        // Test: Empty #1
124
        $field->setReturnType('array');
125
        $field->setValue('');
126
        $this->assertInternalType('array', $field->query('->', 3));
127
        $this->assertCount(0, $field->query('->', 3));
128
129
        // Test: Invalid #1
130
        $field->setReturnType('array');
131
        $field->setValue('["morris"]');
132
        $this->assertEquals([], $field->query('->', 17));
133
    }
134
135
    /**
136
     * Tests query() by means of the integer Postgres String match operator: '->>'
137
     */
138
    public function testQueryWithMatchOnStr()
139
    {
140
        $field = $this->sut;
141
142
        // Data Source: Object
143
        // Return Type: ARRAY
144
        // Operator: "->>" (String)
145
        $field->setReturnType('array');
146
        $field->setValue($this->getFixture('object'));
147
        $this->assertEquals(['Subaru' => 'Impreza'], $field->query('->>', 'Subaru'));
148
        $this->assertEquals(
149
            ['japanese' => ['fast' => ['Subaru' => 'Impreza'], 'slow' => ['Honda' => 'Civic']]],
150
            $field->query('->>', 'japanese')
151
        );
152
153
        // Data Source: Object
154
        // Return Type: JSON
155
        // Operator: "->>" (String)
156
        $field->setReturnType('json');
157
        $field->setValue($this->getFixture('object'));
158
        $this->assertEquals('{"Subaru":"Impreza"}', $field->query('->>', 'Subaru'));
159
        $this->assertEquals(
160
            '{"japanese":{"fast":{"Subaru":"Impreza"},"slow":{"Honda":"Civic"}}}',
161
            $field->query('->>', 'japanese')
162
        );
163
164
        // Data Source: Object
165
        // Return Type: SilverStripe
166
        // Operator: "->>" (String)
167
        // SS Type: Varchar
168
        $field->setReturnType('silverstripe');
169
        $field->setValue($this->getFixture('object'));
170
        $this->assertInternalType('array', $field->query('->>', 'Subaru'));
171
        $this->assertInstanceOf(DBVarchar::class, $field->query('->>', 'Subaru')['Subaru']);
172
        $this->assertEquals('Impreza', $field->query('->>', 'Subaru')['Subaru']->getValue());
173
174
        // Data Source: Object
175
        // Return Type: SilverStripe
176
        // Operator: "->>" (String)
177
        // SS Type: Boolean
178
        $field->setReturnType('silverstripe');
179
        $field->setValue($this->getFixture('object'));
180
        $this->assertInternalType('array', $field->query('->>', 'beer tastes good'));
181
        $this->assertInstanceOf(DBBoolean::class, $field->query('->>', 'beer tastes good')['beer tastes good']);
182
        $this->assertEquals(1, $field->query('->>', 'beer tastes good')['beer tastes good']->getValue());
183
184
        // Data Source: Object
185
        // Return Type: SilverStripe
186
        // Operator: "->>" (String)
187
        // SS Type: Float
188
        $field->setReturnType('silverstripe');
189
        $field->setValue($this->getFixture('object'));
190
        $this->assertInternalType('array', $field->query('->>', 'how sure are you'));
191
        $this->assertInstanceOf(DBFloat::class, $field->query('->>', 'how sure are you')['how sure are you']);
192
        $this->assertEquals(99.99, $field->query('->>', 'how sure are you')['how sure are you']->getValue());
193
194
        // Data Source: Object
195
        // Return Type: SilverStripe
196
        // Operator: "->>" (String)
197
        // SS Type: Int
198
        $field->setReturnType('silverstripe');
199
        $field->setValue($this->getFixture('object'));
200
        $this->assertInternalType('array', $field->query('->>', 'how high'));
201
        $this->assertInstanceOf(DBInt::class, $field->query('->>', 'how high')['how high']);
202
        $this->assertEquals(6, $field->query('->>', 'how high')['how high']->getValue());
203
204
        // Data Source: Object
205
        // Return Type: SilverStripe
206
        // Operator: "->>" (String)
207
        // SS Type: N/A Nested sub-array example, expect an array
208
        $field->setReturnType('silverstripe');
209
        $field->setValue($this->getFixture('object'));
210
        $this->assertInternalType('array', $field->query('->>', 'planes')['planes']);
211
        $this->assertInternalType('array', $field->query('->>', 'planes')['planes']['russian']);
212
        $this->assertCount(2, $field->query('->>', 'planes')['planes']['russian']);
213
        $this->assertInstanceOf(DBVarchar::class, $field->query('->>', 'planes')['planes']['russian'][0]);
214
        $this->assertInstanceOf(DBVarchar::class, $field->query('->>', 'planes')['planes']['russian'][1]);
215
216
        // Test: Empty #1
217
        $field->setReturnType('array');
218
        $field->setValue('');
219
        $this->assertInternalType('array', $field->query('->>', 'planes'));
220
        $this->assertCount(0, $field->query('->', 3));
221
222
        // Test: Empty #2
223
        $field->setReturnType('array');
224
        $field->setValue('["morris"]');
225
        $this->assertEquals([], $field->query('->', 17));
226
227
        // Test: Invalid #1
228
        $field->setReturnType('array');
229
        $field->setValue('["trabant"]');
230
        $this->assertEquals([], $field->query('->', 1));
231
    }
232
233
    /**
234
     * Tests query() by means of the Postgres path-match operator: '#>'
235
     */
236
    public function testQueryWithMatchOnPath()
237
    {
238
        $field = $this->sut;
239
240
        // Data Source: Object
241
        // Return Type: ARRAY
242
        // Operator: "#>" (Path)
243
        // Expect: Array due to duplicate keys in different parts of the source data
244
        $field->setReturnType('array');
245
        $field->setValue($this->getFixture('object'));
246
        $this->assertEquals(
247
            [['Subaru' => 'Impreza'],['Kawasaki' => 'KR1S250']],
248
            $field->query('#>', '{"japanese":"fast"}')
249
        );
250
251
        // Data Source: Object
252
        // Return Type: JSON
253
        // Operator: "#>" (Path)
254
        // Expect: Array due to duplicate keys in different parts of the source data
255
        $field->setReturnType('json');
256
        $field->setValue($this->getFixture('object'));
257
        $this->assertEquals(
258
            '[{"Subaru":"Impreza"},{"Kawasaki":"KR1S250"}]',
259
            $field->query('#>', '{"japanese":"fast"}')
260
        );
261
262
        // Data Source: Object
263
        // Return Type: SILVERSTRIPE
264
        // Operator: "#>" (Path)
265
        // Expect: Array due to duplicate keys in different parts of the source data
266
        $field->setReturnType('silverstripe');
267
        $field->setValue($this->getFixture('object'));
268
        $this->assertInternalType('array', $field->query('#>', '{"japanese":"fast"}'));
269
        $this->assertCount(2, $field->query('#>', '{"japanese":"fast"}'));
270
271
        $one = $field->query('#>', '{"japanese":"fast"}')[0];
272
        $two = $field->query('#>', '{"japanese":"fast"}')[1];
273
274
        $this->assertInternalType('array', $one);
275
        $this->assertInternalType('array', $two);
276
        $this->assertInstanceOf(DBVarchar::class, array_values($one)[0]);
277
        $this->assertInstanceOf(DBVarchar::class, array_values($two)[0]);
278
        $this->assertEquals('Impreza', array_values($one)[0]->getValue());
279
        $this->assertEquals('KR1S250', array_values($two)[0]->getValue());
280
281
        // Data Source: Object
282
        // Return Type: ARRAY
283
        // Operator: "#>" (Path)
284
        // Expect: Direct scalar comparison assertion
285
        $field->setReturnType('array');
286
        $field->setValue($this->getFixture('object'));
287
        $this->assertEquals(['airbus'], $field->query('#>', '{"planes":"french"}'));
288
289
        // Data Source: Object
290
        // Return Type: JSON
291
        // Operator: "#>" (Path)
292
        // Expect: Direct scalar comparison assertion
293
        $field->setReturnType('json');
294
        $field->setValue($this->getFixture('object'));
295
        $this->assertEquals('["airbus"]', $field->query('#>', '{"planes":"french"}'));
296
297
        // Data Source: Object
298
        // Return Type: SILVERSTRIPE
299
        // Operator: "#>" (Path)
300
        // Expect: Direct scalar comparison assertion (Varchar)
301
        $field->setReturnType('silverstripe');
302
        $field->setValue($this->getFixture('object'));
303
        $this->assertInternalType('array', $field->query('#>', '{"planes":"french"}'));
304
        $this->assertInstanceOf(DBVarchar::class, $field->query('#>', '{"planes":"french"}')[0]);
305
        $this->assertEquals('airbus', $field->query('#>', '{"planes":"french"}')[0]->getValue());
306
307
        // Data Source: Object
308
        // Return Type: SILVERSTRIPE
309
        // Operator: "#>" (Path)
310
        // Expect: Direct scalar comparison assertion (Float)
311
        $field->setReturnType('silverstripe');
312
        $field->setValue($this->getFixture('object'));
313
314
        $res = $field->query('#>', '{"floats":"0"}');
315
316
        $this->assertInternalType('array', $res);
317
        $this->assertInternalType('array', $res[0]); // Why? Because value of "floats" key is a JSON array
318
        $this->assertInstanceOf(DBFloat::class, array_values($res[0])[0]);
319
        $this->assertEquals(99.99, array_values($res[0])[0]->getValue());
320
321
        // Data Source: Object
322
        // Return Type: SILVERSTRIPE
323
        // Operator: "#>" (Path)
324
        // Expect: Direct scalar comparison assertion (Int)
325
        $field->setReturnType('silverstripe');
326
        $field->setValue($this->getFixture('object'));
327
328
        $res = $field->query('#>', '{"ints":"0"}');
329
330
        $this->assertInternalType('array', $res);
331
        $this->assertInternalType('array', $res[0]); // Why? Because value of "floats" key is a JSON array
332
        $this->assertInstanceOf(DBInt::class, array_values($res[0])[1]);
333
        $this->assertEquals(6, array_values($res[0])[1]->getValue());
334
335
        // Data Source: Object
336
        // Return Type: SILVERSTRIPE
337
        // Operator: "#>" (Path)
338
        // Expect: Direct scalar comparison assertion (Boolean)
339
        $field->setReturnType('silverstripe');
340
        $field->setValue($this->getFixture('object'));
341
342
        $res = $field->query('#>', '{"booleans":"0"}');
343
344
        $this->assertInternalType('array', $res);
345
        $this->assertInternalType('array', $res[0]); // Why? Because value of "booleans" key is a JSON array
346
        $this->assertInstanceOf(DBBoolean::class, array_values($res[0])[0]);
347
        $this->assertEquals(1, array_values($res[0])[0]->getValue());
348
349
        // #1 Empty source data
350
        $field->setReturnType('array');
351
        $field->setValue('');
352
        $this->assertEquals([], $field->query('#>', '{"japanese":"fast"}'));
353
354
        // #2 JSON path not found
355
        $field->setReturnType('silverstripe');
356
        $field->setValue($this->getFixture('object'));
357
        $this->assertNull($field->query('#>', '{"ints":"1"}')); // The "ints" key only has a single array as a value
358
359
        // #3 Invalid operand on RHS
360
        $this->setExpectedException(JSONTextException::class);
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0; use expectException() instead

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

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

Loading history...
361
        $field->setReturnType('array');
362
        $field->setValue($this->getFixture('object'));
363
        $this->assertEquals(['Kawasaki' => 'KR1S250'], $field->query('#>', '{"japanese":"fast"'));
364
    }
365
366
    /**
367
     * Tests query() by means of JSONPath expressions.
368
     * N.b. only a minimum no. tests should be required, given that the 3rd party lib via which this functionality
369
     * is derived, is itself well tested.
370
     */
371
    public function testQueryWithMatchOnExpr()
372
    {
373
        $field = $this->sut;
374
375
        // Data Source: Object
376
        // Return Type: ARRAY
377
        // Expression: "$.." (Everything)
378
        // Expect: Array, obviously due to no. nodes in the source JSON
379
        $field->setReturnType('array');
380
        $field->setValue($this->getFixture('object'));
381
        $this->assertInternalType('array', $field->query('$..'));
382
        $this->assertCount(25, $field->query('$..'));
383
384
        // Data Source: Object
385
        // Return Type: ARRAY
386
        // Expression: "$..japanese[*]" (An array of children of all keys matching "japanese")
387
        // Expect: Array
388
        $field->setReturnType('array');
389
        $field->setValue($this->getFixture('object'));
390
        $this->assertCount(4, $field->query('$..japanese[*]'));
391
        $this->assertEquals([
392
            ['Subaru' => 'Impreza'],
393
            ['Honda' => 'Civic'],
394
            ['Kawasaki' => 'KR1S250'],
395
            ['Honda' => 'FS1']
396
        ], $field->query('$..japanese[*]'));
397
398
        // Data Source: Object
399
        // Return Type: JSON
400
        // Expression: "$..japanese[*]" (An array of children of all keys matching "japanese")
401
        // Expect: JSON Array of JSON objects
402
        $field->setReturnType('json');
403
        $field->setValue($this->getFixture('object'));
404
        $this->assertEquals(
405
            '[{"Subaru":"Impreza"},{"Honda":"Civic"},{"Kawasaki":"KR1S250"},{"Honda":"FS1"}]',
406
            $field->query('$..japanese[*]')
407
        );
408
409
        // Data Source: Object
410
        // Return Type: Array
411
        // Expression: "$.cars.american[*]" (All entries in the american cars node)
412
        // Expect: Array
413
        $field->setReturnType('array');
414
        $field->setValue($this->getFixture('object'));
415
        $this->assertEquals(['buick', 'oldsmobile'], $field->query('$.cars.american[*]'));
416
        $this->assertEquals(['buick'], $field->query('$.cars.american[0]'));
417
418
        // Data Source: Object
419
        // Return Type: Array
420
        // Expression: "$.cars.american[*]" (All entries in the american cars node)
421
        // Expect: Array 0f SilverStripe types
422
        $field->setReturnType('silverstripe');
423
        $field->setValue($this->getFixture('object'));
424
        $this->assertInternalType('array', $field->query('$.cars.american[*]'));
425
        $this->assertCount(2, $field->query('$.cars.american[*]'));
426
        $this->assertInstanceOf(DBVarchar::class, $field->query('$.cars.american[*]')[0]);
427
        $this->assertEquals('buick', $field->query('$.cars.american[*]')[0]->getValue());
428
    }
429
430
    /**
431
     * Tests query() by means of passing bad argument combinations.
432
     */
433
    public function testQueryWithInvalidArgs()
434
    {
435
        $field = $this->sut;
436
437
        $this->setExpectedException(JSONTextException::class);
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0; use expectException() instead

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

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

Loading history...
438
        $field->setValue('["trabant"]');
439
        $field->query('$.cars.american[*]', 'foo'); // Cannot pass multiple args when in JSONPath context
440
441
        $this->setExpectedException(null);
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0; use expectException() instead

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

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

Loading history...
442
        $field->setValue('');
443
        $field->query('$.cars.american[*]', 'foo'); // Still shouldn't, but routine only kicks in _After_ checks made in setValue()
444
    }
445
446
    /**
447
     * Get the contents of a fixture
448
     *
449
     * @param string $fixture
450
     * @return string
451
     */
452
    private function getFixture($fixture)
453
    {
454
        $files = $this->fixtures;
455
        return file_get_contents($files[$fixture]);
456
    }
457
458
}
459