@geekmidas/schema 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +351 -0
  2. package/dist/conversion-CH_EmflL.d.cts +30 -0
  3. package/dist/conversion-Civd6bIU.mjs +89 -0
  4. package/dist/conversion-Civd6bIU.mjs.map +1 -0
  5. package/dist/conversion-CrSNoSRa.cjs +125 -0
  6. package/dist/conversion-CrSNoSRa.cjs.map +1 -0
  7. package/dist/conversion-DUyZYTWO.d.mts +30 -0
  8. package/dist/conversion.cjs +8 -0
  9. package/dist/conversion.d.cts +2 -0
  10. package/dist/conversion.d.mts +2 -0
  11. package/dist/conversion.mjs +3 -0
  12. package/dist/index.cjs +7 -0
  13. package/dist/index.d.cts +4 -0
  14. package/dist/index.d.mts +4 -0
  15. package/dist/index.mjs +4 -0
  16. package/dist/openapi-DH5yCqKh.mjs +46 -0
  17. package/dist/openapi-DH5yCqKh.mjs.map +1 -0
  18. package/dist/openapi-DR4_PG-e.d.cts +26 -0
  19. package/dist/openapi-DVvLYx-8.cjs +58 -0
  20. package/dist/openapi-DVvLYx-8.cjs.map +1 -0
  21. package/dist/openapi-j01kFjpI.d.mts +26 -0
  22. package/dist/openapi.cjs +4 -0
  23. package/dist/openapi.d.cts +2 -0
  24. package/dist/openapi.d.mts +2 -0
  25. package/dist/openapi.mjs +3 -0
  26. package/dist/parser.cjs +23 -0
  27. package/dist/parser.cjs.map +1 -0
  28. package/dist/parser.d.cts +17 -0
  29. package/dist/parser.d.mts +17 -0
  30. package/dist/parser.mjs +21 -0
  31. package/dist/parser.mjs.map +1 -0
  32. package/dist/types-ByLHeRGs.d.mts +13 -0
  33. package/dist/types-DfcgE7cO.d.cts +13 -0
  34. package/dist/types.cjs +0 -0
  35. package/dist/types.d.cts +2 -0
  36. package/dist/types.d.mts +2 -0
  37. package/dist/types.mjs +0 -0
  38. package/package.json +44 -0
  39. package/src/__tests__/conversion.spec.ts +319 -0
  40. package/src/__tests__/openapi.spec.ts +396 -0
  41. package/src/__tests__/parser.spec.ts +236 -0
  42. package/src/conversion.ts +199 -0
  43. package/src/index.ts +15 -0
  44. package/src/openapi.ts +75 -0
  45. package/src/parser.ts +30 -0
  46. package/src/types.ts +23 -0
  47. package/tsdown.config.ts +5 -0
@@ -0,0 +1,319 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { z } from 'zod/v4';
3
+ import {
4
+ SchemaVendor,
5
+ convertSchemaWithComponents,
6
+ convertStandardSchemaToJsonSchema,
7
+ getSchemaMetadata,
8
+ getZodMetadata,
9
+ } from '../conversion';
10
+ import { createComponentCollector } from '../openapi';
11
+
12
+ describe('Schema Conversion', () => {
13
+ describe('convertStandardSchemaToJsonSchema', () => {
14
+ it('should convert Zod object schema to JSON Schema', async () => {
15
+ const schema = z.object({
16
+ name: z.string(),
17
+ age: z.number(),
18
+ });
19
+
20
+ const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
21
+
22
+ expect(jsonSchema).toHaveProperty('type', 'object');
23
+ expect(jsonSchema).toHaveProperty('properties');
24
+ expect(jsonSchema.properties).toHaveProperty('name');
25
+ expect(jsonSchema.properties).toHaveProperty('age');
26
+ });
27
+
28
+ it('should return undefined for undefined schema', async () => {
29
+ const jsonSchema = await convertStandardSchemaToJsonSchema(undefined);
30
+
31
+ expect(jsonSchema).toBeUndefined();
32
+ });
33
+
34
+ it('should convert Zod string schema', async () => {
35
+ const schema = z.string();
36
+
37
+ const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
38
+
39
+ expect(jsonSchema).toHaveProperty('type', 'string');
40
+ });
41
+
42
+ it('should convert Zod number schema', async () => {
43
+ const schema = z.number();
44
+
45
+ const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
46
+
47
+ expect(jsonSchema).toHaveProperty('type', 'number');
48
+ });
49
+
50
+ it('should convert Zod array schema', async () => {
51
+ const schema = z.array(z.string());
52
+
53
+ const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
54
+
55
+ expect(jsonSchema).toHaveProperty('type', 'array');
56
+ expect(jsonSchema).toHaveProperty('items');
57
+ });
58
+
59
+ it('should convert Zod enum schema', async () => {
60
+ const schema = z.enum(['active', 'inactive', 'pending']);
61
+
62
+ const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
63
+
64
+ expect(jsonSchema).toHaveProperty('enum');
65
+ expect(jsonSchema.enum).toEqual(['active', 'inactive', 'pending']);
66
+ });
67
+
68
+ it('should convert nested object schema', async () => {
69
+ const schema = z.object({
70
+ user: z.object({
71
+ name: z.string(),
72
+ age: z.number(),
73
+ }),
74
+ });
75
+
76
+ const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
77
+
78
+ expect(jsonSchema.type).toBe('object');
79
+ expect(jsonSchema.properties).toHaveProperty('user');
80
+ expect(jsonSchema.properties.user).toHaveProperty('type', 'object');
81
+ });
82
+
83
+ it('should handle optional fields', async () => {
84
+ const schema = z.object({
85
+ required: z.string(),
86
+ optional: z.string().optional(),
87
+ });
88
+
89
+ const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
90
+
91
+ expect(jsonSchema.required).toContain('required');
92
+ expect(jsonSchema.required).not.toContain('optional');
93
+ });
94
+
95
+ it('should extract and convert $defs with component collector', async () => {
96
+ const userSchema = z.object({
97
+ name: z.string(),
98
+ age: z.number(),
99
+ });
100
+
101
+ const schema = z.object({
102
+ user: userSchema,
103
+ });
104
+
105
+ const collector = createComponentCollector();
106
+ const jsonSchema = await convertStandardSchemaToJsonSchema(
107
+ schema,
108
+ collector,
109
+ );
110
+
111
+ expect(jsonSchema).toBeDefined();
112
+ // $defs should be removed from main schema
113
+ expect(jsonSchema).not.toHaveProperty('$defs');
114
+ });
115
+
116
+ it('should throw error for unsupported vendor', async () => {
117
+ const invalidSchema = {
118
+ '~standard': {
119
+ vendor: 'unsupported' as any,
120
+ validate: vi.fn(),
121
+ },
122
+ };
123
+
124
+ await expect(
125
+ convertStandardSchemaToJsonSchema(invalidSchema as any),
126
+ ).rejects.toThrow(/Unsupported or missing vendor/);
127
+ });
128
+
129
+ it('should throw error for missing vendor', async () => {
130
+ const invalidSchema = {
131
+ '~standard': {
132
+ vendor: undefined,
133
+ validate: vi.fn(),
134
+ },
135
+ };
136
+
137
+ await expect(
138
+ convertStandardSchemaToJsonSchema(invalidSchema as any),
139
+ ).rejects.toThrow(/Unsupported or missing vendor/);
140
+ });
141
+
142
+ it('should handle schema with descriptions', async () => {
143
+ const schema = z
144
+ .object({
145
+ name: z.string(),
146
+ })
147
+ .describe('User information');
148
+
149
+ const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
150
+
151
+ expect(jsonSchema.description).toBe('User information');
152
+ });
153
+
154
+ it('should handle union types', async () => {
155
+ const schema = z.union([z.string(), z.number()]);
156
+
157
+ const jsonSchema = await convertStandardSchemaToJsonSchema(schema);
158
+
159
+ expect(jsonSchema).toHaveProperty('anyOf');
160
+ });
161
+ });
162
+
163
+ describe('getZodMetadata', () => {
164
+ it('should return undefined for non-Zod objects', async () => {
165
+ const schema = z.string();
166
+
167
+ const metadata = await getZodMetadata(schema);
168
+
169
+ expect(metadata).toBeUndefined();
170
+ });
171
+
172
+ it('should get metadata from Zod object with meta', async () => {
173
+ const schema = z.object({ name: z.string() }).meta({ id: 'User' });
174
+
175
+ const metadata = await getZodMetadata(schema);
176
+
177
+ expect(metadata).toEqual({ id: 'User' });
178
+ });
179
+
180
+ it('should return undefined for Zod object without meta', async () => {
181
+ const schema = z.object({ name: z.string() });
182
+
183
+ const metadata = await getZodMetadata(schema);
184
+
185
+ // Returns undefined when no meta is set
186
+ expect(metadata).toBeUndefined();
187
+ });
188
+ });
189
+
190
+ describe('getSchemaMetadata', () => {
191
+ it('should get metadata for Zod schema', async () => {
192
+ const schema = z.object({ name: z.string() }).meta({ id: 'UserMeta' });
193
+
194
+ const metadata = await getSchemaMetadata(schema);
195
+
196
+ expect(metadata).toEqual({ id: 'UserMeta' });
197
+ });
198
+
199
+ it('should return undefined for non-Zod vendor', async () => {
200
+ const schema = {
201
+ '~standard': {
202
+ vendor: 'valibot',
203
+ validate: vi.fn(),
204
+ },
205
+ };
206
+
207
+ const metadata = await getSchemaMetadata(schema as any);
208
+
209
+ expect(metadata).toBeUndefined();
210
+ });
211
+
212
+ it('should return undefined for schema without vendor', async () => {
213
+ const schema = {
214
+ '~standard': {
215
+ vendor: undefined,
216
+ validate: vi.fn(),
217
+ },
218
+ };
219
+
220
+ const metadata = await getSchemaMetadata(schema as any);
221
+
222
+ expect(metadata).toBeUndefined();
223
+ });
224
+ });
225
+
226
+ describe('convertSchemaWithComponents', () => {
227
+ it('should convert schema without component collector', async () => {
228
+ const schema = z.object({
229
+ name: z.string(),
230
+ age: z.number(),
231
+ });
232
+
233
+ const jsonSchema = await convertSchemaWithComponents(schema);
234
+
235
+ expect(jsonSchema).toHaveProperty('type', 'object');
236
+ expect(jsonSchema).toHaveProperty('properties');
237
+ });
238
+
239
+ it('should return undefined for undefined schema', async () => {
240
+ const jsonSchema = await convertSchemaWithComponents(undefined);
241
+
242
+ expect(jsonSchema).toBeUndefined();
243
+ });
244
+
245
+ it('should add schema with ID to component collector', async () => {
246
+ const schema = z.object({ name: z.string() }).meta({ id: 'UserComp' });
247
+
248
+ const collector = createComponentCollector();
249
+ const result = await convertSchemaWithComponents(schema, collector);
250
+
251
+ expect(result).toEqual({ $ref: '#/components/schemas/UserComp' });
252
+ expect(collector.schemas).toHaveProperty('UserComp');
253
+ expect(collector.schemas.UserComp).not.toHaveProperty('id');
254
+ });
255
+
256
+ it('should not add schema without ID to component collector', async () => {
257
+ const schema = z.object({ name: z.string() });
258
+
259
+ const collector = createComponentCollector();
260
+ const result = await convertSchemaWithComponents(schema, collector);
261
+
262
+ expect(result).not.toHaveProperty('$ref');
263
+ expect(Object.keys(collector.schemas)).toHaveLength(0);
264
+ });
265
+
266
+ it('should handle schema with id in JSON Schema', async () => {
267
+ const schema = z.object({ name: z.string() }).meta({ id: 'Person' });
268
+
269
+ const collector = createComponentCollector();
270
+ const result = await convertSchemaWithComponents(schema, collector);
271
+
272
+ expect(result).toEqual({ $ref: '#/components/schemas/Person' });
273
+ expect(collector.schemas).toHaveProperty('Person');
274
+ });
275
+
276
+ it('should convert multiple schemas with collector', async () => {
277
+ const schema1 = z.object({ name: z.string() }).meta({ id: 'UserMulti' });
278
+ const schema2 = z.object({ title: z.string() }).meta({ id: 'PostMulti' });
279
+
280
+ const collector = createComponentCollector();
281
+
282
+ await convertSchemaWithComponents(schema1, collector);
283
+ await convertSchemaWithComponents(schema2, collector);
284
+
285
+ expect(Object.keys(collector.schemas)).toHaveLength(2);
286
+ expect(collector.schemas).toHaveProperty('UserMulti');
287
+ expect(collector.schemas).toHaveProperty('PostMulti');
288
+ });
289
+
290
+ it('should handle nested objects with collector', async () => {
291
+ const schema = z
292
+ .object({
293
+ user: z.object({
294
+ name: z.string(),
295
+ profile: z.object({
296
+ bio: z.string(),
297
+ }),
298
+ }),
299
+ })
300
+ .meta({ id: 'UserData' });
301
+
302
+ const collector = createComponentCollector();
303
+ const result = await convertSchemaWithComponents(schema, collector);
304
+
305
+ expect(result).toEqual({ $ref: '#/components/schemas/UserData' });
306
+ expect(collector.schemas).toHaveProperty('UserData');
307
+ });
308
+ });
309
+
310
+ describe('SchemaVendor enum', () => {
311
+ it('should have zod vendor', () => {
312
+ expect(SchemaVendor.zod).toBe('zod');
313
+ });
314
+
315
+ it('should have valibot vendor', () => {
316
+ expect(SchemaVendor.valibot).toBe('valibot');
317
+ });
318
+ });
319
+ });
@@ -0,0 +1,396 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ type ComponentCollector,
4
+ buildOpenApiSchema,
5
+ createComponentCollector,
6
+ } from '../openapi';
7
+
8
+ describe('OpenAPI Schema', () => {
9
+ describe('createComponentCollector', () => {
10
+ it('should create a component collector', () => {
11
+ const collector = createComponentCollector();
12
+
13
+ expect(collector).toHaveProperty('schemas');
14
+ expect(collector).toHaveProperty('addSchema');
15
+ expect(collector).toHaveProperty('getReference');
16
+ expect(collector.schemas).toEqual({});
17
+ });
18
+
19
+ it('should add schema to collector', () => {
20
+ const collector = createComponentCollector();
21
+
22
+ collector.addSchema('User', {
23
+ type: 'object',
24
+ properties: {
25
+ name: { type: 'string' },
26
+ },
27
+ });
28
+
29
+ expect(collector.schemas).toHaveProperty('User');
30
+ expect(collector.schemas.User).toEqual({
31
+ type: 'object',
32
+ properties: {
33
+ name: { type: 'string' },
34
+ },
35
+ });
36
+ });
37
+
38
+ it('should get reference to schema', () => {
39
+ const collector = createComponentCollector();
40
+
41
+ const reference = collector.getReference('User');
42
+
43
+ expect(reference).toEqual({ $ref: '#/components/schemas/User' });
44
+ });
45
+
46
+ it('should add multiple schemas', () => {
47
+ const collector = createComponentCollector();
48
+
49
+ collector.addSchema('User', {
50
+ type: 'object',
51
+ properties: { name: { type: 'string' } },
52
+ });
53
+
54
+ collector.addSchema('Post', {
55
+ type: 'object',
56
+ properties: { title: { type: 'string' } },
57
+ });
58
+
59
+ expect(Object.keys(collector.schemas)).toHaveLength(2);
60
+ expect(collector.schemas).toHaveProperty('User');
61
+ expect(collector.schemas).toHaveProperty('Post');
62
+ });
63
+
64
+ it('should overwrite existing schema with same name', () => {
65
+ const collector = createComponentCollector();
66
+
67
+ collector.addSchema('User', {
68
+ type: 'object',
69
+ properties: { name: { type: 'string' } },
70
+ });
71
+
72
+ collector.addSchema('User', {
73
+ type: 'object',
74
+ properties: { email: { type: 'string' } },
75
+ });
76
+
77
+ expect(collector.schemas.User.properties).toHaveProperty('email');
78
+ expect(collector.schemas.User.properties).not.toHaveProperty('name');
79
+ });
80
+
81
+ it('should handle complex nested schemas', () => {
82
+ const collector = createComponentCollector();
83
+
84
+ collector.addSchema('User', {
85
+ type: 'object',
86
+ properties: {
87
+ name: { type: 'string' },
88
+ address: {
89
+ type: 'object',
90
+ properties: {
91
+ street: { type: 'string' },
92
+ city: { type: 'string' },
93
+ },
94
+ },
95
+ },
96
+ });
97
+
98
+ expect(collector.schemas.User.properties?.address).toBeDefined();
99
+ });
100
+ });
101
+
102
+ describe('buildOpenApiSchema', () => {
103
+ it('should build OpenAPI schema with default options', async () => {
104
+ const mockEndpoint = {
105
+ async toOpenApi3Route(collector?: ComponentCollector) {
106
+ return {
107
+ '/users': {
108
+ get: {
109
+ operationId: 'getUsers',
110
+ responses: {
111
+ '200': {
112
+ description: 'Success',
113
+ },
114
+ },
115
+ },
116
+ },
117
+ };
118
+ },
119
+ };
120
+
121
+ const doc = await buildOpenApiSchema([mockEndpoint]);
122
+
123
+ expect(doc).toHaveProperty('openapi', '3.0.0');
124
+ expect(doc).toHaveProperty('info');
125
+ expect(doc.info).toEqual({
126
+ title: 'API',
127
+ version: '1.0.0',
128
+ });
129
+ expect(doc).toHaveProperty('paths');
130
+ expect(doc.paths).toHaveProperty('/users');
131
+ });
132
+
133
+ it('should build OpenAPI schema with custom options', async () => {
134
+ const mockEndpoint = {
135
+ async toOpenApi3Route(collector?: ComponentCollector) {
136
+ return {
137
+ '/posts': {
138
+ get: {
139
+ operationId: 'getPosts',
140
+ responses: {
141
+ '200': {
142
+ description: 'Success',
143
+ },
144
+ },
145
+ },
146
+ },
147
+ };
148
+ },
149
+ };
150
+
151
+ const doc = await buildOpenApiSchema([mockEndpoint], {
152
+ title: 'My API',
153
+ version: '2.0.0',
154
+ description: 'API Description',
155
+ });
156
+
157
+ expect(doc.info).toEqual({
158
+ title: 'My API',
159
+ version: '2.0.0',
160
+ description: 'API Description',
161
+ });
162
+ });
163
+
164
+ it('should merge multiple endpoints into paths', async () => {
165
+ const endpoint1 = {
166
+ async toOpenApi3Route(collector?: ComponentCollector) {
167
+ return {
168
+ '/users': {
169
+ get: {
170
+ operationId: 'getUsers',
171
+ responses: { '200': { description: 'Success' } },
172
+ },
173
+ },
174
+ };
175
+ },
176
+ };
177
+
178
+ const endpoint2 = {
179
+ async toOpenApi3Route(collector?: ComponentCollector) {
180
+ return {
181
+ '/posts': {
182
+ get: {
183
+ operationId: 'getPosts',
184
+ responses: { '200': { description: 'Success' } },
185
+ },
186
+ },
187
+ };
188
+ },
189
+ };
190
+
191
+ const doc = await buildOpenApiSchema([endpoint1, endpoint2]);
192
+
193
+ expect(Object.keys(doc.paths)).toHaveLength(2);
194
+ expect(doc.paths).toHaveProperty('/users');
195
+ expect(doc.paths).toHaveProperty('/posts');
196
+ });
197
+
198
+ it('should merge multiple methods for same path', async () => {
199
+ const endpoint1 = {
200
+ async toOpenApi3Route(collector?: ComponentCollector) {
201
+ return {
202
+ '/users': {
203
+ get: {
204
+ operationId: 'getUsers',
205
+ responses: { '200': { description: 'Success' } },
206
+ },
207
+ },
208
+ };
209
+ },
210
+ };
211
+
212
+ const endpoint2 = {
213
+ async toOpenApi3Route(collector?: ComponentCollector) {
214
+ return {
215
+ '/users': {
216
+ post: {
217
+ operationId: 'createUser',
218
+ responses: { '201': { description: 'Created' } },
219
+ },
220
+ },
221
+ };
222
+ },
223
+ };
224
+
225
+ const doc = await buildOpenApiSchema([endpoint1, endpoint2]);
226
+
227
+ expect(doc.paths['/users']).toHaveProperty('get');
228
+ expect(doc.paths['/users']).toHaveProperty('post');
229
+ });
230
+
231
+ it('should add components when schemas are collected', async () => {
232
+ const mockEndpoint = {
233
+ async toOpenApi3Route(collector?: ComponentCollector) {
234
+ if (collector) {
235
+ collector.addSchema('User', {
236
+ type: 'object',
237
+ properties: {
238
+ name: { type: 'string' },
239
+ },
240
+ });
241
+ }
242
+
243
+ return {
244
+ '/users': {
245
+ get: {
246
+ operationId: 'getUsers',
247
+ responses: {
248
+ '200': {
249
+ description: 'Success',
250
+ content: {
251
+ 'application/json': {
252
+ schema: collector?.getReference('User'),
253
+ },
254
+ },
255
+ },
256
+ },
257
+ },
258
+ },
259
+ };
260
+ },
261
+ };
262
+
263
+ const doc = await buildOpenApiSchema([mockEndpoint]);
264
+
265
+ expect(doc).toHaveProperty('components');
266
+ expect(doc.components).toHaveProperty('schemas');
267
+ expect(doc.components?.schemas).toHaveProperty('User');
268
+ });
269
+
270
+ it('should not add components when no schemas collected', async () => {
271
+ const mockEndpoint = {
272
+ async toOpenApi3Route(collector?: ComponentCollector) {
273
+ return {
274
+ '/health': {
275
+ get: {
276
+ operationId: 'healthCheck',
277
+ responses: {
278
+ '200': {
279
+ description: 'Healthy',
280
+ },
281
+ },
282
+ },
283
+ },
284
+ };
285
+ },
286
+ };
287
+
288
+ const doc = await buildOpenApiSchema([mockEndpoint]);
289
+
290
+ expect(doc).not.toHaveProperty('components');
291
+ });
292
+
293
+ it('should handle empty endpoints array', async () => {
294
+ const doc = await buildOpenApiSchema([]);
295
+
296
+ expect(doc.paths).toEqual({});
297
+ expect(doc).not.toHaveProperty('components');
298
+ });
299
+
300
+ it('should handle endpoints with parameters', async () => {
301
+ const mockEndpoint = {
302
+ async toOpenApi3Route(collector?: ComponentCollector) {
303
+ return {
304
+ '/users/{id}': {
305
+ get: {
306
+ operationId: 'getUserById',
307
+ parameters: [
308
+ {
309
+ name: 'id',
310
+ in: 'path',
311
+ required: true,
312
+ schema: { type: 'string' },
313
+ },
314
+ ],
315
+ responses: {
316
+ '200': {
317
+ description: 'Success',
318
+ },
319
+ },
320
+ },
321
+ },
322
+ };
323
+ },
324
+ };
325
+
326
+ const doc = await buildOpenApiSchema([mockEndpoint]);
327
+
328
+ expect(doc.paths['/users/{id}']?.get?.parameters).toBeDefined();
329
+ expect(doc.paths['/users/{id}']?.get?.parameters).toHaveLength(1);
330
+ });
331
+
332
+ it('should handle endpoints with request body', async () => {
333
+ const mockEndpoint = {
334
+ async toOpenApi3Route(collector?: ComponentCollector) {
335
+ return {
336
+ '/users': {
337
+ post: {
338
+ operationId: 'createUser',
339
+ requestBody: {
340
+ required: true,
341
+ content: {
342
+ 'application/json': {
343
+ schema: {
344
+ type: 'object',
345
+ properties: {
346
+ name: { type: 'string' },
347
+ },
348
+ },
349
+ },
350
+ },
351
+ },
352
+ responses: {
353
+ '201': {
354
+ description: 'Created',
355
+ },
356
+ },
357
+ },
358
+ },
359
+ };
360
+ },
361
+ };
362
+
363
+ const doc = await buildOpenApiSchema([mockEndpoint]);
364
+
365
+ expect(doc.paths['/users']?.post?.requestBody).toBeDefined();
366
+ });
367
+
368
+ it('should handle endpoints with multiple response codes', async () => {
369
+ const mockEndpoint = {
370
+ async toOpenApi3Route(collector?: ComponentCollector) {
371
+ return {
372
+ '/users': {
373
+ get: {
374
+ operationId: 'getUsers',
375
+ responses: {
376
+ '200': { description: 'Success' },
377
+ '400': { description: 'Bad Request' },
378
+ '401': { description: 'Unauthorized' },
379
+ '500': { description: 'Server Error' },
380
+ },
381
+ },
382
+ },
383
+ };
384
+ },
385
+ };
386
+
387
+ const doc = await buildOpenApiSchema([mockEndpoint]);
388
+
389
+ const responses = doc.paths['/users']?.get?.responses;
390
+ expect(responses).toHaveProperty('200');
391
+ expect(responses).toHaveProperty('400');
392
+ expect(responses).toHaveProperty('401');
393
+ expect(responses).toHaveProperty('500');
394
+ });
395
+ });
396
+ });