@pikku/openapi-parser 0.12.20 → 0.12.22
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.
- package/CHANGELOG.md +37 -0
- package/dist/auth-config.d.ts +3 -3
- package/dist/codegen.js +32 -21
- package/package.json +8 -6
- package/src/auth-config.ts +0 -94
- package/src/codegen.test.ts +0 -973
- package/src/codegen.ts +0 -1670
- package/src/index.ts +0 -24
- package/src/naming.test.ts +0 -260
- package/src/naming.ts +0 -369
- package/src/openapi-to-zod-schema.test.ts +0 -664
- package/src/openapi-to-zod-schema.ts +0 -583
- package/src/parse-openapi.test.ts +0 -316
- package/src/parse-openapi.ts +0 -467
- package/src/zod-codegen.ts +0 -9
- package/tsconfig.json +0 -12
- package/tsconfig.tsbuildinfo +0 -1
package/src/codegen.test.ts
DELETED
|
@@ -1,973 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from 'assert'
|
|
2
|
-
import { describe, test } from 'node:test'
|
|
3
|
-
import { generateAddonFromOpenAPI } from './codegen.js'
|
|
4
|
-
import type { ParsedSpec, ParsedOperation } from './parse-openapi.js'
|
|
5
|
-
|
|
6
|
-
function makeVars(
|
|
7
|
-
overrides: Partial<{
|
|
8
|
-
name: string
|
|
9
|
-
camelName: string
|
|
10
|
-
pascalName: string
|
|
11
|
-
screamingName: string
|
|
12
|
-
displayName: string
|
|
13
|
-
description: string
|
|
14
|
-
}> = {}
|
|
15
|
-
) {
|
|
16
|
-
return {
|
|
17
|
-
name: 'test-api',
|
|
18
|
-
camelName: 'testApi',
|
|
19
|
-
pascalName: 'TestApi',
|
|
20
|
-
screamingName: 'TEST_API',
|
|
21
|
-
displayName: 'Test API',
|
|
22
|
-
description: 'Test API integration',
|
|
23
|
-
...overrides,
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function makeSpec(overrides: Partial<ParsedSpec> = {}): ParsedSpec {
|
|
28
|
-
return {
|
|
29
|
-
info: { title: 'Test API', version: '1.0.0' },
|
|
30
|
-
baseUrl: 'https://api.example.com',
|
|
31
|
-
serverUrls: ['https://api.example.com'],
|
|
32
|
-
authType: 'bearer',
|
|
33
|
-
operations: [],
|
|
34
|
-
componentSchemas: {},
|
|
35
|
-
securitySchemes: {},
|
|
36
|
-
tagDescriptions: {},
|
|
37
|
-
...overrides,
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function makeOp(overrides: Partial<ParsedOperation> = {}): ParsedOperation {
|
|
42
|
-
return {
|
|
43
|
-
method: 'get',
|
|
44
|
-
path: '/items',
|
|
45
|
-
tags: [],
|
|
46
|
-
pathParams: [],
|
|
47
|
-
queryParams: [],
|
|
48
|
-
headerParams: [],
|
|
49
|
-
errorResponses: [],
|
|
50
|
-
deprecated: false,
|
|
51
|
-
...overrides,
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
// Topological sort of schema declarations
|
|
57
|
-
// ---------------------------------------------------------------------------
|
|
58
|
-
describe('topological sort of schemas', () => {
|
|
59
|
-
test('dependency schemas are declared before dependents in types file', () => {
|
|
60
|
-
const addressSchema = {
|
|
61
|
-
type: 'object' as const,
|
|
62
|
-
properties: {
|
|
63
|
-
street: { type: 'string' as const },
|
|
64
|
-
},
|
|
65
|
-
}
|
|
66
|
-
const userSchema = {
|
|
67
|
-
type: 'object' as const,
|
|
68
|
-
properties: {
|
|
69
|
-
name: { type: 'string' as const },
|
|
70
|
-
address: { $ref: '#/components/schemas/Address' },
|
|
71
|
-
},
|
|
72
|
-
}
|
|
73
|
-
const spec = makeSpec({
|
|
74
|
-
componentSchemas: {
|
|
75
|
-
// User comes first but depends on Address
|
|
76
|
-
User: userSchema,
|
|
77
|
-
Address: addressSchema,
|
|
78
|
-
},
|
|
79
|
-
// Both schemas must be referenced by 2+ operations to land in the types file
|
|
80
|
-
operations: [
|
|
81
|
-
makeOp({ method: 'get', path: '/users', responseSchema: userSchema }),
|
|
82
|
-
makeOp({ method: 'post', path: '/users', responseSchema: userSchema }),
|
|
83
|
-
],
|
|
84
|
-
})
|
|
85
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
86
|
-
oauth: false,
|
|
87
|
-
secret: false,
|
|
88
|
-
})
|
|
89
|
-
const typesFile = files['src/test-api.types.ts']
|
|
90
|
-
assert.ok(typesFile, 'types file should exist')
|
|
91
|
-
|
|
92
|
-
const addressIdx = typesFile.indexOf('AddressSchema')
|
|
93
|
-
const userIdx = typesFile.indexOf('UserSchema')
|
|
94
|
-
assert.ok(
|
|
95
|
-
addressIdx < userIdx,
|
|
96
|
-
`Address should be declared before User. Address at ${addressIdx}, User at ${userIdx}`
|
|
97
|
-
)
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
// ---------------------------------------------------------------------------
|
|
102
|
-
// z.lazy() for circular schema references
|
|
103
|
-
// ---------------------------------------------------------------------------
|
|
104
|
-
describe('circular schema references', () => {
|
|
105
|
-
test('generates z.lazy() forward declarations for cyclic schemas', () => {
|
|
106
|
-
// Create schemas that reference each other
|
|
107
|
-
const nodeSchema: any = {
|
|
108
|
-
type: 'object',
|
|
109
|
-
properties: {
|
|
110
|
-
name: { type: 'string' },
|
|
111
|
-
},
|
|
112
|
-
}
|
|
113
|
-
// Make it self-referential via $ref (surviving after resolution)
|
|
114
|
-
nodeSchema.properties.children = {
|
|
115
|
-
type: 'array',
|
|
116
|
-
items: { $ref: '#/components/schemas/TreeNode' },
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const spec = makeSpec({
|
|
120
|
-
componentSchemas: {
|
|
121
|
-
TreeNode: nodeSchema,
|
|
122
|
-
},
|
|
123
|
-
// Use TreeNode in 2+ operations so it lands in the shared types file
|
|
124
|
-
operations: [
|
|
125
|
-
makeOp({ method: 'get', path: '/tree', responseSchema: nodeSchema }),
|
|
126
|
-
makeOp({ method: 'post', path: '/tree', responseSchema: nodeSchema }),
|
|
127
|
-
],
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
131
|
-
oauth: false,
|
|
132
|
-
secret: false,
|
|
133
|
-
})
|
|
134
|
-
const typesFile = files['src/test-api.types.ts']
|
|
135
|
-
assert.ok(typesFile, 'types file should exist')
|
|
136
|
-
|
|
137
|
-
// Should contain z.lazy for the circular reference
|
|
138
|
-
// The self-reference should be handled (either z.lazy or direct reference)
|
|
139
|
-
assert.ok(
|
|
140
|
-
typesFile.includes('TreeNodeSchema'),
|
|
141
|
-
'should declare TreeNodeSchema'
|
|
142
|
-
)
|
|
143
|
-
})
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
// ---------------------------------------------------------------------------
|
|
147
|
-
// Duplicate schema variable names → append _2, _3
|
|
148
|
-
// ---------------------------------------------------------------------------
|
|
149
|
-
describe('duplicate schema variable names', () => {
|
|
150
|
-
test('deduplicates schema names that sanitize to the same identifier', () => {
|
|
151
|
-
const userSchema = {
|
|
152
|
-
type: 'object' as const,
|
|
153
|
-
properties: { id: { type: 'string' as const } },
|
|
154
|
-
}
|
|
155
|
-
const userSchema2 = {
|
|
156
|
-
type: 'object' as const,
|
|
157
|
-
properties: { name: { type: 'string' as const } },
|
|
158
|
-
}
|
|
159
|
-
const spec = makeSpec({
|
|
160
|
-
componentSchemas: {
|
|
161
|
-
User: userSchema,
|
|
162
|
-
// A schema whose sanitized name collides with "User"
|
|
163
|
-
user: userSchema2,
|
|
164
|
-
},
|
|
165
|
-
// Both schemas referenced by 2+ operations so they land in the types file
|
|
166
|
-
operations: [
|
|
167
|
-
makeOp({ method: 'get', path: '/a', responseSchema: userSchema }),
|
|
168
|
-
makeOp({ method: 'get', path: '/b', responseSchema: userSchema }),
|
|
169
|
-
makeOp({ method: 'get', path: '/c', responseSchema: userSchema2 }),
|
|
170
|
-
makeOp({ method: 'get', path: '/d', responseSchema: userSchema2 }),
|
|
171
|
-
],
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
175
|
-
oauth: false,
|
|
176
|
-
secret: false,
|
|
177
|
-
})
|
|
178
|
-
const typesFile = files['src/test-api.types.ts']
|
|
179
|
-
assert.ok(typesFile, 'types file should exist')
|
|
180
|
-
|
|
181
|
-
// Both schemas should appear — one might have _2 suffix
|
|
182
|
-
// At minimum there should be no syntax error from duplicate const names
|
|
183
|
-
const schemaMatches = typesFile.match(/export const \w+Schema/g)
|
|
184
|
-
assert.ok(schemaMatches, 'should have schema exports')
|
|
185
|
-
const uniqueNames = new Set(schemaMatches)
|
|
186
|
-
assert.equal(
|
|
187
|
-
uniqueNames.size,
|
|
188
|
-
schemaMatches.length,
|
|
189
|
-
'all schema const names should be unique'
|
|
190
|
-
)
|
|
191
|
-
})
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
// ---------------------------------------------------------------------------
|
|
195
|
-
// Function files import used schema refs from types file
|
|
196
|
-
// ---------------------------------------------------------------------------
|
|
197
|
-
describe('function file schema imports', () => {
|
|
198
|
-
test('imports shared schemas from types file', () => {
|
|
199
|
-
const petSchema = {
|
|
200
|
-
type: 'object' as const,
|
|
201
|
-
properties: {
|
|
202
|
-
id: { type: 'string' as const },
|
|
203
|
-
name: { type: 'string' as const },
|
|
204
|
-
},
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const spec = makeSpec({
|
|
208
|
-
componentSchemas: {
|
|
209
|
-
Pet: petSchema,
|
|
210
|
-
},
|
|
211
|
-
// Pet used by 2+ operations → shared → imported from types file
|
|
212
|
-
operations: [
|
|
213
|
-
makeOp({
|
|
214
|
-
method: 'get',
|
|
215
|
-
path: '/pets/{id}',
|
|
216
|
-
operationId: 'getPet',
|
|
217
|
-
pathParams: [
|
|
218
|
-
{ name: 'id', required: true, schema: { type: 'string' } },
|
|
219
|
-
],
|
|
220
|
-
responseSchema: petSchema,
|
|
221
|
-
}),
|
|
222
|
-
makeOp({
|
|
223
|
-
method: 'get',
|
|
224
|
-
path: '/pets',
|
|
225
|
-
operationId: 'listPets',
|
|
226
|
-
responseSchema: { type: 'array' as const, items: petSchema },
|
|
227
|
-
}),
|
|
228
|
-
],
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
232
|
-
oauth: false,
|
|
233
|
-
secret: false,
|
|
234
|
-
})
|
|
235
|
-
const funcFile = files['src/functions/getPet.function.ts']
|
|
236
|
-
assert.ok(funcFile, 'function file should exist')
|
|
237
|
-
assert.ok(
|
|
238
|
-
funcFile.includes("from '../test-api.types.js'"),
|
|
239
|
-
`should import from types file, got: ${funcFile.split('\n').slice(0, 10).join('\n')}`
|
|
240
|
-
)
|
|
241
|
-
assert.ok(funcFile.includes('PetSchema'), 'should reference PetSchema')
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
test('inlines single-use schemas instead of importing from types file', () => {
|
|
245
|
-
const petSchema = {
|
|
246
|
-
type: 'object' as const,
|
|
247
|
-
properties: {
|
|
248
|
-
id: { type: 'string' as const },
|
|
249
|
-
name: { type: 'string' as const },
|
|
250
|
-
},
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const spec = makeSpec({
|
|
254
|
-
componentSchemas: {
|
|
255
|
-
Pet: petSchema,
|
|
256
|
-
},
|
|
257
|
-
// Pet only used by 1 operation → inlined in function file
|
|
258
|
-
operations: [
|
|
259
|
-
makeOp({
|
|
260
|
-
method: 'get',
|
|
261
|
-
path: '/pets/{id}',
|
|
262
|
-
operationId: 'getPet',
|
|
263
|
-
pathParams: [
|
|
264
|
-
{ name: 'id', required: true, schema: { type: 'string' } },
|
|
265
|
-
],
|
|
266
|
-
responseSchema: { $ref: '#/components/schemas/Pet' },
|
|
267
|
-
}),
|
|
268
|
-
],
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
272
|
-
oauth: false,
|
|
273
|
-
secret: false,
|
|
274
|
-
})
|
|
275
|
-
const funcFile = files['src/functions/getPet.function.ts']
|
|
276
|
-
assert.ok(funcFile, 'function file should exist')
|
|
277
|
-
// Should NOT import from types file
|
|
278
|
-
assert.ok(
|
|
279
|
-
!funcFile.includes("from '../test-api.types.js'"),
|
|
280
|
-
`should not import from types file when schema is single-use`
|
|
281
|
-
)
|
|
282
|
-
// Schema should be inlined
|
|
283
|
-
assert.ok(funcFile.includes('const PetSchema'), 'should inline PetSchema')
|
|
284
|
-
// Types file should not be generated (no shared schemas)
|
|
285
|
-
assert.equal(
|
|
286
|
-
files['src/test-api.types.ts'],
|
|
287
|
-
undefined,
|
|
288
|
-
'should not generate types file'
|
|
289
|
-
)
|
|
290
|
-
})
|
|
291
|
-
})
|
|
292
|
-
|
|
293
|
-
// ---------------------------------------------------------------------------
|
|
294
|
-
// Duplicate properties in z.object() from merged path + operation params
|
|
295
|
-
// ---------------------------------------------------------------------------
|
|
296
|
-
describe('duplicate parameter deduplication', () => {
|
|
297
|
-
test('does not produce duplicate properties from overlapping params', () => {
|
|
298
|
-
const spec = makeSpec({
|
|
299
|
-
operations: [
|
|
300
|
-
makeOp({
|
|
301
|
-
method: 'get',
|
|
302
|
-
path: '/items/{id}',
|
|
303
|
-
operationId: 'getItem',
|
|
304
|
-
pathParams: [
|
|
305
|
-
{ name: 'id', required: true, schema: { type: 'string' } },
|
|
306
|
-
],
|
|
307
|
-
queryParams: [
|
|
308
|
-
{ name: 'id', required: false, schema: { type: 'string' } }, // duplicate from shared params
|
|
309
|
-
{ name: 'format', required: false, schema: { type: 'string' } },
|
|
310
|
-
],
|
|
311
|
-
}),
|
|
312
|
-
],
|
|
313
|
-
})
|
|
314
|
-
|
|
315
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
316
|
-
oauth: false,
|
|
317
|
-
secret: false,
|
|
318
|
-
})
|
|
319
|
-
const funcFile = files['src/functions/getItem.function.ts']
|
|
320
|
-
assert.ok(funcFile, 'function file should exist')
|
|
321
|
-
|
|
322
|
-
// Count occurrences of 'id:' in the input schema
|
|
323
|
-
const idMatches = funcFile.match(/\bid:/g)
|
|
324
|
-
assert.ok(idMatches, 'should contain id property')
|
|
325
|
-
// Should only appear once in the input schema definition
|
|
326
|
-
assert.equal(
|
|
327
|
-
idMatches.length,
|
|
328
|
-
1,
|
|
329
|
-
`id should appear only once in input, found ${idMatches.length}`
|
|
330
|
-
)
|
|
331
|
-
})
|
|
332
|
-
})
|
|
333
|
-
|
|
334
|
-
// ---------------------------------------------------------------------------
|
|
335
|
-
// Empty server URLs fallback
|
|
336
|
-
// ---------------------------------------------------------------------------
|
|
337
|
-
describe('empty server URLs', () => {
|
|
338
|
-
test('generates variable file even with empty serverUrls', () => {
|
|
339
|
-
const spec = makeSpec({
|
|
340
|
-
serverUrls: [],
|
|
341
|
-
operations: [makeOp({ operationId: 'listItems' })],
|
|
342
|
-
})
|
|
343
|
-
|
|
344
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
345
|
-
oauth: false,
|
|
346
|
-
secret: false,
|
|
347
|
-
})
|
|
348
|
-
const varFile = files['src/test-api.variable.ts']
|
|
349
|
-
assert.ok(varFile, 'variable file should exist')
|
|
350
|
-
|
|
351
|
-
// Should use z.string() instead of z.enum([]) for empty serverUrls
|
|
352
|
-
assert.ok(
|
|
353
|
-
varFile.includes('z.string()'),
|
|
354
|
-
`should fall back to z.string() for empty URLs, got: ${varFile}`
|
|
355
|
-
)
|
|
356
|
-
assert.ok(
|
|
357
|
-
!varFile.includes('z.enum([])'),
|
|
358
|
-
'should not produce empty z.enum([])'
|
|
359
|
-
)
|
|
360
|
-
})
|
|
361
|
-
})
|
|
362
|
-
|
|
363
|
-
// ---------------------------------------------------------------------------
|
|
364
|
-
// Apostrophe sanitization in display names
|
|
365
|
-
// ---------------------------------------------------------------------------
|
|
366
|
-
describe('display name sanitization', () => {
|
|
367
|
-
test('sanitizes apostrophes in display name used in service file', () => {
|
|
368
|
-
const spec = makeSpec({
|
|
369
|
-
operations: [makeOp({ operationId: 'listItems' })],
|
|
370
|
-
})
|
|
371
|
-
const vars = makeVars({ displayName: "O'Reilly API" })
|
|
372
|
-
|
|
373
|
-
const files = generateAddonFromOpenAPI(spec, vars, {
|
|
374
|
-
oauth: false,
|
|
375
|
-
secret: false,
|
|
376
|
-
})
|
|
377
|
-
const serviceFile = files['src/test-api-api.service.ts']
|
|
378
|
-
assert.ok(serviceFile, 'service file should exist')
|
|
379
|
-
|
|
380
|
-
// The display name should not break string literals
|
|
381
|
-
assert.ok(
|
|
382
|
-
!serviceFile.includes("O'Reilly"),
|
|
383
|
-
`should sanitize apostrophes, got raw apostrophe in service file`
|
|
384
|
-
)
|
|
385
|
-
})
|
|
386
|
-
})
|
|
387
|
-
|
|
388
|
-
// ---------------------------------------------------------------------------
|
|
389
|
-
// Tag description newline sanitization
|
|
390
|
-
// ---------------------------------------------------------------------------
|
|
391
|
-
describe('tag description sanitization', () => {
|
|
392
|
-
test('sanitizes newlines in tag descriptions used as comments', () => {
|
|
393
|
-
const spec = makeSpec({
|
|
394
|
-
operations: [
|
|
395
|
-
makeOp({
|
|
396
|
-
operationId: 'getUser',
|
|
397
|
-
tags: ['Users'],
|
|
398
|
-
method: 'get',
|
|
399
|
-
path: '/users/{id}',
|
|
400
|
-
pathParams: [
|
|
401
|
-
{ name: 'id', required: true, schema: { type: 'string' } },
|
|
402
|
-
],
|
|
403
|
-
}),
|
|
404
|
-
],
|
|
405
|
-
tagDescriptions: {
|
|
406
|
-
Users: 'User management\nhandles CRUD operations\nfor all users',
|
|
407
|
-
},
|
|
408
|
-
})
|
|
409
|
-
|
|
410
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
411
|
-
oauth: false,
|
|
412
|
-
secret: false,
|
|
413
|
-
})
|
|
414
|
-
const funcFile = files['src/functions/getUser.function.ts']
|
|
415
|
-
assert.ok(funcFile, 'function file should exist')
|
|
416
|
-
|
|
417
|
-
// The comment line should have newlines collapsed to spaces
|
|
418
|
-
const commentLine = funcFile
|
|
419
|
-
.split('\n')
|
|
420
|
-
.find((l: string) => l.startsWith('// Users'))
|
|
421
|
-
if (commentLine) {
|
|
422
|
-
assert.ok(
|
|
423
|
-
!commentLine.includes('\n'),
|
|
424
|
-
'tag description in comment should not contain raw newlines'
|
|
425
|
-
)
|
|
426
|
-
}
|
|
427
|
-
})
|
|
428
|
-
})
|
|
429
|
-
|
|
430
|
-
// ---------------------------------------------------------------------------
|
|
431
|
-
// Index file exports
|
|
432
|
-
// ---------------------------------------------------------------------------
|
|
433
|
-
describe('index file generation', () => {
|
|
434
|
-
test('exports all generated functions', () => {
|
|
435
|
-
const spec = makeSpec({
|
|
436
|
-
operations: [
|
|
437
|
-
makeOp({ operationId: 'listUsers', method: 'get', path: '/users' }),
|
|
438
|
-
makeOp({ operationId: 'createUser', method: 'post', path: '/users' }),
|
|
439
|
-
],
|
|
440
|
-
})
|
|
441
|
-
|
|
442
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
443
|
-
oauth: false,
|
|
444
|
-
secret: false,
|
|
445
|
-
})
|
|
446
|
-
const indexFile = files['src/index.ts']
|
|
447
|
-
assert.ok(indexFile, 'index file should exist')
|
|
448
|
-
assert.ok(indexFile.includes('listUsers'))
|
|
449
|
-
assert.ok(indexFile.includes('createUser'))
|
|
450
|
-
})
|
|
451
|
-
})
|
|
452
|
-
|
|
453
|
-
// ---------------------------------------------------------------------------
|
|
454
|
-
// Types file not generated when no component schemas
|
|
455
|
-
// ---------------------------------------------------------------------------
|
|
456
|
-
describe('types file conditional generation', () => {
|
|
457
|
-
test('does not generate types file when no component schemas', () => {
|
|
458
|
-
const spec = makeSpec({
|
|
459
|
-
componentSchemas: {},
|
|
460
|
-
operations: [makeOp({ operationId: 'ping' })],
|
|
461
|
-
})
|
|
462
|
-
|
|
463
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
464
|
-
oauth: false,
|
|
465
|
-
secret: false,
|
|
466
|
-
})
|
|
467
|
-
assert.equal(
|
|
468
|
-
files['src/test-api.types.ts'],
|
|
469
|
-
undefined,
|
|
470
|
-
'should not generate types file'
|
|
471
|
-
)
|
|
472
|
-
})
|
|
473
|
-
})
|
|
474
|
-
|
|
475
|
-
// ---------------------------------------------------------------------------
|
|
476
|
-
// Service file error handling
|
|
477
|
-
// ---------------------------------------------------------------------------
|
|
478
|
-
describe('service file generation', () => {
|
|
479
|
-
test('generates error switch statement in service file', () => {
|
|
480
|
-
const spec = makeSpec({
|
|
481
|
-
operations: [
|
|
482
|
-
makeOp({
|
|
483
|
-
operationId: 'getItem',
|
|
484
|
-
errorResponses: [
|
|
485
|
-
{ statusCode: 404, description: 'Not found' },
|
|
486
|
-
{ statusCode: 429, description: 'Rate limited' },
|
|
487
|
-
],
|
|
488
|
-
}),
|
|
489
|
-
],
|
|
490
|
-
})
|
|
491
|
-
|
|
492
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
493
|
-
oauth: false,
|
|
494
|
-
secret: false,
|
|
495
|
-
})
|
|
496
|
-
const serviceFile = files['src/test-api-api.service.ts']
|
|
497
|
-
assert.ok(serviceFile, 'service file should exist')
|
|
498
|
-
assert.ok(serviceFile.includes('NotFoundError'))
|
|
499
|
-
assert.ok(serviceFile.includes('case 404'))
|
|
500
|
-
})
|
|
501
|
-
})
|
|
502
|
-
|
|
503
|
-
// ---------------------------------------------------------------------------
|
|
504
|
-
// MCP flag
|
|
505
|
-
// ---------------------------------------------------------------------------
|
|
506
|
-
describe('MCP flag', () => {
|
|
507
|
-
test('adds mcp: true to function config when flag is set', () => {
|
|
508
|
-
const spec = makeSpec({
|
|
509
|
-
operations: [makeOp({ operationId: 'listItems' })],
|
|
510
|
-
})
|
|
511
|
-
|
|
512
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
513
|
-
oauth: false,
|
|
514
|
-
secret: false,
|
|
515
|
-
mcp: true,
|
|
516
|
-
})
|
|
517
|
-
const funcFile = files['src/functions/listItems.function.ts']
|
|
518
|
-
assert.ok(funcFile, 'function file should exist')
|
|
519
|
-
assert.ok(funcFile.includes('mcp: true'), 'should include mcp flag')
|
|
520
|
-
})
|
|
521
|
-
})
|
|
522
|
-
|
|
523
|
-
// ---------------------------------------------------------------------------
|
|
524
|
-
// camelCase flag
|
|
525
|
-
// ---------------------------------------------------------------------------
|
|
526
|
-
describe('camelCase flag', () => {
|
|
527
|
-
test('converts snake_case param names to camelCase in function input schema', () => {
|
|
528
|
-
const spec = makeSpec({
|
|
529
|
-
operations: [
|
|
530
|
-
makeOp({
|
|
531
|
-
operationId: 'getRepo',
|
|
532
|
-
method: 'get',
|
|
533
|
-
path: '/repos/{repo_slug}',
|
|
534
|
-
pathParams: [
|
|
535
|
-
{ name: 'repo_slug', required: true, schema: { type: 'string' } },
|
|
536
|
-
],
|
|
537
|
-
queryParams: [
|
|
538
|
-
{ name: 'page_size', required: false, schema: { type: 'number' } },
|
|
539
|
-
],
|
|
540
|
-
}),
|
|
541
|
-
],
|
|
542
|
-
})
|
|
543
|
-
|
|
544
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
545
|
-
oauth: false,
|
|
546
|
-
secret: false,
|
|
547
|
-
camelCase: true,
|
|
548
|
-
})
|
|
549
|
-
const funcFile = files['src/functions/getRepo.function.ts']
|
|
550
|
-
assert.ok(funcFile, 'function file should exist')
|
|
551
|
-
// Should use camelCase names
|
|
552
|
-
assert.ok(
|
|
553
|
-
funcFile.includes('repoSlug:'),
|
|
554
|
-
'should convert repo_slug to repoSlug'
|
|
555
|
-
)
|
|
556
|
-
assert.ok(
|
|
557
|
-
funcFile.includes('pageSize:'),
|
|
558
|
-
'should convert page_size to pageSize'
|
|
559
|
-
)
|
|
560
|
-
// Should NOT have original snake_case names in the schema
|
|
561
|
-
assert.ok(!funcFile.includes('repo_slug:'), 'should not contain repo_slug')
|
|
562
|
-
assert.ok(!funcFile.includes('page_size:'), 'should not contain page_size')
|
|
563
|
-
})
|
|
564
|
-
|
|
565
|
-
test('converts snake_case object property names to camelCase in response schema', () => {
|
|
566
|
-
const responseSchema = {
|
|
567
|
-
type: 'object' as const,
|
|
568
|
-
properties: {
|
|
569
|
-
created_at: { type: 'string' as const },
|
|
570
|
-
display_name: { type: 'string' as const },
|
|
571
|
-
id: { type: 'string' as const },
|
|
572
|
-
},
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
const spec = makeSpec({
|
|
576
|
-
operations: [
|
|
577
|
-
makeOp({
|
|
578
|
-
operationId: 'getUser',
|
|
579
|
-
method: 'get',
|
|
580
|
-
path: '/users/{id}',
|
|
581
|
-
pathParams: [
|
|
582
|
-
{ name: 'id', required: true, schema: { type: 'string' } },
|
|
583
|
-
],
|
|
584
|
-
responseSchema,
|
|
585
|
-
}),
|
|
586
|
-
],
|
|
587
|
-
})
|
|
588
|
-
|
|
589
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
590
|
-
oauth: false,
|
|
591
|
-
secret: false,
|
|
592
|
-
camelCase: true,
|
|
593
|
-
})
|
|
594
|
-
const funcFile = files['src/functions/getUser.function.ts']
|
|
595
|
-
assert.ok(funcFile, 'function file should exist')
|
|
596
|
-
// Output schema should use camelCase
|
|
597
|
-
assert.ok(
|
|
598
|
-
funcFile.includes('createdAt:'),
|
|
599
|
-
'should convert created_at to createdAt'
|
|
600
|
-
)
|
|
601
|
-
assert.ok(
|
|
602
|
-
funcFile.includes('displayName:'),
|
|
603
|
-
'should convert display_name to displayName'
|
|
604
|
-
)
|
|
605
|
-
// Should still have 'id' (no conversion needed)
|
|
606
|
-
assert.ok(funcFile.includes('id:'), 'should keep id as-is')
|
|
607
|
-
})
|
|
608
|
-
|
|
609
|
-
test('converts snake_case body property names to camelCase in input schema', () => {
|
|
610
|
-
const requestBody = {
|
|
611
|
-
type: 'object' as const,
|
|
612
|
-
properties: {
|
|
613
|
-
first_name: { type: 'string' as const },
|
|
614
|
-
last_name: { type: 'string' as const },
|
|
615
|
-
},
|
|
616
|
-
required: ['first_name'],
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
const spec = makeSpec({
|
|
620
|
-
operations: [
|
|
621
|
-
makeOp({
|
|
622
|
-
operationId: 'createUser',
|
|
623
|
-
method: 'post',
|
|
624
|
-
path: '/users',
|
|
625
|
-
requestBody,
|
|
626
|
-
}),
|
|
627
|
-
],
|
|
628
|
-
})
|
|
629
|
-
|
|
630
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
631
|
-
oauth: false,
|
|
632
|
-
secret: false,
|
|
633
|
-
camelCase: true,
|
|
634
|
-
})
|
|
635
|
-
const funcFile = files['src/functions/createUser.function.ts']
|
|
636
|
-
assert.ok(funcFile, 'function file should exist')
|
|
637
|
-
assert.ok(
|
|
638
|
-
funcFile.includes('firstName:'),
|
|
639
|
-
'should convert first_name to firstName'
|
|
640
|
-
)
|
|
641
|
-
assert.ok(
|
|
642
|
-
funcFile.includes('lastName:'),
|
|
643
|
-
'should convert last_name to lastName'
|
|
644
|
-
)
|
|
645
|
-
})
|
|
646
|
-
|
|
647
|
-
test('generates _toSnakeCase and _toCamelCase helpers in service file', () => {
|
|
648
|
-
const spec = makeSpec({
|
|
649
|
-
operations: [makeOp({ operationId: 'listItems' })],
|
|
650
|
-
})
|
|
651
|
-
|
|
652
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
653
|
-
oauth: false,
|
|
654
|
-
secret: false,
|
|
655
|
-
camelCase: true,
|
|
656
|
-
})
|
|
657
|
-
const serviceFile = files['src/test-api-api.service.ts']
|
|
658
|
-
assert.ok(serviceFile, 'service file should exist')
|
|
659
|
-
assert.ok(
|
|
660
|
-
serviceFile.includes('_toSnakeCase'),
|
|
661
|
-
'should contain _toSnakeCase helper'
|
|
662
|
-
)
|
|
663
|
-
assert.ok(
|
|
664
|
-
serviceFile.includes('_toCamelCase'),
|
|
665
|
-
'should contain _toCamelCase helper'
|
|
666
|
-
)
|
|
667
|
-
assert.ok(
|
|
668
|
-
serviceFile.includes('rawData'),
|
|
669
|
-
'should use rawData for snake_case converted input'
|
|
670
|
-
)
|
|
671
|
-
})
|
|
672
|
-
|
|
673
|
-
test('does not convert names when camelCase flag is off', () => {
|
|
674
|
-
const spec = makeSpec({
|
|
675
|
-
operations: [
|
|
676
|
-
makeOp({
|
|
677
|
-
operationId: 'getRepo',
|
|
678
|
-
method: 'get',
|
|
679
|
-
path: '/repos/{repo_slug}',
|
|
680
|
-
pathParams: [
|
|
681
|
-
{ name: 'repo_slug', required: true, schema: { type: 'string' } },
|
|
682
|
-
],
|
|
683
|
-
}),
|
|
684
|
-
],
|
|
685
|
-
})
|
|
686
|
-
|
|
687
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
688
|
-
oauth: false,
|
|
689
|
-
secret: false,
|
|
690
|
-
camelCase: false,
|
|
691
|
-
})
|
|
692
|
-
const funcFile = files['src/functions/getRepo.function.ts']
|
|
693
|
-
assert.ok(funcFile, 'function file should exist')
|
|
694
|
-
// Should keep original snake_case names
|
|
695
|
-
assert.ok(funcFile.includes('repo_slug:'), 'should keep repo_slug as-is')
|
|
696
|
-
assert.ok(!funcFile.includes('repoSlug:'), 'should not convert to repoSlug')
|
|
697
|
-
})
|
|
698
|
-
|
|
699
|
-
test('converts snake_case in shared component schemas', () => {
|
|
700
|
-
const itemSchema = {
|
|
701
|
-
type: 'object' as const,
|
|
702
|
-
properties: {
|
|
703
|
-
item_name: { type: 'string' as const },
|
|
704
|
-
created_at: { type: 'string' as const },
|
|
705
|
-
},
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
const spec = makeSpec({
|
|
709
|
-
componentSchemas: {
|
|
710
|
-
Item: itemSchema,
|
|
711
|
-
},
|
|
712
|
-
operations: [
|
|
713
|
-
makeOp({
|
|
714
|
-
method: 'get',
|
|
715
|
-
path: '/items',
|
|
716
|
-
operationId: 'listItems',
|
|
717
|
-
responseSchema: { type: 'array' as const, items: itemSchema },
|
|
718
|
-
}),
|
|
719
|
-
makeOp({
|
|
720
|
-
method: 'get',
|
|
721
|
-
path: '/items/{id}',
|
|
722
|
-
operationId: 'getItem',
|
|
723
|
-
pathParams: [
|
|
724
|
-
{ name: 'id', required: true, schema: { type: 'string' } },
|
|
725
|
-
],
|
|
726
|
-
responseSchema: itemSchema,
|
|
727
|
-
}),
|
|
728
|
-
],
|
|
729
|
-
})
|
|
730
|
-
|
|
731
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
732
|
-
oauth: false,
|
|
733
|
-
secret: false,
|
|
734
|
-
camelCase: true,
|
|
735
|
-
})
|
|
736
|
-
const typesFile = files['src/test-api.types.ts']
|
|
737
|
-
assert.ok(typesFile, 'types file should exist')
|
|
738
|
-
assert.ok(
|
|
739
|
-
typesFile.includes('itemName:'),
|
|
740
|
-
'should convert item_name to itemName in shared schema'
|
|
741
|
-
)
|
|
742
|
-
assert.ok(
|
|
743
|
-
typesFile.includes('createdAt:'),
|
|
744
|
-
'should convert created_at to createdAt in shared schema'
|
|
745
|
-
)
|
|
746
|
-
})
|
|
747
|
-
|
|
748
|
-
test('ROUTES map keeps original snake_case names for HTTP requests', () => {
|
|
749
|
-
const spec = makeSpec({
|
|
750
|
-
operations: [
|
|
751
|
-
makeOp({
|
|
752
|
-
operationId: 'getRepo',
|
|
753
|
-
method: 'get',
|
|
754
|
-
path: '/repos/{repo_slug}',
|
|
755
|
-
pathParams: [
|
|
756
|
-
{ name: 'repo_slug', required: true, schema: { type: 'string' } },
|
|
757
|
-
],
|
|
758
|
-
queryParams: [
|
|
759
|
-
{ name: 'page_size', required: false, schema: { type: 'number' } },
|
|
760
|
-
],
|
|
761
|
-
}),
|
|
762
|
-
],
|
|
763
|
-
})
|
|
764
|
-
|
|
765
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
766
|
-
oauth: false,
|
|
767
|
-
secret: false,
|
|
768
|
-
camelCase: true,
|
|
769
|
-
})
|
|
770
|
-
const serviceFile = files['src/test-api-api.service.ts']
|
|
771
|
-
assert.ok(serviceFile, 'service file should exist')
|
|
772
|
-
// ROUTES should use original names for HTTP path interpolation
|
|
773
|
-
assert.ok(
|
|
774
|
-
serviceFile.includes('"repo_slug"'),
|
|
775
|
-
'ROUTES should keep repo_slug for path params'
|
|
776
|
-
)
|
|
777
|
-
assert.ok(
|
|
778
|
-
serviceFile.includes('"page_size"'),
|
|
779
|
-
'ROUTES should keep page_size for query params'
|
|
780
|
-
)
|
|
781
|
-
})
|
|
782
|
-
})
|
|
783
|
-
|
|
784
|
-
// ---------------------------------------------------------------------------
|
|
785
|
-
// Function/MCP-tool description synthesis (specs that omit descriptions)
|
|
786
|
-
// ---------------------------------------------------------------------------
|
|
787
|
-
describe('function description synthesis', () => {
|
|
788
|
-
test('synthesizes a description from operationId when the spec omits one', () => {
|
|
789
|
-
const spec = makeSpec({
|
|
790
|
-
operations: [
|
|
791
|
-
makeOp({
|
|
792
|
-
operationId: 'contactsControllerGetContacts',
|
|
793
|
-
method: 'get',
|
|
794
|
-
path: '/contacts',
|
|
795
|
-
}),
|
|
796
|
-
],
|
|
797
|
-
})
|
|
798
|
-
|
|
799
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
800
|
-
oauth: false,
|
|
801
|
-
secret: false,
|
|
802
|
-
mcp: true,
|
|
803
|
-
})
|
|
804
|
-
const funcFile =
|
|
805
|
-
files['src/functions/contactsControllerGetContacts.function.ts']
|
|
806
|
-
assert.ok(funcFile, 'function file should exist')
|
|
807
|
-
assert.ok(
|
|
808
|
-
funcFile.includes('description: "Contacts get contacts"'),
|
|
809
|
-
'humanized operationId (Controller stripped) should be emitted as the description'
|
|
810
|
-
)
|
|
811
|
-
assert.ok(funcFile.includes('mcp: true'), 'mcp tool flag should be present')
|
|
812
|
-
})
|
|
813
|
-
|
|
814
|
-
test('falls back to "METHOD /path" when operationId is absent', () => {
|
|
815
|
-
const spec = makeSpec({
|
|
816
|
-
operations: [
|
|
817
|
-
makeOp({ operationId: undefined, method: 'get', path: '/health' }),
|
|
818
|
-
],
|
|
819
|
-
})
|
|
820
|
-
|
|
821
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
822
|
-
oauth: false,
|
|
823
|
-
secret: false,
|
|
824
|
-
})
|
|
825
|
-
const key = Object.keys(files).find((k) => k.startsWith('src/functions/'))
|
|
826
|
-
assert.ok(key, 'a function file should be generated')
|
|
827
|
-
assert.ok(
|
|
828
|
-
files[key].includes('description: "GET /health"'),
|
|
829
|
-
'should fall back to METHOD path when nothing else is available'
|
|
830
|
-
)
|
|
831
|
-
})
|
|
832
|
-
|
|
833
|
-
test('prefers the spec description over synthesis', () => {
|
|
834
|
-
const spec = makeSpec({
|
|
835
|
-
operations: [
|
|
836
|
-
makeOp({
|
|
837
|
-
operationId: 'getPet',
|
|
838
|
-
description: 'Fetch a single pet by id',
|
|
839
|
-
}),
|
|
840
|
-
],
|
|
841
|
-
})
|
|
842
|
-
|
|
843
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
844
|
-
oauth: false,
|
|
845
|
-
secret: false,
|
|
846
|
-
})
|
|
847
|
-
const funcFile = files['src/functions/getPet.function.ts']
|
|
848
|
-
assert.ok(
|
|
849
|
-
funcFile.includes('description: "Fetch a single pet by id"'),
|
|
850
|
-
'the real spec description should win over the synthesized one'
|
|
851
|
-
)
|
|
852
|
-
})
|
|
853
|
-
})
|
|
854
|
-
|
|
855
|
-
// ---------------------------------------------------------------------------
|
|
856
|
-
// Auth-config: custom headers + delegated login
|
|
857
|
-
// ---------------------------------------------------------------------------
|
|
858
|
-
describe('auth config', () => {
|
|
859
|
-
const delegatedConfig = {
|
|
860
|
-
headerName: 'authentication',
|
|
861
|
-
headerFormat: 'raw' as const,
|
|
862
|
-
delegated: {
|
|
863
|
-
loginPath: '/users/login-ai-plugin',
|
|
864
|
-
loginMethod: 'post',
|
|
865
|
-
credentials: ['email', 'password'] as ('email' | 'password' | 'apiKey')[],
|
|
866
|
-
apiKeyHeader: 'x-api-key',
|
|
867
|
-
tokenPath: 'token',
|
|
868
|
-
claims: {
|
|
869
|
-
source: 'jwt' as const,
|
|
870
|
-
externalId: 'user._id',
|
|
871
|
-
email: 'user.email',
|
|
872
|
-
name: ['user.first_name', 'user.last_name'],
|
|
873
|
-
role: 'user.role',
|
|
874
|
-
tenantId: 'user.company',
|
|
875
|
-
},
|
|
876
|
-
},
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
test('custom raw header replaces Authorization Bearer in the bearer-credential service', () => {
|
|
880
|
-
const spec = makeSpec({ operations: [makeOp()] })
|
|
881
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
882
|
-
oauth: false,
|
|
883
|
-
secret: false,
|
|
884
|
-
credential: 'bearer',
|
|
885
|
-
authConfig: delegatedConfig,
|
|
886
|
-
})
|
|
887
|
-
const service = files['src/test-api-api.service.ts']
|
|
888
|
-
assert.ok(
|
|
889
|
-
service.includes('headers["authentication"] = this.creds.token'),
|
|
890
|
-
'custom raw header should carry the bare token'
|
|
891
|
-
)
|
|
892
|
-
assert.ok(
|
|
893
|
-
!service.includes('headers.Authorization'),
|
|
894
|
-
'default Authorization header must not be emitted'
|
|
895
|
-
)
|
|
896
|
-
})
|
|
897
|
-
|
|
898
|
-
test('custom header with bearer format keeps the Bearer prefix', () => {
|
|
899
|
-
const spec = makeSpec({ operations: [makeOp()] })
|
|
900
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
901
|
-
oauth: false,
|
|
902
|
-
secret: false,
|
|
903
|
-
credential: 'bearer',
|
|
904
|
-
authConfig: { headerName: 'x-auth', headerFormat: 'bearer' as const },
|
|
905
|
-
})
|
|
906
|
-
const service = files['src/test-api-api.service.ts']
|
|
907
|
-
assert.ok(
|
|
908
|
-
service.includes('headers["x-auth"] = `Bearer ${this.creds.token}`'),
|
|
909
|
-
'bearer format should keep the prefix on the custom header'
|
|
910
|
-
)
|
|
911
|
-
})
|
|
912
|
-
|
|
913
|
-
test('delegated config emits the upstream-auth file and exports it', () => {
|
|
914
|
-
const spec = makeSpec({ operations: [makeOp()] })
|
|
915
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
916
|
-
oauth: false,
|
|
917
|
-
secret: false,
|
|
918
|
-
credential: 'bearer',
|
|
919
|
-
authConfig: delegatedConfig,
|
|
920
|
-
})
|
|
921
|
-
const authFile = files['src/test-api-upstream-auth.ts']
|
|
922
|
-
assert.ok(authFile, 'upstream auth file should be generated')
|
|
923
|
-
assert.ok(authFile.includes('export const authenticateTestApiUpstream'))
|
|
924
|
-
assert.ok(
|
|
925
|
-
authFile.includes('/users/login-ai-plugin'),
|
|
926
|
-
'login path baked in'
|
|
927
|
-
)
|
|
928
|
-
assert.ok(authFile.includes('"user._id"'), 'externalId claim path baked in')
|
|
929
|
-
assert.ok(
|
|
930
|
-
authFile.includes("pick(claims, 'exp')"),
|
|
931
|
-
'jwt source defaults expiry to the exp claim'
|
|
932
|
-
)
|
|
933
|
-
assert.ok(
|
|
934
|
-
files['src/index.ts'].includes(
|
|
935
|
-
"export { authenticateTestApiUpstream } from './test-api-upstream-auth.js'"
|
|
936
|
-
),
|
|
937
|
-
'index should export the authenticate function'
|
|
938
|
-
)
|
|
939
|
-
})
|
|
940
|
-
|
|
941
|
-
test('extraHeaders are baked into the service and the upstream-auth login', () => {
|
|
942
|
-
const spec = makeSpec({ operations: [makeOp()] })
|
|
943
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
944
|
-
oauth: false,
|
|
945
|
-
secret: false,
|
|
946
|
-
credential: 'bearer',
|
|
947
|
-
authConfig: {
|
|
948
|
-
...delegatedConfig,
|
|
949
|
-
extraHeaders: { Origin: 'https://tenant.example.com' },
|
|
950
|
-
},
|
|
951
|
-
})
|
|
952
|
-
const service = files['src/test-api-api.service.ts']
|
|
953
|
-
assert.ok(
|
|
954
|
-
service.includes('"Origin": "https://tenant.example.com",'),
|
|
955
|
-
'service headers init should carry the static header'
|
|
956
|
-
)
|
|
957
|
-
const authFile = files['src/test-api-upstream-auth.ts']
|
|
958
|
-
assert.ok(
|
|
959
|
-
authFile.includes('"Origin": "https://tenant.example.com",'),
|
|
960
|
-
'login fetch should carry the static header'
|
|
961
|
-
)
|
|
962
|
-
})
|
|
963
|
-
|
|
964
|
-
test('no delegated config → no upstream-auth file', () => {
|
|
965
|
-
const spec = makeSpec({ operations: [makeOp()] })
|
|
966
|
-
const files = generateAddonFromOpenAPI(spec, makeVars(), {
|
|
967
|
-
oauth: false,
|
|
968
|
-
secret: false,
|
|
969
|
-
credential: 'bearer',
|
|
970
|
-
})
|
|
971
|
-
assert.equal(files['src/test-api-upstream-auth.ts'], undefined)
|
|
972
|
-
})
|
|
973
|
-
})
|