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 |
||
19 | class ShowcaseControllerTest extends RestTestCase |
||
20 | { |
||
21 | /** |
||
22 | * @const complete content type string expected on a resouce |
||
23 | */ |
||
24 | const CONTENT_TYPE = 'application/json; charset=UTF-8; profile=http://localhost/schema/hans/showcase/item'; |
||
25 | |||
26 | /** |
||
27 | * @const corresponding vendorized schema mime type |
||
28 | */ |
||
29 | const COLLECTION_TYPE = 'application/json; charset=UTF-8; profile=http://localhost/schema/hans/showcase/collection'; |
||
30 | |||
31 | /** |
||
32 | * suppress setup client and load fixtures of parent class |
||
33 | * |
||
34 | * @return void |
||
35 | */ |
||
36 | public function setUp() |
||
37 | { |
||
38 | $this->loadFixtures( |
||
39 | [ |
||
40 | LoadLanguageData::class |
||
41 | ], |
||
42 | null, |
||
43 | 'doctrine_mongodb' |
||
44 | ); |
||
45 | } |
||
46 | |||
47 | /** |
||
48 | * checks empty objects |
||
49 | * |
||
50 | * @return void |
||
51 | */ |
||
52 | public function testGetEmptyObject() |
||
91 | |||
92 | /** |
||
93 | * see how our missing fields are explained to us |
||
94 | * |
||
95 | * @return void |
||
96 | */ |
||
97 | public function testMissingFields() |
||
98 | { |
||
99 | $document = json_decode( |
||
100 | file_get_contents(dirname(__FILE__).'/../resources/showcase-incomplete.json'), |
||
101 | true |
||
102 | ); |
||
103 | |||
104 | $client = static::createRestClient(); |
||
105 | $client->post('/hans/showcase', $document); |
||
106 | |||
107 | $expectedErrors = []; |
||
108 | $notNullError = new \stdClass(); |
||
109 | $notNullError->propertyPath = 'aBoolean'; |
||
110 | $notNullError->message = 'The property aBoolean is required'; |
||
111 | $expectedErrors[] = $notNullError; |
||
112 | // test choices field (string should not be blank) |
||
113 | $notNullErrorChoices = new \stdClass(); |
||
114 | $notNullErrorChoices->propertyPath = 'choices'; |
||
115 | $notNullErrorChoices->message = 'The property choices is required'; |
||
116 | $expectedErrors[] = $notNullErrorChoices; |
||
117 | |||
118 | $this->assertJsonStringEqualsJsonString( |
||
119 | json_encode($expectedErrors), |
||
120 | json_encode($client->getResults()) |
||
121 | ); |
||
122 | } |
||
123 | |||
124 | /** |
||
125 | * see how our empty fields are explained to us |
||
126 | * |
||
127 | * @return void |
||
128 | */ |
||
129 | public function testEmptyAllFields() |
||
130 | { |
||
131 | $document = [ |
||
132 | 'anotherInt' => 6555488894525, |
||
133 | 'testField' => ['en' => 'a test string'], |
||
134 | 'aBoolean' => '', |
||
135 | 'contactCode' => [ |
||
136 | 'text' => ['en' => 'Some Text'], |
||
137 | 'someDate' => '1984-05-01T00:00:00+0000', |
||
138 | ], |
||
139 | 'contact' => [ |
||
140 | 'type' => '', |
||
141 | 'value' => '', |
||
142 | 'protocol' => '', |
||
143 | ], |
||
144 | ]; |
||
145 | |||
146 | $client = static::createRestClient(); |
||
147 | $client->post('/hans/showcase', $document); |
||
148 | |||
149 | $this->assertEquals( |
||
150 | Response::HTTP_BAD_REQUEST, |
||
151 | $client->getResponse()->getStatusCode() |
||
152 | ); |
||
153 | |||
154 | $this->assertEquals( |
||
155 | [ |
||
156 | (object) [ |
||
157 | 'propertyPath' => 'choices', |
||
158 | 'message' => 'The property choices is required', |
||
159 | ], |
||
160 | (object) [ |
||
161 | 'propertyPath' => 'aBoolean', |
||
162 | 'message' => 'String value found, but a boolean is required', |
||
163 | ], |
||
164 | (object) [ |
||
165 | 'propertyPath' => 'contact.type', |
||
166 | 'message' => 'Must be at least 1 characters long', |
||
167 | ], |
||
168 | (object) [ |
||
169 | 'propertyPath' => 'contact.protocol', |
||
170 | 'message' => 'Must be at least 1 characters long', |
||
171 | ], |
||
172 | (object) [ |
||
173 | 'propertyPath' => 'contact.value', |
||
174 | 'message' => 'Must be at least 1 characters long', |
||
175 | ] |
||
176 | ], |
||
177 | $client->getResults() |
||
178 | ); |
||
179 | } |
||
180 | |||
181 | /** |
||
182 | * see how our empty fields are explained to us |
||
183 | * |
||
184 | * @return void |
||
185 | */ |
||
186 | public function testEmptyFields() |
||
187 | { |
||
188 | $document = [ |
||
189 | 'anotherInt' => 6555488894525, |
||
190 | 'testField' => ['en' => 'a test string'], |
||
191 | 'aBoolean' => true, |
||
192 | 'contactCode' => [ |
||
193 | 'text' => ['en' => 'Some Text'], |
||
194 | 'someDate' => '1984-05-01T00:00:00+0000', |
||
195 | ], |
||
196 | 'contact' => [ |
||
197 | 'type' => 'abc', |
||
198 | 'value' => '', |
||
199 | 'protocol' => '', |
||
200 | ], |
||
201 | ]; |
||
202 | |||
203 | $client = static::createRestClient(); |
||
204 | $client->post('/hans/showcase', $document); |
||
205 | |||
206 | $this->assertEquals( |
||
207 | Response::HTTP_BAD_REQUEST, |
||
208 | $client->getResponse()->getStatusCode() |
||
209 | ); |
||
210 | |||
211 | $this->assertEquals( |
||
212 | [ |
||
213 | (object) [ |
||
214 | 'propertyPath' => 'choices', |
||
215 | 'message' => 'The property choices is required', |
||
216 | ], |
||
217 | (object) [ |
||
218 | 'propertyPath' => 'contact.protocol', |
||
219 | 'message' => 'Must be at least 1 characters long', |
||
220 | ], |
||
221 | (object) [ |
||
222 | 'propertyPath' => 'contact.value', |
||
223 | 'message' => 'Must be at least 1 characters long', |
||
224 | ], |
||
225 | ], |
||
226 | $client->getResults() |
||
227 | ); |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * make sure an invalid choice value is detected |
||
232 | * |
||
233 | * @return void |
||
234 | */ |
||
235 | public function testWrongChoiceValue() |
||
236 | { |
||
237 | $payload = json_decode(file_get_contents($this->postCreationDataProvider()['minimal'][0])); |
||
238 | $payload->choices = 'invalidChoice'; |
||
239 | |||
240 | $client = static::createRestClient(); |
||
241 | $client->post('/hans/showcase', $payload); |
||
242 | $this->assertEquals(400, $client->getResponse()->getStatusCode()); |
||
243 | |||
244 | $expectedErrors = []; |
||
245 | $expectedErrors[0] = new \stdClass(); |
||
246 | $expectedErrors[0]->propertyPath = "choices"; |
||
247 | $expectedErrors[0]->message = 'Does not have a value in the enumeration ["<",">","=",">=","<=","<>"]'; |
||
248 | |||
249 | $this->assertJsonStringEqualsJsonString( |
||
250 | json_encode($expectedErrors), |
||
251 | json_encode($client->getResults()) |
||
252 | ); |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * make sure an invalid extref value is detected |
||
257 | * |
||
258 | * @return void |
||
259 | */ |
||
260 | public function testWrongExtRef() |
||
261 | { |
||
262 | $payload = json_decode(file_get_contents($this->postCreationDataProvider()['minimal'][0])); |
||
263 | $payload->nestedApps = [ |
||
264 | (object) ['$ref' => 'http://localhost/core/module/name'], |
||
265 | (object) ['$ref' => 'unknown'] |
||
266 | ]; |
||
267 | |||
268 | $client = static::createRestClient(); |
||
269 | $client->post('/hans/showcase', $payload); |
||
270 | $this->assertEquals(400, $client->getResponse()->getStatusCode()); |
||
271 | |||
272 | $expectedErrors = [ |
||
273 | (object) [ |
||
274 | 'propertyPath' => "nestedApps[0].\$ref", |
||
275 | 'message' => |
||
276 | 'Value "http://localhost/core/module/name" does not refer to a correct collection for this extref.' |
||
277 | ], |
||
278 | (object) [ |
||
279 | 'propertyPath' => "nestedApps[1].\$ref", |
||
280 | 'message' => |
||
281 | 'Does not match the regex pattern (\/core\/app\/)([a-zA-Z0-9\-_\+\040\'\.]+)$' |
||
282 | ] |
||
283 | ]; |
||
284 | |||
285 | $this->assertJsonStringEqualsJsonString( |
||
286 | json_encode($expectedErrors), |
||
287 | json_encode($client->getResults()) |
||
288 | ); |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * insert various formats to see if all works as expected |
||
293 | * |
||
294 | * @dataProvider postCreationDataProvider |
||
295 | * |
||
296 | * @param string $filename filename |
||
297 | * |
||
298 | * @return void |
||
299 | */ |
||
300 | public function testPost($filename) |
||
329 | |||
330 | /** |
||
331 | * Provides test sets for the testPost() test. |
||
332 | * |
||
333 | * @return array |
||
|
|||
334 | */ |
||
335 | public function postCreationDataProvider() |
||
344 | |||
345 | /** |
||
346 | * test if we can save & retrieve extrefs inside 'free form objects' |
||
347 | * |
||
348 | * @return void |
||
349 | */ |
||
350 | public function testFreeFormExtRefs() |
||
390 | |||
391 | /** |
||
392 | * are extra fields denied? |
||
393 | * |
||
394 | * @return void |
||
395 | */ |
||
396 | public function testExtraFieldPost() |
||
425 | |||
426 | /** |
||
427 | * Test RQL select statement |
||
428 | * |
||
429 | * @return void |
||
430 | */ |
||
431 | public function testRqlSelect() |
||
481 | |||
482 | /** |
||
483 | * Test to see if we can do like() searches on identifier fields |
||
484 | * |
||
485 | * @return void |
||
486 | */ |
||
487 | public function testLikeSearchOnIdentifierField() |
||
508 | |||
509 | /** |
||
510 | * Test PATCH for deep nested attribute |
||
511 | * |
||
512 | * @return void |
||
513 | */ |
||
514 | View Code Duplication | public function testPatchDeepNestedProperty() |
|
547 | |||
548 | /** |
||
549 | * Test success PATCH method - response headers contains link to resource |
||
550 | * |
||
551 | * @return void |
||
552 | */ |
||
553 | public function testPatchSuccessResponseHeaderContainsResourceLink() |
||
581 | |||
582 | /** |
||
583 | * Test PATCH method - remove/change ID not allowed |
||
584 | * |
||
585 | * @return void |
||
586 | */ |
||
587 | public function testPatchRemoveAndChangeIdNotAllowed() |
||
609 | |||
610 | /** |
||
611 | * Test PATCH: add property to free object structure |
||
612 | * |
||
613 | * @return void |
||
614 | */ |
||
615 | View Code Duplication | public function testPatchAddPropertyToFreeObject() |
|
648 | |||
649 | /** |
||
650 | * Test PATCH for $ref attribute |
||
651 | * |
||
652 | * @return void |
||
653 | * @incomplete |
||
654 | */ |
||
655 | public function testApplyPatchForRefAttribute() |
||
692 | |||
693 | /** |
||
694 | * Test PATCH: apply patch which results to invalid Showcase schema |
||
695 | * |
||
696 | * @return void |
||
697 | */ |
||
698 | public function testPatchToInvalidShowcase() |
||
727 | |||
728 | /** |
||
729 | * Test PATCH: remove element from array |
||
730 | * |
||
731 | * @return void |
||
732 | */ |
||
733 | View Code Duplication | public function testRemoveFromArrayPatch() |
|
763 | |||
764 | /** |
||
765 | * Test PATCH: add new element to array |
||
766 | * |
||
767 | * @return void |
||
768 | */ |
||
769 | public function testAddElementToSpecificIndexInArrayPatch() |
||
804 | |||
805 | /** |
||
806 | * Test PATCH: add complex object App to array |
||
807 | * |
||
808 | * @group ref |
||
809 | * @return void |
||
810 | */ |
||
811 | View Code Duplication | public function testPatchAddComplexObjectToSpecificIndexInArray() |
|
846 | |||
847 | /** |
||
848 | * Test PATCH: add complex object App to array |
||
849 | * |
||
850 | * @group ref |
||
851 | * @return void |
||
852 | */ |
||
853 | View Code Duplication | public function testPatchAddComplexObjectToTheEndOfArray() |
|
888 | |||
889 | /** |
||
890 | * Test PATCH: test operation to undefined index |
||
891 | * |
||
892 | * @group ref |
||
893 | * @return void |
||
894 | */ |
||
895 | public function testPatchTestOperationToUndefinedIndexThrowsException() |
||
919 | |||
920 | /** |
||
921 | * Test PATCH: add complex object App to array |
||
922 | * |
||
923 | * @group ref |
||
924 | * @return void |
||
925 | */ |
||
926 | public function testPatchAddElementToUndefinedIndexResponseAsBadRequest() |
||
957 | |||
958 | /** |
||
959 | * Encode string value in RQL |
||
960 | * |
||
961 | * @param string $value String value |
||
962 | * @return string |
||
963 | */ |
||
964 | View Code Duplication | private function encodeRqlString($value) |
|
976 | |||
977 | /** |
||
978 | * Trigger a 301 Status code |
||
979 | * |
||
980 | * @param string $url requested url |
||
981 | * @param string $redirectUrl redirected url |
||
982 | * @dataProvider rqlDataProvider |
||
983 | * @return void |
||
984 | */ |
||
985 | View Code Duplication | public function testTrigger301($url, $redirectUrl) |
|
992 | |||
993 | /** |
||
994 | * Provides urls for the testTrigger301() test. |
||
995 | * |
||
996 | * @return array |
||
997 | */ |
||
998 | public function rqlDataProvider() |
||
1005 | |||
1006 | /** |
||
1007 | * Here we test the client expectation in "id" property exposing in the json schema. |
||
1008 | * |
||
1009 | * They want |
||
1010 | * * "id" of an extref object should *not* be described/present in schema |
||
1011 | * * "id" of others, including embedded objects, *should* be described/present in schema |
||
1012 | * |
||
1013 | * @return void |
||
1014 | */ |
||
1015 | public function testCorrectIdExposingInSchema() |
||
1032 | |||
1033 | /** |
||
1034 | * test finding of showcases by ref |
||
1035 | * |
||
1036 | * @dataProvider findByExtrefProvider |
||
1037 | * |
||
1038 | * @param string $field which reference to search in |
||
1039 | * @param mixed $url ref to search for |
||
1040 | * @param integer $count number of results to expect |
||
1041 | * |
||
1042 | * @return void |
||
1043 | */ |
||
1044 | public function testFindByExtref($field, $url, $count) |
||
1063 | |||
1064 | /** |
||
1065 | * @return array |
||
1066 | */ |
||
1067 | View Code Duplication | public function findByExtrefProvider() |
|
1092 | } |
||
1093 |
This check looks for the generic type
array
as a return type and suggests a more specific type. This type is inferred from the actual code.