Skip to content

Commit 62ff107

Browse files
committed
TestCommand.php: Enh. output
1 parent 79e058e commit 62ff107

File tree

2 files changed

+127
-65
lines changed

2 files changed

+127
-65
lines changed

tools/raml2html/src/Command/TestCommand.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use EzSystems\Raml2Html\Test\ReferenceTester;
88
use Symfony\Component\Console\Command\Command;
9+
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
910
use Symfony\Component\Console\Input\InputInterface;
1011
use Symfony\Component\Console\Input\InputArgument;
1112
use Symfony\Component\Console\Input\InputOption;
@@ -19,7 +20,7 @@ final class TestCommand extends Command
1920
protected function configure(): void
2021
{
2122
$this->setName('test')
22-
->setDescription('Compare REST API Reference with Ibexa DXP routing configuration under /api/ibexa/v2 prefix')
23+
->setDescription('Compare REST API Reference documentation with Ibexa DXP routing configuration under /api/ibexa/v2 prefix')
2324
->setHelp('It is recommended to not use --console-path and --routing-file options while testing the Rest API Reference HTML file against configuration. Those options are used to test that the default configuration file list is up-to-date and other subtleties.')
2425
->addArgument('rest-api-reference', InputArgument::REQUIRED, 'Path to the REST API Reference HTML file')
2526
->addArgument('ibexa-dxp-root', InputArgument::REQUIRED, 'Path to an Ibexa DXP root directory')
@@ -55,7 +56,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
5556

5657
$routingFiles = $input->getOption('routing-file');
5758

58-
$referenceTester = new ReferenceTester($restApiReference, $dxpRoot, $consolePath, $routingFiles);
59+
$referenceTester = new ReferenceTester($restApiReference, $dxpRoot, $consolePath, $routingFiles, $output);
5960

6061
$testedRoutes = @[
6162
'ref' => ReferenceTester::TEST_REFERENCE_ROUTES,

tools/raml2html/src/Test/ReferenceTester.php

Lines changed: 124 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
namespace EzSystems\Raml2Html\Test;
44

5+
use Symfony\Component\Console\Output\Output;
6+
57
class 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

Comments
 (0)