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
arrayas a return type and suggests a more specific type. This type is inferred from the actual code.