22
33namespace EzSystems \Raml2Html \Test ;
44
5+ use Symfony \Component \Console \Output \Output ;
6+
57class ReferenceTester
68{
7- const TEST_REFERENCE_ROUTES = 1 ;
8- const TEST_CONFIG_ROUTES = 2 ;
9- const TEST_ALL_ROUTES = 3 ;
9+ public const TEST_REFERENCE_ROUTES = 1 ;
10+ public const TEST_CONFIG_ROUTES = 2 ;
11+ public const TEST_ALL_ROUTES = 3 ;
1012
11- const DEFAULT_FILE_LIST = [
13+ public const DEFAULT_FILE_LIST = [
1214 'vendor/ibexa/rest/src/bundle/Resources/config/routing.yml ' ,
1315 'vendor/ibexa/commerce-rest/src/bundle/Resources/config/routing.yaml ' ,
1416 // `find $dxpRoot/vendor/ibexa -name "routing_rest.y*ml"`
@@ -21,15 +23,34 @@ class ReferenceTester
2123 'vendor/ibexa/taxonomy/src/bundle/Resources/config/routing_rest.yaml ' ,
2224 ];
2325
24- private $ apiUri = '/api/ibexa/v2 ' ;
26+ public const METHOD_LIST = [
27+ 'OPTIONS ' ,
28+ 'GET ' ,
29+ 'HEAD ' ,
30+ 'POST ' ,
31+ 'PATCH ' ,
32+ 'COPY ' ,
33+ 'MOVE ' ,
34+ 'SWAP ' ,
35+ 'PUBLISH ' ,
36+ 'DELETE ' ,
37+ ];
38+
39+ public $ apiUri = '/api/ibexa/v2 ' ;
40+
41+ private const REF_METHOD_NOT_IN_CONF = 'ref_route_method_missing_from_conf ' ;
42+ private const CONF_METHOD_NOT_IN_REF = 'conf_route_method_missing_from_ref ' ;
2543
2644 private $ restApiReference ;
2745 private $ dxpRoot ;
2846
2947 private $ refRoutes ;
3048 private $ confRoutes ;
3149
32- public function __construct ($ restApiReference , $ dxpRoot , $ consolePath = 'bin/console ' , $ routingFiles = null )
50+ /** @var Output */
51+ private $ output ;
52+
53+ public function __construct ($ restApiReference , $ dxpRoot , $ consolePath = 'bin/console ' , $ routingFiles = null , Output $ output = null )
3354 {
3455 if (!is_file ($ restApiReference )) {
3556 user_error ("$ restApiReference doesn't exist or is not a file " , E_USER_ERROR );
@@ -43,6 +64,8 @@ public function __construct($restApiReference, $dxpRoot, $consolePath = 'bin/con
4364 exit (2 );
4465 }
4566
67+ $ this ->output = $ output ;
68+
4669 $ this ->restApiReference = $ restApiReference ;
4770 $ this ->dxpRoot = $ dxpRoot ;
4871 $ this ->parseApiReference ($ this ->restApiReference );
@@ -144,8 +167,7 @@ private function parseRoutingFiles($routingFiles): void
144167 foreach ($ parsedRoutingFile as $ routeId => $ routeDef ) {
145168 $ line = (int )explode (': ' , `grep -n '^ $ routeId:$' {$ this ->dxpRoot }/ $ routingFile `)[0 ];
146169 if (!array_key_exists ('methods ' , $ routeDef )) {
147- user_error ("$ routeId ( $ routingFile@ $ line) matches every methods by default; skipped " , E_USER_WARNING );
148- continue ;
170+ $ routeDef ['methods ' ] = self ::METHOD_LIST ;
149171 }
150172 if (!array_key_exists ($ routeDef ['path ' ], $ confRoutes )) {
151173 $ confRoutes [$ routeDef ['path ' ]] = [
@@ -170,45 +192,54 @@ public function run(int $testedRoutes = self::TEST_ALL_ROUTES)
170192 $ refRoutes = $ this ->refRoutes ;
171193 $ confRoutes = $ this ->confRoutes ;
172194
195+ // Check methods from routes found in both reference and configuration
173196 foreach (array_intersect (array_keys ($ refRoutes ), array_keys ($ confRoutes )) as $ commonRoute ) {
174197 $ missingMethods = $ this ->compareMethods ($ commonRoute , $ commonRoute , $ testedRoutes );
175198 if (!array_key_exists ('GET ' , $ refRoutes [$ commonRoute ]['methods ' ]) && array_key_exists ('HEAD ' , $ refRoutes [$ commonRoute ]['methods ' ])
176199 && array_key_exists ('GET ' , $ confRoutes [$ commonRoute ]['methods ' ]) && array_key_exists ('HEAD ' , $ confRoutes [$ commonRoute ]['methods ' ])
177200 && !is_null ($ confRoutes [$ commonRoute ]['methods ' ]['HEAD ' ]['id ' ]) && $ confRoutes [$ commonRoute ]['methods ' ]['GET ' ]['id ' ] === $ confRoutes [$ commonRoute ]['methods ' ]['HEAD ' ]['id ' ]) {
178- echo "\t$ commonRoute has no GET reference but has a HEAD reference, HEAD and GET share the same route id ( {$ confRoutes [$ commonRoute ]['methods ' ]['GET ' ]['id ' ]}) so GET might be just a fallback for HEAD. \n" ;
201+ $ this -> output ( "\t$ commonRoute has no GET reference but has a HEAD reference, HEAD and GET share the same configuration route id ( {$ confRoutes [$ commonRoute ]['methods ' ]['GET ' ]['id ' ]}) so GET might be just a fallback for HEAD. " ) ;
179202 }
180- if ($ missingMethods && false !== strpos ($ commonRoute , '{ ' )) {
181- $ similarRefRoutes = $ this ->getSimilarRoutes ($ commonRoute , $ refRoutes );
182- $ similarConfRoutes = $ this ->getSimilarRoutes ($ commonRoute , $ confRoutes );
183- foreach (['highly ' , 'poorly ' ] as $ similarityLevel ) {
184- foreach ($ similarRefRoutes [$ similarityLevel ] as $ refRoute ) {
185- if ($ refRoute === $ commonRoute ) {
186- continue ;
187- }
188- $ stillMissingMethod = $ this ->compareMethods ($ refRoute , $ commonRoute , $ testedRoutes , $ missingMethods );
189- $ foundMethods = array_diff ($ missingMethods , $ stillMissingMethod );
190- if (!empty ($ foundMethods )) {
191- foreach ($ foundMethods as $ foundMethod ) {
192- if ('highly ' === $ similarityLevel ) {
193- echo "\t$ refRoute has $ foundMethod and is highly similar to $ commonRoute \n" ;
194- } else {
195- echo "\t$ refRoute has $ foundMethod and is a bit similar to $ commonRoute \n" ;
203+ if (false !== strpos ($ commonRoute , '{ ' )) {
204+ if (self ::TEST_REFERENCE_ROUTES & $ testedRoutes && $ missingMethods [self ::REF_METHOD_NOT_IN_CONF ]) {
205+ // Check reference route's methods not found in the configuration against similar routes from configuration
206+ $ similarConfRoutes = $ this ->getSimilarRoutes ($ commonRoute , $ confRoutes );
207+ foreach (['highly ' , 'poorly ' ] as $ similarityLevel ) {
208+ foreach ($ similarConfRoutes [$ similarityLevel ] as $ confRoute ) {
209+ if ($ confRoute === $ commonRoute ) {
210+ continue ;
211+ }
212+ $ stillMissingMethod = $ this ->compareMethods ($ commonRoute , $ confRoute , self ::TEST_REFERENCE_ROUTES , $ missingMethods [self ::REF_METHOD_NOT_IN_CONF ]);
213+ $ foundMethods = array_diff ($ missingMethods [self ::REF_METHOD_NOT_IN_CONF ], $ stillMissingMethod [self ::REF_METHOD_NOT_IN_CONF ]);
214+ if (!empty ($ foundMethods )) {
215+ foreach ($ foundMethods as $ foundMethod ) {
216+ if ('highly ' === $ similarityLevel ) {
217+ $ this ->output ("\t{$ this ->getConfRoutePrompt ($ confRoute )} has $ foundMethod and is highly similar to $ commonRoute " );
218+ } else {
219+ $ this ->output ("\t{$ this ->getConfRoutePrompt ($ confRoute )} has $ foundMethod and is a bit similar to $ commonRoute " );
220+ }
196221 }
197222 }
198223 }
199224 }
200- foreach ($ similarConfRoutes [$ similarityLevel ] as $ confRoute ) {
201- if ($ confRoute === $ commonRoute ) {
202- continue ;
203- }
204- $ stillMissingMethod = $ this ->compareMethods ($ commonRoute , $ confRoute , $ testedRoutes , $ missingMethods );
205- $ foundMethods = array_diff ($ missingMethods , $ stillMissingMethod );
206- if (!empty ($ foundMethods )) {
207- foreach ($ foundMethods as $ foundMethod ) {
208- if ('highly ' === $ similarityLevel ) {
209- echo "\t{$ this ->getConfRoutePrompt ($ confRoute )} has $ foundMethod and is highly similar to $ commonRoute \n" ;
210- } else {
211- echo "\t{$ this ->getConfRoutePrompt ($ confRoute )} has $ foundMethod and is a bit similar to $ commonRoute \n" ;
225+ }
226+ if (self ::TEST_CONFIG_ROUTES & $ testedRoutes ) {
227+ // Check configuration route's methods not found in the reference against similar routes from reference
228+ $ similarRefRoutes = $ this ->getSimilarRoutes ($ commonRoute , $ refRoutes );
229+ foreach (['highly ' , 'poorly ' ] as $ similarityLevel ) {
230+ foreach ($ similarRefRoutes [$ similarityLevel ] as $ refRoute ) {
231+ if ($ refRoute === $ commonRoute ) {
232+ continue ;
233+ }
234+ $ stillMissingMethod = $ this ->compareMethods ($ refRoute , $ commonRoute , self ::TEST_CONFIG_ROUTES , $ missingMethods [self ::CONF_METHOD_NOT_IN_REF ]);
235+ $ foundMethods = array_diff ($ missingMethods [self ::CONF_METHOD_NOT_IN_REF ], $ stillMissingMethod [self ::CONF_METHOD_NOT_IN_REF ]);
236+ if (!empty ($ foundMethods )) {
237+ foreach ($ foundMethods as $ foundMethod ) {
238+ if ('highly ' === $ similarityLevel ) {
239+ $ this ->output ("\t$ refRoute has $ foundMethod and is highly similar to $ commonRoute " );
240+ } else {
241+ $ this ->output ("\t$ refRoute has $ foundMethod and is a bit similar to $ commonRoute " );
242+ }
212243 }
213244 }
214245 }
@@ -218,84 +249,101 @@ public function run(int $testedRoutes = self::TEST_ALL_ROUTES)
218249 }
219250
220251 if (self ::TEST_REFERENCE_ROUTES & $ testedRoutes ) {
252+ // Check reference routes not found in the configuration
221253 foreach (array_diff (array_keys ($ refRoutes ), array_keys ($ confRoutes )) as $ refRouteWithoutConf ) {
254+ $ this ->output ("$ refRouteWithoutConf not found in config files. " );
222255 if (false !== strpos ($ refRouteWithoutConf , '{ ' )) {
223256 $ similarConfRoutes = $ this ->getSimilarRoutes ($ refRouteWithoutConf , $ confRoutes );
224257 if (!empty ($ similarConfRoutes ['highly ' ])) {
225- echo "$ refRouteWithoutConf not found in config files but \n" ;
226258 foreach ($ similarConfRoutes ['highly ' ] as $ confRoute ) {
227- echo "\t$ refRouteWithoutConf is highly similar to $ confRoute\n" ;
228- $ this ->compareMethods ($ refRouteWithoutConf , $ confRoute , $ testedRoutes );
259+ $ this -> output ( "\t$ refRouteWithoutConf is highly similar to $ confRoute" ) ;
260+ $ this ->compareMethods ($ refRouteWithoutConf , $ confRoute , self :: TEST_REFERENCE_ROUTES );
229261 }
230262 continue ;
231263 }
232264 if (!empty ($ similarConfRoutes ['poorly ' ])) {
233- echo "$ refRouteWithoutConf not found in config files but \n" ;
234265 foreach ($ similarConfRoutes ['poorly ' ] as $ confRoute ) {
235- echo "\t$ refRouteWithoutConf is a bit similar to $ confRoute\n" ;
236- $ this ->compareMethods ($ refRouteWithoutConf , $ confRoute , $ testedRoutes );
266+ $ this -> output ( "\t$ refRouteWithoutConf is a bit similar to $ confRoute" ) ;
267+ $ this ->compareMethods ($ refRouteWithoutConf , $ confRoute , self :: TEST_REFERENCE_ROUTES );
237268 }
238- continue ;
239269 }
240270 }
241- echo "$ refRouteWithoutConf not found in config files. \n" ;
242271 }
243272 }
244273
245274 if (self ::TEST_CONFIG_ROUTES & $ testedRoutes ) {
275+ // Check configuration routes not found in the reference
246276 foreach (array_diff (array_keys ($ confRoutes ), array_keys ($ refRoutes )) as $ confRouteWithoutRef ) {
277+ $ this ->output ("{$ this ->getConfRoutePrompt ($ confRouteWithoutRef )} not found in reference. " );
247278 if (false !== strpos ($ confRouteWithoutRef , '{ ' )) {
248279 $ similarRefRoutes = $ this ->getSimilarRoutes ($ confRouteWithoutRef , $ refRoutes );
249280 if (!empty ($ similarRefRoutes ['highly ' ])) {
250- echo "{$ this ->getConfRoutePrompt ($ confRouteWithoutRef )} not found in reference but \n" ;
251281 foreach ($ similarRefRoutes ['highly ' ] as $ refRoute ) {
252- echo "\t$ confRouteWithoutRef is highly similar to $ refRoute\n" ;
253- $ this ->compareMethods ($ refRoute , $ confRouteWithoutRef , $ testedRoutes );
282+ $ this -> output ( "\t$ confRouteWithoutRef is highly similar to $ refRoute" ) ;
283+ $ this ->compareMethods ($ refRoute , $ confRouteWithoutRef , self :: TEST_CONFIG_ROUTES );
254284 }
255285 continue ;
256286 }
257287 if (!empty ($ similarRefRoutes ['poorly ' ])) {
258- echo "{$ this ->getConfRoutePrompt ($ confRouteWithoutRef )} not found in reference but \n" ;
259288 foreach ($ similarRefRoutes ['poorly ' ] as $ refRoute ) {
260- echo "\t$ confRouteWithoutRef is a bit similar to $ refRoute\n" ;
261- $ this ->compareMethods ($ refRoute , $ confRouteWithoutRef , $ testedRoutes );
289+ $ this -> output ( "\t$ confRouteWithoutRef is a bit similar to $ refRoute" ) ;
290+ $ this ->compareMethods ($ refRoute , $ confRouteWithoutRef , self :: TEST_CONFIG_ROUTES );
262291 }
263- continue ;
264292 }
265293 }
266- echo "{$ this ->getConfRoutePrompt ($ confRouteWithoutRef )} not found in reference. \n" ;
267294 }
268295 }
269296 }
270297
271- private function compareMethods (string $ refRoute , string $ confRoute , int $ testedRoutes = self ::TEST_ALL_ROUTES , ?array $ testedMethods = null ): array
298+ /**
299+ * Compare reference route methods and configuration route methods, output methods missing on one side or the other.
300+ * @param array|null $testedMethods A list of methods to search for and compare; if null, all existing methods are compared
301+ * @return array A list of missing methods
302+ */
303+ private
304+ function compareMethods (string $ refRoute , string $ confRoute , int $ testedRoutes = self ::TEST_ALL_ROUTES , ?array $ testedMethods = null ): array
272305 {
273306 $ refRoutes = $ this ->refRoutes ;
274307 $ confRoutes = $ this ->confRoutes ;
275- $ missingMethods = [];
308+ $ missingMethods = [
309+ self ::REF_METHOD_NOT_IN_CONF => [],
310+ self ::CONF_METHOD_NOT_IN_REF => [],
311+
312+ ];
276313
277314 if (self ::TEST_REFERENCE_ROUTES & $ testedRoutes ) {
315+ // Check reference route's methods missing from configuration route
278316 foreach (array_diff (array_keys ($ refRoutes [$ refRoute ]['methods ' ]), array_keys ($ confRoutes [$ confRoute ]['methods ' ])) as $ refMethodWithoutConf ) {
279317 if (null === $ testedMethods || in_array ($ refMethodWithoutConf , $ testedMethods )) {
280- echo "$ refRoute: $ refMethodWithoutConf method not found in conf files " . ($ refRoute === $ confRoute ? '' : " (while comparing to $ confRoute) " ) . ". \n" ;
281- $ missingMethods [] = $ refMethodWithoutConf ;
318+ if ($ refRoute === $ confRoute ) {
319+ $ this ->output ("$ refRoute: $ refMethodWithoutConf not found in configuration. " );
320+ } else {
321+ $ this ->output ("\t$ refMethodWithoutConf not found in configuration while comparing to $ confRoute. " );
322+ }
323+ $ missingMethods [self ::REF_METHOD_NOT_IN_CONF ][] = $ refMethodWithoutConf ;
282324 }
283325 }
284326 }
285327
286328 if (self ::TEST_CONFIG_ROUTES & $ testedRoutes ) {
329+ // Check configuration route's methods missing from reference route
287330 foreach (array_diff (array_keys ($ confRoutes [$ confRoute ]['methods ' ]), array_keys ($ refRoutes [$ refRoute ]['methods ' ])) as $ confMethodWithoutRef ) {
288331 if (null === $ testedMethods || in_array ($ confMethodWithoutRef , $ testedMethods )) {
289- echo "{$ this ->getConfRoutePrompt ($ confRoute , $ confMethodWithoutRef )}: $ confMethodWithoutRef not found in reference " . ($ refRoute === $ confRoute ? '' : " (while comparing to $ refRoute) " ) . ". \n" ;
290- $ missingMethods [] = $ confMethodWithoutRef ;
332+ if ($ refRoute === $ confRoute ) {
333+ $ this ->output ("{$ this ->getConfRoutePrompt ($ confRoute , $ confMethodWithoutRef )}: $ confMethodWithoutRef not found in reference. " );
334+ } else {
335+ $ this ->output ("\t$ confMethodWithoutRef not found in reference while comparing to $ refRoute. " );
336+ }
337+ $ missingMethods [self ::CONF_METHOD_NOT_IN_REF ][] = $ confMethodWithoutRef ;
291338 }
292339 }
293340 }
294341
295342 return $ missingMethods ;
296343 }
297344
298- private function getSimilarRoutes (string $ path , array $ routeCollection ): array
345+ private
346+ function getSimilarRoutes (string $ path , array $ routeCollection ): array
299347 {
300348 $ routePattern = $ this ->getRoutePattern ($ path );
301349 $ highlySimilarRoutes = [];
@@ -315,17 +363,20 @@ private function getSimilarRoutes(string $path, array $routeCollection): array
315363 ];
316364 }
317365
318- private function getSimplifiedRoute (string $ path ): string
366+ private
367+ function getSimplifiedRoute (string $ path ): string
319368 {
320369 return str_replace (['identifier ' , 'number ' , '_ ' , '- ' ], ['id ' , 'no ' , '' ], strtolower ($ path ));
321370 }
322371
323- private function getRoutePattern (string $ path ): string
372+ private
373+ function getRoutePattern (string $ path ): string
324374 {
325375 return '@^ ' . preg_replace ('@\{[^}]+\}@ ' , '\{[^}]+\} ' , $ path ) . '$@ ' ;
326376 }
327377
328- private function getConfRoutePrompt (string $ path , $ method = null ): string
378+ private
379+ function getConfRoutePrompt (string $ path , $ method = null ): string
329380 {
330381 $ prompt = $ path ;
331382
@@ -369,4 +420,14 @@ private function getConfRoutePrompt(string $path, $method = null): string
369420
370421 return $ prompt ;
371422 }
423+
424+ private
425+ function output ($ message )
426+ {
427+ if ($ this ->output ) {
428+ $ this ->output ->writeln ($ message );
429+ } else {
430+ echo strip_tags ($ message ) . "\n" ;
431+ }
432+ }
372433}
0 commit comments