Skip to content

Commit 3c98bc7

Browse files
committed
added tests for openapi
Signed-off-by: bidi47 <bidi@apidemia.com>
1 parent 8e39a84 commit 3c98bc7

1 file changed

Lines changed: 124 additions & 0 deletions

File tree

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ApiTest\Unit\App\OpenApi;
6+
7+
use OpenApi\Annotations\Operation;
8+
use OpenApi\Undefined;
9+
use PHPUnit\Framework\TestCase;
10+
use ReflectionAttribute;
11+
use ReflectionClass;
12+
13+
use function array_filter;
14+
use function basename;
15+
use function class_exists;
16+
use function count;
17+
use function dirname;
18+
use function glob;
19+
use function implode;
20+
use function is_string;
21+
use function sprintf;
22+
use function strtoupper;
23+
24+
/**
25+
* Every documented operation must carry an explicit `operationId`, and no two may share one.
26+
*
27+
* The attributes are read through reflection, before swagger-php processes them. In the generated document
28+
* the key is always present, because swagger-php's OperationId processor fills in a hash for any operation
29+
* without one, so only the source attributes show whether an ID was written.
30+
*/
31+
final class OperationIdTest extends TestCase
32+
{
33+
public function testTheTestSeesTheWholeSpecification(): void
34+
{
35+
$this->assertNotEmpty($this->openApiClasses(), 'No OpenAPI class was found under src/*/src/OpenAPI.php.');
36+
$this->assertNotEmpty($this->operations(), 'No OpenAPI operation was found in the OpenAPI classes.');
37+
}
38+
39+
public function testEveryOperationHasAnOperationId(): void
40+
{
41+
$missing = [];
42+
foreach ($this->operations() as $operation) {
43+
if (! $this->isSet($operation['operationId'])) {
44+
$missing[] = $operation['label'];
45+
}
46+
}
47+
48+
$this->assertSame(
49+
[],
50+
$missing,
51+
sprintf("These operations have no operationId:\n%s", implode("\n", $missing)),
52+
);
53+
}
54+
55+
public function testOperationIdsAreUniqueAcrossTheProject(): void
56+
{
57+
$labelsById = [];
58+
foreach ($this->operations() as $operation) {
59+
if ($this->isSet($operation['operationId'])) {
60+
$labelsById[$operation['operationId']][] = $operation['label'];
61+
}
62+
}
63+
64+
$duplicates = [];
65+
foreach (array_filter($labelsById, static fn (array $labels): bool => count($labels) > 1) as $id => $labels) {
66+
$duplicates[] = sprintf('%s is used by %s', $id, implode(', ', $labels));
67+
}
68+
69+
$this->assertSame(
70+
[],
71+
$duplicates,
72+
sprintf("These operationIds are not unique:\n%s", implode("\n", $duplicates)),
73+
);
74+
}
75+
76+
/**
77+
* @phpstan-assert-if-true non-empty-string $operationId
78+
*/
79+
private function isSet(mixed $operationId): bool
80+
{
81+
return is_string($operationId) && $operationId !== '' && ! Undefined::isDefault($operationId);
82+
}
83+
84+
/**
85+
* Finds the module OpenAPI classes: src/<Module>/src/OpenAPI.php is Api\<Module>\OpenAPI.
86+
*
87+
* @return list<class-string>
88+
*/
89+
private function openApiClasses(): array
90+
{
91+
$classes = [];
92+
foreach (glob(dirname(__DIR__, 4) . '/src/*/src/OpenAPI.php') ?: [] as $file) {
93+
$class = sprintf('Api\\%s\\OpenAPI', basename(dirname($file, 2)));
94+
$this->assertTrue(class_exists($class), sprintf('%s was found at %s but cannot be loaded.', $class, $file));
95+
$classes[] = $class;
96+
}
97+
98+
return $classes;
99+
}
100+
101+
/**
102+
* @return list<array{label: string, operationId: mixed}>
103+
*/
104+
private function operations(): array
105+
{
106+
$operations = [];
107+
foreach ($this->openApiClasses() as $class) {
108+
$attributes = (new ReflectionClass($class))
109+
->getAttributes(Operation::class, ReflectionAttribute::IS_INSTANCEOF);
110+
111+
foreach ($attributes as $attribute) {
112+
/** @var Operation $operation */
113+
$operation = $attribute->newInstance();
114+
115+
$operations[] = [
116+
'label' => strtoupper((string) $operation->method) . ' ' . $operation->path,
117+
'operationId' => $operation->operationId,
118+
];
119+
}
120+
}
121+
122+
return $operations;
123+
}
124+
}

0 commit comments

Comments
 (0)