forked from orval-labs/orval
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapi-generation.spec.ts
More file actions
143 lines (126 loc) 路 5.39 KB
/
Copy pathapi-generation.spec.ts
File metadata and controls
143 lines (126 loc) 路 5.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { readFile } from 'node:fs/promises';
import path from 'node:path';
import { expect, test } from 'vitest';
import { describeApiGenerationSnapshots } from '../test-utils/snapshot-testing';
const generated = (...segments: string[]) =>
path.resolve(import.meta.dirname, 'generated', ...segments);
await describeApiGenerationSnapshots({
dirs: [
generated('angular'),
generated('angular-query'),
generated('axios'),
generated('cli'),
generated('default'),
generated('fetch'),
generated('hono'),
generated('mcp'),
generated('mock'),
generated('multi-files'),
generated('react-query'),
generated('svelte-query'),
generated('swr'),
generated('vue-query'),
generated('zod'),
],
snapshotsDir: path.resolve(import.meta.dirname, '__snapshots__'),
rootDir: path.resolve(import.meta.dirname, '..'),
});
test('angular issue-3103 emits filterParams in tags-split default service', async () => {
// Keep this focused assertion alongside the snapshot so #3103 fails with a
// targeted message instead of a full-file snapshot diff.
const defaultServiceFile = generated(
'angular',
'issue-3103',
'default',
'default.service.ts',
);
const content = await readFile(defaultServiceFile, 'utf8');
expect(content).toContain('export class DefaultService');
expect(content).toContain('function filterParams(');
expect(content).toContain('const filteredParams = filterParams(');
});
test('react-query issue-708 isolates the infinite query key from the regular one', async () => {
// Regression for #708: an operation generated as both a regular and an
// infinite query must not share a query key, otherwise React Query serves
// one query's cached data for the other. Keep this focused assertion
// alongside the snapshot so #708 fails with a targeted message instead of a
// full-file snapshot diff.
const content = await readFile(
generated('react-query', 'issue-708', 'endpoints.ts'),
'utf8',
);
// Slices out a single `getGetList[Infinite]QueryKey` declaration.
const queryKeyFn = (variant: 'Infinite' | '') => {
const marker = `getGetList${variant}QueryKey = (`;
const start = content.indexOf(marker);
expect(start, `${marker} should be generated`).toBeGreaterThan(-1);
const end = content.indexOf('as const', start);
expect(end, `${marker} body should be terminated`).toBeGreaterThan(start);
return content.slice(start, end);
};
// Both key fns must exist as separate functions.
expect(content).toContain('getGetListInfiniteQueryKey = (');
expect(content).toContain('getGetListQueryKey = (');
// The infinite key carries an 'infinite' segment; the regular one must not,
// otherwise the two query keys would be identical and collide in the cache.
expect(queryKeyFn('Infinite')).toContain("'infinite'");
expect(queryKeyFn('')).not.toContain("'infinite'");
// Each hook must consume its own key fn. Assert on the full `queryKey`
// assignment rather than the bare call: `getGetListQueryKey(params)` is a
// suffix of `getGetListInfiniteQueryKey(params)`, so a bare-call check would
// pass even if the regular hook never invoked its own key fn.
expect(content).toContain(
'queryOptions?.queryKey ?? getGetListInfiniteQueryKey(params)',
);
expect(content).toContain(
'queryOptions?.queryKey ?? getGetListQueryKey(params)',
);
});
test('default issue-826 wraps bodies whose readonly props come from nested schemas', async () => {
// Regression for #826: an object schema with no direct `readOnly` property
// but a property referencing a schema that does have readonly props must
// still wrap the request body in `NonReadonly<>`, otherwise the nested
// readonly modifier leaks into the request type. Keep this focused assertion
// alongside the snapshot so #826 fails with a targeted message instead of a
// full-file snapshot diff.
const content = await readFile(
generated('default', 'readonly', 'endpoints.ts'),
'utf8',
);
expect(content).toContain(
'nestedReadonlyObject?: NonReadonly<NestedReadonlyObject>',
);
});
test('default issue-873 does not duplicate multi-tag operations across tag files', async () => {
// Regression for #873: an operation declaring multiple tags
// (`listPets` has `tags: [cats, dogs]` in multiple-tags.yaml) must be
// emitted only under its first tag. Emitting it under every tag duplicated
// the operation across tag files and made the workspace index re-export the
// same symbol twice, so the generated code failed to compile. Keep this
// focused assertion alongside the snapshot so #873 fails with a targeted
// message instead of a full-file snapshot diff.
const marker = 'export const listPets = (';
// [mode, first-tag file, later-tag file]
const modes = [
['multiple-tags', ['cats.ts'], ['dogs.ts']],
['multiple-tags-split', ['cats', 'cats.ts'], ['dogs', 'dogs.ts']],
] as const;
for (const [mode, firstTag, laterTag] of modes) {
const firstTagContent = await readFile(
generated('default', mode, ...firstTag),
'utf8',
);
const laterTagContent = await readFile(
generated('default', mode, ...laterTag),
'utf8',
);
expect(
firstTagContent,
`${mode}: listPets should be emitted under its first tag`,
).toContain(marker);
expect(
laterTagContent,
`${mode}: listPets must not be duplicated into a later tag`,
).not.toContain(marker);
}
});