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
|
|||||||
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
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
![]() |
|||||||
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
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
![]() |
|||||||
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
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
![]() |
|||||||
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
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
![]() |
|||||||
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
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
![]() |
|||||||
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
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
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. ![]() |
|||||||
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
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
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. ![]() |
|||||||
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
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
![]() |
|||||||
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 |
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.