Skip to content

Commit 8b5359c

Browse files
committed
add openapi test
1 parent 0e63639 commit 8b5359c

File tree

4 files changed

+75
-67
lines changed

4 files changed

+75
-67
lines changed

src/OpenApi/Collections/OpenApiCollection.php

Lines changed: 20 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace Astral\Serialize\OpenApi\Collections;
44

5-
use Astral\Serialize\Enums\TypeKindEnum;
65
use Astral\Serialize\OpenApi\Annotations\Headers;
76
use Astral\Serialize\OpenApi\Annotations\RequestBody;
87
use Astral\Serialize\OpenApi\Annotations\Response;
@@ -52,7 +51,6 @@ public function build() : Method
5251

5352
$openAPIMethod->withRequestBody($this->requestBody !== null ? $this->buildRequestBodyByAttribute() : $this->buildRequestBodyByParameters());
5453
$openAPIMethod->addResponse(200, $this->buildResponse());
55-
5654
return $openAPIMethod;
5755
}
5856

@@ -62,7 +60,7 @@ public function build() : Method
6260
public function buildRequestBodyByAttribute(): RequestBodyStorage
6361
{
6462
$openAPIRequestBody = new RequestBodyStorage($this->requestBody->contentType);
65-
$schemaStorage = (new SchemaStorage())->build($this->buildRequestBodyParameterCollections($this->requestBody->className,$this->requestBody->group),$n);
63+
$schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($this->requestBody->className,$this->requestBody->group),$n);
6664
$openAPIRequestBody->withParameter($schemaStorage);
6765
return $openAPIRequestBody;
6866
}
@@ -77,7 +75,7 @@ public function buildRequestBodyByParameters(): RequestBodyStorage
7775
$type = $methodParam?->getType();
7876
$requestBodyClass = $type instanceof ReflectionNamedType ? $type->getName() : '';
7977
if (is_subclass_of($requestBodyClass, Serialize::class)) {
80-
$schemaStorage = (new SchemaStorage())->build($this->buildRequestBodyParameterCollections($requestBodyClass),$node);
78+
$schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($requestBodyClass),$node);
8179
$openAPIRequestBody->withParameter($schemaStorage);
8280
}
8381

@@ -89,9 +87,23 @@ public function buildRequestBodyByParameters(): RequestBodyStorage
8987
*/
9088
public function buildResponse(): ResponseStorage
9189
{
90+
$returnClass = $this->reflectionMethod->getReturnType();
91+
$returnClass = $returnClass instanceof ReflectionNamedType ? $returnClass->getName() : null;
92+
$responseClass = match(true){
93+
$this->response !== null => $this->response->className,
94+
$returnClass && is_subclass_of($returnClass,Serialize::class) => $returnClass,
95+
default => null,
96+
};
97+
9298
$responseStorage = new ResponseStorage();
93-
$schemaStorage = (new SchemaStorage())->build($this->buildResponseParameterCollections());
94-
$responseStorage->withParameter($schemaStorage);
99+
100+
101+
if($responseClass) {
102+
$groups = $this->response && is_array($this->response->groups) ? $this->response->groups : ['default'];
103+
$schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($responseClass, $groups));
104+
$responseStorage->withParameter($schemaStorage);
105+
}
106+
95107
return $responseStorage;
96108
}
97109

@@ -101,13 +113,12 @@ public function buildResponse(): ResponseStorage
101113
* @return array<ParameterCollection>
102114
* @throws InvalidArgumentException
103115
*/
104-
public function buildRequestBodyParameterCollections(string $className, array $groups = ['default']): array
116+
public function buildParameterCollections(string $className, array $groups = ['default']): array
105117
{
106118
$serializeContext = ContextFactory::build($className);
107119
$serializeContext->from();
108120
$properties = $serializeContext->getGroupCollection()->getProperties();
109121

110-
111122
$vols = [];
112123
foreach ($properties as $property){
113124
$vol = new ParameterCollection(
@@ -123,53 +134,7 @@ className: $className,
123134
if($property->getChildren()){
124135
foreach ($property->getChildren() as $children){
125136
$className = $children->getClassName();
126-
$vol->children[$className] = $this->buildRequestBodyParameterCollections($className);
127-
}
128-
}
129-
130-
$vols[] = $vol;
131-
}
132-
133-
return $vols;
134-
}
135-
136-
/**
137-
* @return array<ParameterCollection>
138-
* @throws InvalidArgumentException
139-
*/
140-
public function buildResponseParameterCollections(): array
141-
{
142-
$returnClass = $this->reflectionMethod->getReturnType();
143-
$responseClass = match(true){
144-
$this->response !== null => $this->response->className,
145-
$returnClass && is_subclass_of($returnClass,Serialize::class) => $returnClass,
146-
default => null,
147-
};
148-
149-
if(!$responseClass){
150-
return [];
151-
}
152-
153-
$groups = $this->response && is_array($this->response->groups) ? $this->response->groups : ['default'];
154-
$serializeContext = ContextFactory::build($responseClass);
155-
$serializeContext->from();
156-
$properties = $serializeContext->getGroupCollection()->getProperties();
157-
158-
$vols = [];
159-
foreach ($properties as $property){
160-
$vol = new ParameterCollection(
161-
className: $responseClass,
162-
name: current($property->getOutNamesByGroups($groups,$responseClass)),
163-
types: $property->getTypes(),
164-
type: ParameterTypeEnum::getByTypes($property->getTypes()),
165-
descriptions: '',
166-
required: !$property->isNullable(),
167-
ignore: false,
168-
);
169-
170-
if($property->getChildren()){
171-
foreach ($property->getChildren() as $children){
172-
$vol->children[] = $this->buildRequestBodyParameterCollections($children->getClassName());
137+
$vol->children[$className] = $this->buildParameterCollections($className);
173138
}
174139
}
175140

src/OpenApi/Handler/Handler.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,6 @@ protected function scanFolderRecursively(string $folder, string $namespace): voi
137137
}
138138
}
139139

140-
141-
142140
public function output(string $path): bool
143141
{
144142
return true;

src/OpenApi/Storage/OpenAPI/ResponseStorage.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
class ResponseStorage implements StorageInterface
1010
{
11-
public array $parameter;
11+
public array $parameter = [];
1212

1313
public function __construct(
1414
public string $contentType = 'application/json',

tests/Openapi/OpenApi.php

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use Astral\Serialize\Serialize;
44

5-
beforeAll(function () {
5+
beforeAll(static function () {
66

77
class OtherOpenApiArrayNestedOne
88
{
@@ -31,7 +31,7 @@ class TestOpenApiRequest extends Serialize
3131

3232
class TestOpenApiResponse extends Serialize
3333
{
34-
public string $name;
34+
public ?string $name;
3535
public int $id;
3636
}
3737

@@ -40,18 +40,63 @@ class TestOpenApiController{
4040

4141
#[\Astral\Serialize\OpenApi\Annotations\Summary('测试方法一')]
4242
#[\Astral\Serialize\OpenApi\Annotations\Route('/test/one-action')]
43-
public function one(TestOpenApiRequest $request)
43+
public function one(TestOpenApiRequest $request): TestOpenApiResponse
4444
{
4545
return new TestOpenApiResponse();
4646
}
47+
4748
}
4849

4950
});
5051

51-
//
52-
it('test openapi build by class', function () {
53-
$api = new \Astral\Serialize\OpenApi\OpenApi();
52+
test('OpenAPI structure is correct', function () {
53+
54+
$api = new \Astral\Serialize\OpenApi\OpenApi();
5455
$api->buildByClass(TestOpenApiController::class);
55-
$res = $api->toString();
56-
var_dump($res);
57-
});
56+
57+
$openApi = $api::$OpenAPI;
58+
59+
// 顶层结构断言
60+
expect($openApi->openapi)->toBe('3.1.1')
61+
->and($openApi->info->title)->toBe('API 接口')
62+
->and($openApi->info->version)->toBe('1.0.0')
63+
->and($openApi->tags[0]->name)->toBe('接口测试');
64+
65+
// 路径是否存在
66+
$paths = $openApi->paths;
67+
expect($paths)->toHaveKey('/test/one-action');
68+
69+
// 方法与标签断言
70+
$post = $paths['/test/one-action']['post'];
71+
expect($post->summary)->toBe('测试方法一')
72+
->and($post->tags)->toContain('接口测试');
73+
74+
// 请求体断言
75+
$requestBody = $post->requestBody;
76+
expect($requestBody['required'])->toBeTrue();
77+
$schema = $requestBody['content']['application/json']['schema'];
78+
79+
// 请求字段存在
80+
expect($schema['properties'])->toHaveKeys(['name', 'id', 'any_array']);
81+
82+
// id 字段是 oneOf 并包含 string, integer, number
83+
$idOneOf = $schema['properties']['id']['oneOf'];
84+
$types = array_map(static fn($item) => $item['type']->value, $idOneOf);
85+
expect($types)->toMatchArray(['string', 'integer', 'number']);
86+
87+
// any_array 是 oneOf 并包含至少一个 array 类型
88+
$anyArray = $schema['properties']['any_array'];
89+
expect($anyArray['type'])->toBe('oneOf')
90+
->and($anyArray['oneOf'])->toBeArray()
91+
->and($anyArray['oneOf'][0]['type'])->toBe('array');
92+
93+
// 响应体 200 是否定义成功
94+
$response200 = $post->responses[200];
95+
expect($response200['description'])->toBe('成功');
96+
97+
$schema = $response200['content']['application/json']['schema'];
98+
expect($schema['properties'])->toHaveKeys(['name', 'id'])
99+
->and($schema['required'])->toHaveCount(1)
100+
->and($schema['required'][0])->toBeString('id');
101+
102+
});

0 commit comments

Comments
 (0)