Passed
Push — master ( 8a3be9...89d545 )
by Tomas
02:05
created

AbstractArgumentBuilderTest::testInvalidGetCall()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Feedo\ArgumentBuilder\Tests\Unit;
4
5
use Feedo\ArgumentBuilder\Exception\InvalidArgumentException;
6
use Feedo\ArgumentBuilder\Exception\InvalidDefinitionException;
7
use Feedo\ArgumentBuilder\Exception\UndefinedMethodException;
8
use Feedo\ArgumentBuilder\AbstractArgumentBuilder;
9
use Feedo\ArgumentBuilder\Tests\Fixtures\CustomMockArgumentBuilder;
10
use Feedo\ArgumentBuilder\Tests\Fixtures\MockArgumentBuilder;
11
use Feedo\ArgumentBuilder\Tests\Fixtures\SubMockArgumentBuilder;
12
use PHPUnit\Framework\TestCase;
13
14
class AbstractArgumentBuilderTest extends TestCase
15
{
16
    private $sampleData;
17
    private $sampleDataEncoded;
18
19
    protected function setUp()
20
    {
21
        $this->sampleData = array(
22
            'arg1' => 'xxx',
23
            'arg2' => 'yyy',
24
            'sub1' => array(
25
                'subarg1' => 'zzz',
26
                'subarg2' => 'aaa',
27
                'boolarg' => 'false',
28
            ),
29
            'sub2' => array(
30
                'subarg1' => 'nnn',
31
                'subarg2' => 'mmm',
32
                'boolarg' => 'true',
33
            ),
34
            'enum' => 'val1',
35
        );
36
37
        $this->sampleDataEncoded = http_build_query($this->sampleData);
38
    }
39
40
    private function getBuilderMock()
41
    {
42
        $builder = new MockArgumentBuilder();
43
        $builder->setArg1('xxx');
44
        $builder->setArg2('yyy');
45
        $builder->setSub1('subarg1', 'zzz');
46
        $builder->setSub1('subarg2', 'aaa');
47
        $builder->setSub1('boolarg', false);
48
        $builder->setSub2('subarg1', 'nnn');
49
        $builder->setSub2('subarg2', 'mmm');
50
        $builder->setSub2('boolarg', true);
51
        $builder->setEnum('val1');
52
53
        return $builder;
54
    }
55
56
    public function testBuild()
57
    {
58
        $builder = $this->getBuilderMock();
59
60
        $this->assertEquals($this->sampleData, $builder->build());
61
    }
62
63
    public function testToString()
64
    {
65
        $builder = $this->getBuilderMock();
66
67
        $this->assertEquals($this->sampleDataEncoded, (string) $builder);
68
    }
69
70
    public function testGetFields()
71
    {
72
        $builder = $this->getBuilderMock();
73
74
        $this->assertEquals('xxx', $builder->getArg1());
75
        $this->assertInstanceOf(SubMockArgumentBuilder::class, $builder->getSub1());
76
        $this->assertEquals('zzz', $builder->getSub1('subarg1'));
0 ignored issues
show
Unused Code introduced by
The call to Feedo\ArgumentBuilder\Te...umentBuilder::getSub1() has too many arguments starting with 'subarg1'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

76
        $this->assertEquals('zzz', $builder->/** @scrutinizer ignore-call */ getSub1('subarg1'));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
77
        $builder->unsetSub1();
78
        $this->assertEquals(null, $builder->getSub1('subarg1'));
79
    }
80
81
    public function testSetNullFields()
82
    {
83
        $data = $this->sampleData;
84
        $data['arg1'] = null;
85
        $data['sub1'] = null;
86
        $data['sub2']['subarg1'] = null;
87
88
        $builder = $this->getBuilderMock();
89
        $builder->setArg1(null);
90
        $builder->setSub1(null);
91
        $builder->setSub2('subarg1', null);
92
93
        $this->assertEquals($data, $builder->build());
94
    }
95
96
    public function testSetSubAsObject()
97
    {
98
        $builder = $this->getBuilderMock();
99
        $builder->setSub1(new SubMockArgumentBuilder());
100
    }
101
102
    public function testUnsetFields()
103
    {
104
        $data = $this->sampleData;
105
        unset($data['arg1'], $data['sub1'], $data['sub2']['subarg1']);
106
107
        $builder = $this->getBuilderMock();
108
        $builder->unsetArg1();
109
        $builder->unsetSub1();
110
        $builder->unsetSub2('subarg1');
111
112
        $this->assertEquals($data, $builder->build());
113
    }
114
115
    public function testSetFields()
116
    {
117
        $data = $this->sampleData;
118
        $data['arg1'] = 'aaa';
119
        $data['sub1']['subarg1'] = 'new';
120
        $data['sub1']['boolarg'] = 'true';
121
        $data['sub2'] = array('subarg1' => 'good');
122
123
        $builder = $this->getBuilderMock();
124
        $builder->setArg1('aaa');
125
        $builder->setSub1('subarg1', 'new');
126
        $builder->setSub2((new SubMockArgumentBuilder())->setSubarg1('good'));
0 ignored issues
show
Bug introduced by
The method setSubarg1() does not exist on Feedo\ArgumentBuilder\Te...\SubMockArgumentBuilder. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

126
        $builder->setSub2((new SubMockArgumentBuilder())->/** @scrutinizer ignore-call */ setSubarg1('good'));
Loading history...
127
        $builder->setSub1('boolarg', true);
128
129
        $this->assertEquals($data, $builder->build());
130
    }
131
132
    public function testInvalidDefinitionNotArray()
133
    {
134
        $this->expectException(InvalidDefinitionException::class);
135
        $this->expectExceptionMessage('Field description must be either string (shortcut for class name), or int (shortcut for field type) or array (full form)');
136
137
        $builder = new CustomMockArgumentBuilder(array(
138
            'arg1' => new \stdClass(),
139
        ));
140
        $builder->setArg1('aaa');
0 ignored issues
show
Bug introduced by
The method setArg1() does not exist on Feedo\ArgumentBuilder\Te...stomMockArgumentBuilder. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

140
        $builder->/** @scrutinizer ignore-call */ 
141
                  setArg1('aaa');
Loading history...
141
    }
142
143
    public function testInvalidDefinitionMissingType()
144
    {
145
        $this->expectException(InvalidDefinitionException::class);
146
        $this->expectExceptionMessage('Field type is not defined');
147
148
        $builder = new CustomMockArgumentBuilder(array(
149
            'arg1' => array(
150
            ),
151
        ));
152
        $builder->setArg1('aaa');
153
    }
154
155
    public function testInvalidDefinitionMissingClass()
156
    {
157
        $this->expectException(InvalidDefinitionException::class);
158
        $this->expectExceptionMessage('Field of type ARGUMENT_TYPE_ARGUMENT_BUILDER must have class defined');
159
160
        $builder = new CustomMockArgumentBuilder(array(
161
            'arg1' => array(
162
                'type' => AbstractArgumentBuilder::ARGUMENT_TYPE_ARGUMENT_BUILDER,
163
            ),
164
        ));
165
        $builder->setArg1('aaa');
166
    }
167
168
    public function testCallUndefinedMethod()
169
    {
170
        $this->expectException(UndefinedMethodException::class);
171
        $this->expectExceptionMessageRegExp('/^Call to undefined method/');
172
173
        $builder = $this->getBuilderMock();
174
        $builder->dummyCall('foo');
0 ignored issues
show
Bug introduced by
The method dummyCall() does not exist on Feedo\ArgumentBuilder\Te...res\MockArgumentBuilder. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

174
        $builder->/** @scrutinizer ignore-call */ 
175
                  dummyCall('foo');
Loading history...
175
    }
176
177
    public function testSetNonexistingParameter()
178
    {
179
        $this->expectException(UndefinedMethodException::class);
180
        $this->expectExceptionMessageRegExp('/^Call to undefined method/');
181
182
        $builder = $this->getBuilderMock();
183
        $builder->setNonexistant('blah');
0 ignored issues
show
Bug introduced by
The method setNonexistant() does not exist on Feedo\ArgumentBuilder\Te...res\MockArgumentBuilder. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

183
        $builder->/** @scrutinizer ignore-call */ 
184
                  setNonexistant('blah');
Loading history...
184
    }
185
186
    public function testUnsetNonexistingParameter()
187
    {
188
        $this->expectException(UndefinedMethodException::class);
189
        $this->expectExceptionMessageRegExp('/^Call to undefined method/');
190
191
        $builder = $this->getBuilderMock();
192
        $builder->unsetNonexistant();
0 ignored issues
show
Bug introduced by
The method unsetNonexistant() does not exist on Feedo\ArgumentBuilder\Te...res\MockArgumentBuilder. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

192
        $builder->/** @scrutinizer ignore-call */ 
193
                  unsetNonexistant();
Loading history...
193
    }
194
195
    /**
196
     * @dataProvider provideNonstringData
197
     */
198
    public function testInvalidSugarUsage($value)
199
    {
200
        $this->expectException(InvalidArgumentException::class);
201
        $this->expectExceptionMessageRegExp('/^Method .+ expects the first parameter to be string if you want to get sub-value$/');
202
203
        $builder = $this->getBuilderMock();
204
        $builder->getSub1($value);
0 ignored issues
show
Unused Code introduced by
The call to Feedo\ArgumentBuilder\Te...umentBuilder::getSub1() has too many arguments starting with $value. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

204
        $builder->/** @scrutinizer ignore-call */ 
205
                  getSub1($value);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
205
    }
206
207
    public function testInvalidGetCall()
208
    {
209
        $this->expectException(InvalidArgumentException::class);
210
        $this->expectExceptionMessageRegExp('/Method .+ must take exactly 0 arguments/');
211
212
        $builder = $this->getBuilderMock();
213
        $builder->getArg1('yyy');
0 ignored issues
show
Unused Code introduced by
The call to Feedo\ArgumentBuilder\Te...umentBuilder::getArg1() has too many arguments starting with 'yyy'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

213
        $builder->/** @scrutinizer ignore-call */ 
214
                  getArg1('yyy');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
214
    }
215
216
    public function testGetNonexistingParameter()
217
    {
218
        $this->expectException(UndefinedMethodException::class);
219
        $this->expectExceptionMessageRegExp('/^Call to undefined method/');
220
221
        $builder = $this->getBuilderMock();
222
        $builder->getDummy();
0 ignored issues
show
Bug introduced by
The method getDummy() does not exist on Feedo\ArgumentBuilder\Te...res\MockArgumentBuilder. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

222
        $builder->/** @scrutinizer ignore-call */ 
223
                  getDummy();
Loading history...
223
    }
224
225
    public function testInvalidSubValue()
226
    {
227
        $this->expectException(InvalidArgumentException::class);
228
        $this->expectExceptionMessageRegExp('/^Value of the field ".+" must an instance of ArgumentBuilderInterface$/');
229
230
        $builder = $this->getBuilderMock();
231
        $builder->setSub1(new \stdClass());
232
    }
233
234
    public function testInvalidSubValueNonObject()
235
    {
236
        $this->expectException(InvalidArgumentException::class);
237
        $this->expectExceptionMessageRegExp('/^Invalid value type. Expected instance of ".+", got ".+".$/');
238
239
        $builder = $this->getBuilderMock();
240
        $builder->setSub1(array('xxx'));
241
    }
242
243
    public function testSetValueInvalidArgCount()
244
    {
245
        $this->expectException(InvalidArgumentException::class);
246
        $this->expectExceptionMessageRegExp('/^Method .+ must take exactly 1 argument$/');
247
248
        $builder = $this->getBuilderMock();
249
        $builder->setArg1('aaa', 'bbb');
250
    }
251
252
    /**
253
     * @dataProvider provideNonstringData
254
     */
255
    public function testSetValueInvalidFirstParamSyntaxSugar($value)
256
    {
257
        $this->expectException(InvalidArgumentException::class);
258
        $this->expectExceptionMessageRegExp('/^Method .+ expects the first parameter to be string$/');
259
260
        $builder = $this->getBuilderMock();
261
        $builder->setSub1($value, 'bbb');
262
    }
263
264
    public function testUnsetWrongArgumentCount()
265
    {
266
        $this->expectException(InvalidArgumentException::class);
267
        $this->expectExceptionMessageRegExp('/^Method .+ must take exactly 0 arguments$/');
268
269
        $builder = $this->getBuilderMock();
270
        $builder->unsetArg1('bbb');
271
    }
272
273
    /**
274
     * @dataProvider provideNonstringData
275
     */
276
    public function testUnsetInvalidArgument($value)
277
    {
278
        $this->expectException(InvalidArgumentException::class);
279
        $this->expectExceptionMessageRegExp('/^Method .+ expects the first parameter to be string if you want to unset sub-value$/');
280
281
        $builder = $this->getBuilderMock();
282
        $builder->unsetSub1($value);
283
    }
284
285
    public function testArgumentBuilderTypeClassDoesNotExist()
286
    {
287
        $this->expectException(InvalidDefinitionException::class);
288
        $this->expectExceptionMessageRegExp('/^Class ".+" not found \(field\: ".+"\)$/');
289
290
        $builder = new CustomMockArgumentBuilder(array(
291
            'arg1' => 'DummyNonExistantClass',
292
        ));
293
        $builder->setArg1('something');
294
    }
295
296
    /**
297
     * @dataProvider provideInvalidValidatorData
298
     */
299
    public function testArgumentBuilderInvalidValidator($validator)
300
    {
301
        $this->expectException(InvalidDefinitionException::class);
302
        $this->expectExceptionMessageRegExp('/^Validator for the field ".+" is defined but is not callable$/');
303
304
        $builder = new CustomMockArgumentBuilder(array(
305
            'arg1' => array(
306
                'type' => AbstractArgumentBuilder::ARGUMENT_TYPE_MIXED,
307
                'validator' => $validator,
308
            ),
309
        ));
310
        $builder->setArg1('something');
311
    }
312
313
    /**
314
     * @dataProvider provideEmptyValidatorData
315
     */
316
    public function testArgumentBuilderEmptyValidator($validator)
317
    {
318
        $builder = new CustomMockArgumentBuilder(array(
319
            'arg1' => array(
320
                'type' => AbstractArgumentBuilder::ARGUMENT_TYPE_MIXED,
321
                'validator' => $validator,
322
            ),
323
        ));
324
        $builder->setArg1('something');
325
    }
326
327
    public function testArgumentBuilderValidatorResultFalse()
328
    {
329
        $this->expectException(InvalidArgumentException::class);
330
        $this->expectExceptionMessageRegExp('/^Invalid value ".+" for field ".+"$/');
331
332
        $builder = new CustomMockArgumentBuilder(array(
333
            'arg1' => array(
334
                'type' => AbstractArgumentBuilder::ARGUMENT_TYPE_MIXED,
335
                'validator' => function () {
336
                    return false;
337
                },
338
            ),
339
        ));
340
        $builder->setArg1('something');
341
    }
342
343
    /**
344
     * @return array
345
     */
346
    public function provideInvalidValidatorData()
347
    {
348
        return [
349
            [0],
350
            [1],
351
            ['nonexistantfunction'],
352
            [new \stdClass()],
353
            [array()],
354
            [array(new \stdClass(), 'some')],
355
        ];
356
    }
357
358
    /**
359
     * @return array
360
     */
361
    public function provideNonstringData()
362
    {
363
        return [
364
            [0],
365
            [1],
366
            [new \stdClass()],
367
            [array()],
368
        ];
369
    }
370
371
    /**
372
     * @return array
373
     */
374
    public function provideEmptyValidatorData()
375
    {
376
        return [
377
            [null],
378
            [false],
379
        ];
380
    }
381
}
382