openapi_generate_typescript_fetch 0.1.0
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +17 -0
- data/lib/openapi_generate_typescript_fetch/schema.rb +115 -0
- data/lib/openapi_generate_typescript_fetch/taskinfo.rb +52 -0
- data/lib/openapi_generate_typescript_fetch/tasks.rb +43 -0
- data/lib/openapi_generate_typescript_fetch/version.rb +9 -0
- data/lib/openapi_generate_typescript_fetch.rb +107 -0
- data/template/package.json.erb +48 -0
- data/template/src/callclasses.ts.erb +542 -0
- data/template/src/helpers.ts.erb +191 -0
- data/template/src/index.ts.erb +19 -0
- data/template/src/schemas.ts.erb +1016 -0
- data/template/src/servers.ts.erb +226 -0
- data/template/src/shared.ts.erb +114 -0
- data/template/test/callclasses.ts.erb +844 -0
- data/template/test/helpers.ts.erb +1214 -0
- data/template/test/makers.ts.erb +1237 -0
- data/template/test/schemas.ts.erb +664 -0
- data/template/test/servers.ts.erb +109 -0
- data/template/test/shared.ts.erb +107 -0
- data/template/tsconfig.json.erb +51 -0
- metadata +112 -0
|
@@ -0,0 +1,844 @@
|
|
|
1
|
+
<%=
|
|
2
|
+
Gen.output.config = Gen.x.cfg['ts_indentation']
|
|
3
|
+
Gen.x.generator_info
|
|
4
|
+
%>
|
|
5
|
+
|
|
6
|
+
import {expect} from 'chai';
|
|
7
|
+
import type {CallExtras, String2String, EncodedBodyType, EncoderFunction, <%= Gen.x.schemas.import_types.join(', ') %>} from '../src/schemas.d.ts';
|
|
8
|
+
import {<%= Gen.x.schemas.imports.join(', ') %>} from '../src/schemas.js';
|
|
9
|
+
import type {UrlRequestInit, RequestInitModifier, PrepareArgs, <%= Gen.x.callclasses.import_types.join(', ') %>} from '../src/callclasses.d.ts';
|
|
10
|
+
import {isUrlRequestInit, RequestError, ResponseError, <%= Gen.x.callclasses.imports.join(', ') %>} from '../src/callclasses.js';
|
|
11
|
+
import {<%= Gen.x.makers.functions.sort.join(', ') %>} from './makers.js';
|
|
12
|
+
import { shared } from '../src/shared.js';
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
describe('Not generated', function () {
|
|
16
|
+
describe('isUrlRequestInit', function () {
|
|
17
|
+
it('valids pass', function () {
|
|
18
|
+
expect(isUrlRequestInit({ url: 'any', ri: {}})).to.be.true;
|
|
19
|
+
expect(isUrlRequestInit({ url: 'https://example.com', ri: { any: 'object' }})).to.be.true;
|
|
20
|
+
})
|
|
21
|
+
it('invalids', function () {
|
|
22
|
+
expect(isUrlRequestInit(null)).to.be.false;
|
|
23
|
+
expect(isUrlRequestInit({})).to.be.false;
|
|
24
|
+
expect(isUrlRequestInit({ notUrl: 'string', any: {} })).to.be.false;
|
|
25
|
+
expect(isUrlRequestInit({ url: 345, ri: {} })).to.be.false;
|
|
26
|
+
expect(isUrlRequestInit({ url: 'https://valid.looking', ri: 'invalid' })).to.be.false;
|
|
27
|
+
expect(isUrlRequestInit(3)).to.be.false;
|
|
28
|
+
expect(isUrlRequestInit('invalid')).to.be.false;
|
|
29
|
+
expect(isUrlRequestInit(true)).to.be.false;
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// Caller class tests.
|
|
35
|
+
|
|
36
|
+
<%=
|
|
37
|
+
out = []
|
|
38
|
+
Gen.doc['paths'].each do |path, path_item_object|
|
|
39
|
+
oos = OpenAPISourceTools::ApiObjects.operation_objects(path_item_object)
|
|
40
|
+
oos.each do |method, operation_object|
|
|
41
|
+
oid = operation_object['operationId']
|
|
42
|
+
next if oid.nil?
|
|
43
|
+
clsName = operation_object[:name]
|
|
44
|
+
clsRespName = operation_object[:resp_name]
|
|
45
|
+
clsReqError = operation_object[:req_error]
|
|
46
|
+
paramless = operation_object[:parameterless]
|
|
47
|
+
req = operation_object[:request]
|
|
48
|
+
has_encoder = req[:has_encoder]
|
|
49
|
+
paraschemas = operation_object[:paraschemas]
|
|
50
|
+
out.push("describe('#{oid} #{clsName}', function () {")
|
|
51
|
+
s = <<EOB
|
|
52
|
+
it('setExtras headers', function () {
|
|
53
|
+
const x: CallExtras = {
|
|
54
|
+
headers: { arbitrary: 'stringvalue' }
|
|
55
|
+
};
|
|
56
|
+
const r: #{clsName} = new #{clsName}();
|
|
57
|
+
expect(Object.is(r, r.setExtras(x))).to.be.true;
|
|
58
|
+
expect(r.extras).to.deep.equal(x);
|
|
59
|
+
const x2: CallExtras = {
|
|
60
|
+
headers: { secondary: 'valuestring' }
|
|
61
|
+
};
|
|
62
|
+
expect(Object.is(r, r.setExtras(x2))).to.be.true;
|
|
63
|
+
expect(r.extras).to.have.property('headers');
|
|
64
|
+
expect(r.extras!.headers).to.have.property('arbitrary', x.headers!.arbitrary);
|
|
65
|
+
expect(r.extras!.headers).to.have.property('secondary', x2.headers!.secondary);
|
|
66
|
+
});
|
|
67
|
+
it('setExtras query', function () {
|
|
68
|
+
const x: CallExtras = {
|
|
69
|
+
query: { arbitrary: 'stringvalue' }
|
|
70
|
+
};
|
|
71
|
+
const r: #{clsName} = new #{clsName}();
|
|
72
|
+
expect(Object.is(r, r.setExtras(x))).to.be.true;
|
|
73
|
+
expect(r.extras).to.deep.equal(x);
|
|
74
|
+
const x2: CallExtras = {
|
|
75
|
+
query: { secondary: 'valuestring' }
|
|
76
|
+
};
|
|
77
|
+
expect(Object.is(r, r.setExtras(x2))).to.be.true;
|
|
78
|
+
expect(r.extras).to.have.property('query');
|
|
79
|
+
expect(r.extras!.query).to.have.property('arbitrary', x.query!.arbitrary);
|
|
80
|
+
expect(r.extras!.query).to.have.property('secondary', x2.query!.secondary);
|
|
81
|
+
});
|
|
82
|
+
it('setExtras headers and query', function () {
|
|
83
|
+
const x: CallExtras = {
|
|
84
|
+
headers: { arbitrary: 'stringvalue' },
|
|
85
|
+
query: { param: 'value' }
|
|
86
|
+
};
|
|
87
|
+
const r: #{clsName} = new #{clsName}();
|
|
88
|
+
expect(Object.is(r, r.setExtras(x))).to.be.true;
|
|
89
|
+
expect(r.extras).to.deep.equal(x);
|
|
90
|
+
const x2: CallExtras = {
|
|
91
|
+
headers: { secondary: 'valuestring' },
|
|
92
|
+
query: { other: 'string' }
|
|
93
|
+
};
|
|
94
|
+
expect(Object.is(r, r.setExtras(x2))).to.be.true;
|
|
95
|
+
expect(r.extras).to.have.property('headers');
|
|
96
|
+
expect(r.extras!.headers).to.have.property('arbitrary', x.headers!.arbitrary);
|
|
97
|
+
expect(r.extras!.headers).to.have.property('secondary', x2.headers!.secondary);
|
|
98
|
+
expect(r.extras).to.have.property('query');
|
|
99
|
+
expect(r.extras!.query).to.have.property('param', x.query!.param);
|
|
100
|
+
expect(r.extras!.query).to.have.property('other', x2.query!.other);
|
|
101
|
+
});
|
|
102
|
+
it('setModifier', function () {
|
|
103
|
+
const modder = (ur: UrlRequestInit) => {};
|
|
104
|
+
const r: #{clsName} = new #{clsName}();
|
|
105
|
+
expect(r.modifier).to.be.undefined;
|
|
106
|
+
expect(Object.is(r, r.setModifier(modder))).to.be.true;
|
|
107
|
+
expect(r.modifier).to.equal(modder);
|
|
108
|
+
});
|
|
109
|
+
it('setServerUrl', function () {
|
|
110
|
+
const su = 'serverurl';
|
|
111
|
+
const r: #{clsName} = new #{clsName}();
|
|
112
|
+
expect(r.serverUrl).to.be.undefined;
|
|
113
|
+
expect(Object.is(r, r.setServerUrl(su))).to.be.true;
|
|
114
|
+
expect(r.serverUrl).to.equal(su);
|
|
115
|
+
});
|
|
116
|
+
EOB
|
|
117
|
+
out.push(s)
|
|
118
|
+
if has_encoder
|
|
119
|
+
s = <<EOB
|
|
120
|
+
it('setEncoder', function () {
|
|
121
|
+
const enc = (body: any): [EncodedBodyType, string, number, String2String] => { return ['encodedBody', 'somevalue', 10, {} as String2String]; }
|
|
122
|
+
const r: #{clsName} = new #{clsName}();
|
|
123
|
+
expect(r.encoder).to.be.undefined;
|
|
124
|
+
expect(Object.is(r, r.setEncoder(enc))).to.be.true;
|
|
125
|
+
expect(r.encoder).to.equal(enc);
|
|
126
|
+
});
|
|
127
|
+
EOB
|
|
128
|
+
out.push(s)
|
|
129
|
+
end
|
|
130
|
+
if paramless
|
|
131
|
+
[ 'instance', clsName, 'shared' ].each do |source|
|
|
132
|
+
[ true, false ].each do |signalable|
|
|
133
|
+
[ true, false ].each do |create|
|
|
134
|
+
[ true, false ].each do |precreate|
|
|
135
|
+
s = <<EOB
|
|
136
|
+
it('#{signalable ? 'A' : 'Not a'}bort signalable via #{source}, #{create ? '' : 'do not '}create signal#{precreate ? ', added before' : ''}', function () {
|
|
137
|
+
const instance: #{clsName} = new #{clsName}();
|
|
138
|
+
#{precreate ? '' : '// '}instance.abortController = new AbortController();
|
|
139
|
+
#{clsName}.abortSignalable = undefined;
|
|
140
|
+
#{source}.abortSignalable = #{signalable};
|
|
141
|
+
const p: UrlRequestInit = instance.prepare(#{create ? '{ createAbortSignal: true }' : ''});
|
|
142
|
+
shared.abortSignalable = false;
|
|
143
|
+
expect(isUrlRequestInit(p)).to.be.true;
|
|
144
|
+
expect(Object.is(instance.urlRequestInit, p)).to.be.true;
|
|
145
|
+
expect(instance.abortController).#{signalable || create || precreate ? 'not.' : ''}to.be.null;
|
|
146
|
+
#{signalable || create || precreate ? '' : '// '}expect(Object.is(p.ri.signal, instance.abortController!.signal)).to.be.true;
|
|
147
|
+
#{signalable || create || precreate ? '// ' : ''}expect(p.ri.signal).to.be.undefined;
|
|
148
|
+
});
|
|
149
|
+
EOB
|
|
150
|
+
out.push(s)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
red = (source == 'instance') ? 'error' : 'manual'
|
|
155
|
+
s = <<EOB
|
|
156
|
+
it('Modifier via #{source}', function () {
|
|
157
|
+
const modder = (ur: UrlRequestInit) => { ur.ri.redirect = '#{red}'; };
|
|
158
|
+
const instance: #{clsName} = new #{clsName}();
|
|
159
|
+
#{source}.#{source == 'instance' ? 'setModifier(modder)' : 'modifier = modder'};
|
|
160
|
+
const p: UrlRequestInit = instance.prepare();
|
|
161
|
+
shared.modifier = null;
|
|
162
|
+
expect(isUrlRequestInit(p)).to.be.true;
|
|
163
|
+
expect(Object.is(instance.urlRequestInit, p)).to.be.true;
|
|
164
|
+
expect(p.ri).to.have.property('redirect', '#{red}');
|
|
165
|
+
});
|
|
166
|
+
EOB
|
|
167
|
+
out.push(s)
|
|
168
|
+
ident = 'a'
|
|
169
|
+
value = 'b'
|
|
170
|
+
s = <<EOB
|
|
171
|
+
it('Extra query parameter via #{source}', function () {
|
|
172
|
+
const xt: CallExtras = { query: { #{ident}: '#{value}' }};
|
|
173
|
+
const instance: #{clsName} = new #{clsName}();
|
|
174
|
+
#{source}.#{source == 'instance' ? 'setExtras(xt)' : 'extras = xt'};
|
|
175
|
+
const p: UrlRequestInit = instance.prepare();
|
|
176
|
+
#{clsName}.extras = {};
|
|
177
|
+
shared.extras = {};
|
|
178
|
+
expect(p.url.endsWith(`${encodeURIComponent('#{ident}')}=${encodeURIComponent('#{value}')}`)).to.be.true;
|
|
179
|
+
});
|
|
180
|
+
EOB
|
|
181
|
+
out.push(s)
|
|
182
|
+
end
|
|
183
|
+
else
|
|
184
|
+
param_info = paraschemas.map do |ps|
|
|
185
|
+
{
|
|
186
|
+
name: ps[:param][:name],
|
|
187
|
+
in: ps[:param]['in'], # Where to look from: path, query, header.
|
|
188
|
+
schema: ps[:schema],
|
|
189
|
+
required: ps[:param].fetch('required', false),
|
|
190
|
+
maker: ps[:schema][:pass].first
|
|
191
|
+
}
|
|
192
|
+
end
|
|
193
|
+
s = <<EOB
|
|
194
|
+
it('Missing parameters', function () {
|
|
195
|
+
const r: #{clsName} = new #{clsName}();
|
|
196
|
+
expect(() => r.prepare()).to.throw(#{clsReqError}, 'parameters do not conform');
|
|
197
|
+
});
|
|
198
|
+
EOB
|
|
199
|
+
out.push(s)
|
|
200
|
+
expects = param_info.select { |p| p[:required] }.map do |pi|
|
|
201
|
+
case pi[:in]
|
|
202
|
+
when 'path' then "if (params.#{pi[:name]} !== undefined) expect(p.url.includes(params.#{pi[:name]})).to.be.true;"
|
|
203
|
+
when 'query' then "if (params.#{pi[:name]} !== undefined) expect(p.url.includes(`${encodeURIComponent(#{pi[:name]})}=${encodeURIComponent(params.#{pi[:name]})}`)).to.be.true;"
|
|
204
|
+
when 'header' then "if (params.#{pi[:name]} !== undefined) expect((p.ri.headers! as Headers).get('#{pi[:name]}')).to.equal(params.#{pi[:name]});"
|
|
205
|
+
else
|
|
206
|
+
"// #{clsName} parameter #{pi[:name]} located in #{pi[:in]} not tested."
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
# Loop over all pass functions.
|
|
210
|
+
operation_object[:pass].each do |fn|
|
|
211
|
+
s = <<EOB
|
|
212
|
+
it('Pass using #{fn}', function () {
|
|
213
|
+
const params: #{operation_object[:args_name]} = #{fn}();
|
|
214
|
+
const r: #{clsName} = new #{clsName}(params);
|
|
215
|
+
const p: UrlRequestInit = r.prepare();
|
|
216
|
+
expect(isUrlRequestInit(p)).to.be.true;
|
|
217
|
+
expect(Object.is(r.urlRequestInit, p)).to.be.true;
|
|
218
|
+
#{expects.compact.join("\n")}
|
|
219
|
+
const keys = Object.keys(params);
|
|
220
|
+
if (!keys.includes('encoded')) {
|
|
221
|
+
const others = keys.filter((name: string): boolean => name.startsWith('body') && name != 'body');
|
|
222
|
+
if (keys.includes('body')) {
|
|
223
|
+
expect(p.ri).to.have.property('body');
|
|
224
|
+
const pr: object = params as object;
|
|
225
|
+
for (const other of others)
|
|
226
|
+
expect(Object.is(pr['body' as keyof object], pr[other as keyof object])).to.be.true;
|
|
227
|
+
} else if (0 < others.length) {
|
|
228
|
+
expect(others).to.have.lengthOf(1);
|
|
229
|
+
expect(p.ri).to.have.property('body');
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
expect(p.ri).to.have.property('body');
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
EOB
|
|
236
|
+
out.push(s)
|
|
237
|
+
end
|
|
238
|
+
fn = operation_object[:pass].first
|
|
239
|
+
unless fn.nil?
|
|
240
|
+
[ true, false ].each do |signalable|
|
|
241
|
+
[ true, false ].each do |create|
|
|
242
|
+
[ true, false ].each do |precreate|
|
|
243
|
+
[ 'instance', clsName, 'shared' ].each do |source|
|
|
244
|
+
s = <<EOB
|
|
245
|
+
it('#{signalable ? 'A' : 'Not a'}bort signalable via #{source}, #{create ? '' : 'do not '}create signal#{precreate ? ', added before' : ''}', function () {
|
|
246
|
+
const params: #{operation_object[:args_name]} = #{fn}();
|
|
247
|
+
const instance: #{clsName} = new #{clsName}(params);
|
|
248
|
+
#{precreate ? '' : '// '}instance.abortController = new AbortController();
|
|
249
|
+
#{source}.abortSignalable = #{signalable};
|
|
250
|
+
const p: UrlRequestInit = instance.prepare({ createAbortSignal: #{create} });
|
|
251
|
+
shared.abortSignalable = false;
|
|
252
|
+
expect(isUrlRequestInit(p)).to.be.true;
|
|
253
|
+
expect(Object.is(instance.urlRequestInit, p)).to.be.true;
|
|
254
|
+
#{expects.compact.join("\n")}
|
|
255
|
+
expect(instance.abortController).#{signalable || create || precreate ? 'not.' : ''}to.be.null;
|
|
256
|
+
#{signalable || create || precreate ? '' : '// '}expect(Object.is(p.ri.signal, instance.abortController!.signal)).to.be.true;
|
|
257
|
+
#{signalable || create || precreate ? '// ' : ''}expect(p.ri.signal).to.be.undefined;
|
|
258
|
+
});
|
|
259
|
+
EOB
|
|
260
|
+
out.push(s)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
[ 'instance', clsName, 'shared' ].each do |source|
|
|
266
|
+
red = (source == 'instance') ? 'error' : 'manual'
|
|
267
|
+
s = <<EOB
|
|
268
|
+
it('Modifier via #{source}', function () {
|
|
269
|
+
const modder = (ur: UrlRequestInit) => { ur.ri.redirect = '#{red}'; };
|
|
270
|
+
const params: #{operation_object[:args_name]} = #{fn}();
|
|
271
|
+
const instance: #{clsName} = new #{clsName}(params);
|
|
272
|
+
#{source}.#{source == 'instance' ? 'setModifier(modder)' : 'modifier = modder'};
|
|
273
|
+
const p: UrlRequestInit = instance.prepare();
|
|
274
|
+
shared.modifier = null;
|
|
275
|
+
expect(isUrlRequestInit(p)).to.be.true;
|
|
276
|
+
expect(Object.is(instance.urlRequestInit, p)).to.be.true;
|
|
277
|
+
expect(p.ri).to.have.property('redirect', '#{red}');
|
|
278
|
+
});
|
|
279
|
+
EOB
|
|
280
|
+
out.push(s)
|
|
281
|
+
end
|
|
282
|
+
count = param_info.select { |p| p[:in] == 'query' }.map { |p| p[:name].size }.max || 1
|
|
283
|
+
ident = 'a' * (count + 1)
|
|
284
|
+
value = 'b' * (count + 1)
|
|
285
|
+
[ 'instance', clsName, 'shared' ].each do |source|
|
|
286
|
+
s = <<EOB
|
|
287
|
+
it('Extra query parameter via #{source}', function () {
|
|
288
|
+
const xt: CallExtras = { query: { #{ident}: '#{value}' }};
|
|
289
|
+
const params: #{operation_object[:args_name]} = #{fn}();
|
|
290
|
+
const instance: #{clsName} = new #{clsName}(params);
|
|
291
|
+
#{source}.#{source == 'instance' ? 'setExtras(xt)' : 'extras = xt'};
|
|
292
|
+
const p: UrlRequestInit = instance.prepare();
|
|
293
|
+
shared.extras = {};
|
|
294
|
+
expect(p.url.includes(`${encodeURIComponent('#{ident}')}=${encodeURIComponent('#{value}')}`)).to.be.true;
|
|
295
|
+
});
|
|
296
|
+
EOB
|
|
297
|
+
out.push(s)
|
|
298
|
+
end
|
|
299
|
+
if has_encoder
|
|
300
|
+
s = <<EOB
|
|
301
|
+
it('No encoder', function () {
|
|
302
|
+
const params: #{operation_object[:args_name]} = #{fn}();
|
|
303
|
+
const r: #{clsName} = new #{clsName}(params);
|
|
304
|
+
const enc = #{clsName}.encoder;
|
|
305
|
+
#{clsName}.encoder = null;
|
|
306
|
+
shared.encoder = null;
|
|
307
|
+
expect(() => r.prepare()).to.throw(#{clsReqError}, 'no request body encoder');
|
|
308
|
+
#{clsName}.encoder = enc;
|
|
309
|
+
});
|
|
310
|
+
EOB
|
|
311
|
+
out.push(s)
|
|
312
|
+
[ 'instance', clsName, 'shared' ].each do |source|
|
|
313
|
+
s = <<EOB
|
|
314
|
+
it('Encoder via #{source}', function () {
|
|
315
|
+
const encBody = JSON.stringify('body from #{source} encoder');
|
|
316
|
+
const enc = (body: any): [EncodedBodyType, string, number, String2String] => { return [encBody, 'application/json', encBody.length, {} as String2String]; }
|
|
317
|
+
const params: #{operation_object[:args_name]} = #{fn}();
|
|
318
|
+
const instance: #{clsName} = new #{clsName}(params);
|
|
319
|
+
const original = #{clsName}.encoder;
|
|
320
|
+
#{clsName}.encoder = null;
|
|
321
|
+
shared.encoder = null;
|
|
322
|
+
#{source}.#{source == 'instance' ? 'setEncoder(enc)' : 'encoder = enc'};
|
|
323
|
+
const uri = instance.prepare();
|
|
324
|
+
expect(uri.ri).to.have.property('body', encBody);
|
|
325
|
+
#{clsName}.encoder = original;
|
|
326
|
+
});
|
|
327
|
+
EOB
|
|
328
|
+
out.push(s)
|
|
329
|
+
end
|
|
330
|
+
[ 'instance', clsName ].each do |source|
|
|
331
|
+
s = <<EOB
|
|
332
|
+
it('Encoder via #{source}, overriding shared', function () {
|
|
333
|
+
const encBody = JSON.stringify('body from #{source} encoder');
|
|
334
|
+
const enc = (body: any): [EncodedBodyType, string, number, String2String] => { return [encBody, 'application/json', encBody.length, {} as String2String]; }
|
|
335
|
+
const sharedEnc = (body: any): [EncodedBodyType, string, number, String2String] => { return ['wrong', 'application/json', 5, {} as String2String]; }
|
|
336
|
+
const params: #{operation_object[:args_name]} = #{fn}();
|
|
337
|
+
const instance: #{clsName} = new #{clsName}(params);
|
|
338
|
+
const original = #{clsName}.encoder;
|
|
339
|
+
#{clsName}.encoder = null;
|
|
340
|
+
shared.encoder = sharedEnc;
|
|
341
|
+
#{source}.#{source == 'instance' ? 'setEncoder(enc)' : 'encoder = enc'};
|
|
342
|
+
const uri = instance.prepare();
|
|
343
|
+
expect(uri.ri).to.have.property('body', encBody);
|
|
344
|
+
#{clsName}.encoder = original;
|
|
345
|
+
shared.encoder = null;
|
|
346
|
+
});
|
|
347
|
+
EOB
|
|
348
|
+
out.push(s)
|
|
349
|
+
end
|
|
350
|
+
s = <<EOB
|
|
351
|
+
it('Encoder via instance, overriding #{clsName} and shared', function () {
|
|
352
|
+
const encBody = JSON.stringify('body from instance encoder');
|
|
353
|
+
const enc = (body: any): [EncodedBodyType, string, number, String2String] => { return [encBody, 'application/json', encBody.length, {} as String2String]; }
|
|
354
|
+
const sharedEnc = (body: any): [EncodedBodyType, string, number, String2String] => { return ['wrong', 'application/json', 5, {} as String2String]; }
|
|
355
|
+
const params: #{operation_object[:args_name]} = #{fn}();
|
|
356
|
+
const instance: #{clsName} = new #{clsName}(params);
|
|
357
|
+
shared.encoder = sharedEnc;
|
|
358
|
+
instance.setEncoder(enc);
|
|
359
|
+
const uri = instance.prepare();
|
|
360
|
+
expect(uri.ri).to.have.property('body', encBody);
|
|
361
|
+
shared.encoder = null;
|
|
362
|
+
});
|
|
363
|
+
EOB
|
|
364
|
+
out.push(s)
|
|
365
|
+
s = <<EOB
|
|
366
|
+
it('Encoder that produces invalid content-type', function () {
|
|
367
|
+
const enc = (body: any): [EncodedBodyType, string, number, String2String] => { return ['encodedBody', 'somevalue', 10, {} as String2String]; }
|
|
368
|
+
const params: #{operation_object[:args_name]} = #{fn}();
|
|
369
|
+
const r: #{clsName} = new #{clsName}(params).setEncoder(enc);
|
|
370
|
+
expect(() => r.prepare()).to.throw(#{clsReqError}, 'encoded content-type');
|
|
371
|
+
});
|
|
372
|
+
EOB
|
|
373
|
+
out.push(s)
|
|
374
|
+
s = <<EOB
|
|
375
|
+
it('Encoder adds other headers', function () {
|
|
376
|
+
const params: #{operation_object[:args_name]} = #{fn}();
|
|
377
|
+
const enc = (body: any): [EncodedBodyType, string, number, String2String] => {
|
|
378
|
+
for (const key in Object.keys(params)) {
|
|
379
|
+
if (key.startsWith('body'))
|
|
380
|
+
return ['encodedBody', #{clsName}.body2mediaTypes.get(key)![0], 11, { headerName: 'headerValue' }];
|
|
381
|
+
}
|
|
382
|
+
return ['encodedBody', #{clsName}.allBodyMediaTypes[0], 11, { headerName: 'headerValue' }];
|
|
383
|
+
}
|
|
384
|
+
const r: #{clsName} = new #{clsName}(params).setEncoder(enc);
|
|
385
|
+
const p: UrlRequestInit = r.prepare();
|
|
386
|
+
expect((p.ri.headers! as Headers).get('headerName')).to.equal('headerValue');
|
|
387
|
+
});
|
|
388
|
+
EOB
|
|
389
|
+
out.push(s)
|
|
390
|
+
s = <<EOB
|
|
391
|
+
it('No body to encode', function () {
|
|
392
|
+
const params: Partial<#{operation_object[:args_name]}> = #{fn}();
|
|
393
|
+
for (const key of Object.keys(params)) {
|
|
394
|
+
if (key.startsWith('body'))
|
|
395
|
+
delete params[key as keyof #{operation_object[:args_name]}];
|
|
396
|
+
}
|
|
397
|
+
const r: #{clsName} = new #{clsName}(params as #{operation_object[:args_name]});
|
|
398
|
+
expect(() => r.prepare({ ignoreMismatches: true })).to.throw(#{clsReqError}, 'no body present');
|
|
399
|
+
});
|
|
400
|
+
EOB
|
|
401
|
+
out.push(s)
|
|
402
|
+
tgt = Gen.x.cfg.fetch('target', 'browser')
|
|
403
|
+
if tgt == 'browser'
|
|
404
|
+
enc_arr = 'new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8])'
|
|
405
|
+
elsif tgt == 'node'
|
|
406
|
+
enc_arr = "Buffer.from('encodedBody')"
|
|
407
|
+
end
|
|
408
|
+
s = <<EOB
|
|
409
|
+
it('Already encoded, same instance', function () {
|
|
410
|
+
const params: #{operation_object[:args_name]} = #{fn}();
|
|
411
|
+
for (const key of Object.keys(params)) {
|
|
412
|
+
if (key.startsWith('body'))
|
|
413
|
+
delete params[key as keyof #{operation_object[:args_name]}];
|
|
414
|
+
}
|
|
415
|
+
params.encoded = #{enc_arr};
|
|
416
|
+
const r: #{clsName} = new #{clsName}(params);
|
|
417
|
+
const p: UrlRequestInit = r.prepare();
|
|
418
|
+
expect(Object.is(params.encoded, p.ri.body)).to.be.true;
|
|
419
|
+
});
|
|
420
|
+
EOB
|
|
421
|
+
out.push(s)
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
operation_object[:fail].each do |fn|
|
|
425
|
+
s = <<EOB
|
|
426
|
+
it('Fail using #{fn}', function () {
|
|
427
|
+
const params: #{operation_object[:args_name]} = #{fn}();
|
|
428
|
+
const r: #{clsName} = new #{clsName}(params);
|
|
429
|
+
expect(() => r.prepare()).to.throw(#{clsReqError}, 'parameters do not conform');
|
|
430
|
+
});
|
|
431
|
+
EOB
|
|
432
|
+
out.push(s)
|
|
433
|
+
end
|
|
434
|
+
operation_object[:typefail].each do |fn|
|
|
435
|
+
s = <<EOB
|
|
436
|
+
it('Fail using #{fn}', function () {
|
|
437
|
+
const params: #{operation_object[:args_name]} = #{fn}();
|
|
438
|
+
const r: #{clsName} = new #{clsName}(params);
|
|
439
|
+
expect(() => r.prepare()).to.throw(#{clsReqError}, 'parameters do not conform');
|
|
440
|
+
});
|
|
441
|
+
EOB
|
|
442
|
+
out.push(s)
|
|
443
|
+
end
|
|
444
|
+
operation_object[:throw].each do |fn|
|
|
445
|
+
s = <<EOB
|
|
446
|
+
it('Fail using #{fn}', function () {
|
|
447
|
+
const params: #{operation_object[:args_name]} = #{fn}();
|
|
448
|
+
const r: #{clsName} = new #{clsName}(params);
|
|
449
|
+
expect(() => r.prepare()).to.throw(Error, 'body');
|
|
450
|
+
});
|
|
451
|
+
EOB
|
|
452
|
+
out.push(s)
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
out.push('});')
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
# Call mock response could be set up to provide desired response.
|
|
459
|
+
Gen.output.join(out)
|
|
460
|
+
%>
|
|
461
|
+
|
|
462
|
+
// Response class tests.
|
|
463
|
+
|
|
464
|
+
const utf8encoder = new TextEncoder();
|
|
465
|
+
|
|
466
|
+
function bodyData(contentType: string, item: any): [Uint8Array, string, number] {
|
|
467
|
+
const content: Uint8Array = utf8encoder.encode(JSON.stringify(item));
|
|
468
|
+
if (contentType == 'application/octet-stream') {
|
|
469
|
+
return [content, contentType, content.length];
|
|
470
|
+
}
|
|
471
|
+
return [content, `${contentType}; charset=utf-8`, content.length];
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
<%=
|
|
475
|
+
out = []
|
|
476
|
+
Gen.doc['paths'].each do |path, path_item_object|
|
|
477
|
+
oos = OpenAPISourceTools::ApiObjects.operation_objects(path_item_object)
|
|
478
|
+
oos.each do |method, operation_object|
|
|
479
|
+
oid = operation_object['operationId']
|
|
480
|
+
next if oid.nil?
|
|
481
|
+
clsName = operation_object[:name]
|
|
482
|
+
clsRespName = operation_object[:resp_name]
|
|
483
|
+
clsRespError = operation_object[:resp_error]
|
|
484
|
+
out.push("describe('#{oid} #{clsRespName}', function () {")
|
|
485
|
+
unless operation_object.key?(:responses)
|
|
486
|
+
warn("#{path} #{method} no response")
|
|
487
|
+
next
|
|
488
|
+
end
|
|
489
|
+
status_order = operation_object[:responses][:status_order]
|
|
490
|
+
code2content = operation_object[:responses][:code2content]
|
|
491
|
+
# Figure out what code to use in case there are ranges or default.
|
|
492
|
+
has_default = false
|
|
493
|
+
ranges = {}
|
|
494
|
+
code2content.keys.each do |code|
|
|
495
|
+
dc = code.downcase
|
|
496
|
+
if dc == 'default'
|
|
497
|
+
has_default = true
|
|
498
|
+
ranges[code] = { key: code, code: dc, low: 100, high: 599 }
|
|
499
|
+
elsif dc.end_with?('xx')
|
|
500
|
+
hundreds = dc[0..-2].to_i * 100
|
|
501
|
+
ranges[code] = { key: code, code: dc, low: hundreds, high: hundreds + 99 }
|
|
502
|
+
elsif dc.end_with?('x')
|
|
503
|
+
tens = dc[0..-1].to_i * 10
|
|
504
|
+
ranges[code] = { key: code, code: dc, low: tens, high: tens + 9 }
|
|
505
|
+
else
|
|
506
|
+
ones = dc.to_i
|
|
507
|
+
ranges[code] = { key: code, code: dc, low: ones, high: ones }
|
|
508
|
+
end
|
|
509
|
+
end
|
|
510
|
+
order = ranges.values.sort do |a, b|
|
|
511
|
+
d = (a[:high] - a[:low]) <=> (b[:high] - b[:low])
|
|
512
|
+
if d.zero?
|
|
513
|
+
d = a[:low] <=> b[:low]
|
|
514
|
+
if d.zero?
|
|
515
|
+
a[:high] <=> b[:high]
|
|
516
|
+
else
|
|
517
|
+
d
|
|
518
|
+
end
|
|
519
|
+
else
|
|
520
|
+
d
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
used_status_codes = Set.new
|
|
524
|
+
order.each do |info|
|
|
525
|
+
if info[:low] == info[:high]
|
|
526
|
+
used_status_codes.add(info[:low])
|
|
527
|
+
code2content[info[:key]][:status_code] = info[:low]
|
|
528
|
+
else
|
|
529
|
+
(info[:low]..info[:high]).each do |k|
|
|
530
|
+
next if used_status_codes.member?(k)
|
|
531
|
+
used_status_codes.add(k)
|
|
532
|
+
code2content[info[:key]][:status_code] = k
|
|
533
|
+
break
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
end
|
|
537
|
+
status_vars = code2content.values.map { |info| info[:status_code] }.sort!
|
|
538
|
+
has_599 = false
|
|
539
|
+
status_order.each do |key|
|
|
540
|
+
info = code2content[key]
|
|
541
|
+
status_code = info[:status_code]
|
|
542
|
+
has_599 = true if status_code == 599
|
|
543
|
+
[ nil, false, true ].each do |throwNon2xx|
|
|
544
|
+
[ nil, false, true ].each do |throwBodyMismatch|
|
|
545
|
+
[ :none, :fail, :pass, :typefail ].each do |body_kind|
|
|
546
|
+
# No body for this status code.
|
|
547
|
+
if info[:content].empty?
|
|
548
|
+
if body_kind == :fail || body_kind == :typefail
|
|
549
|
+
if throwNon2xx == true && status_code < 200 || 300 <= status_code
|
|
550
|
+
tst = <<EOF
|
|
551
|
+
try {
|
|
552
|
+
await #{clsRespName}.obtain(args);
|
|
553
|
+
expect(true).to.be.false;
|
|
554
|
+
}
|
|
555
|
+
catch (err) {
|
|
556
|
+
expect(err instanceof #{clsRespError}).to.be.true;
|
|
557
|
+
expect((err as Error).message).to.include('non-2xx');
|
|
558
|
+
}
|
|
559
|
+
EOF
|
|
560
|
+
elsif throwBodyMismatch == true
|
|
561
|
+
tst = <<EOF
|
|
562
|
+
try {
|
|
563
|
+
await #{clsRespName}.obtain(args);
|
|
564
|
+
expect(true).to.be.false;
|
|
565
|
+
}
|
|
566
|
+
catch (err) {
|
|
567
|
+
expect(err instanceof #{clsRespError}).to.be.true;
|
|
568
|
+
expect((err as Error).message).to.include('body mismatch');
|
|
569
|
+
}
|
|
570
|
+
EOF
|
|
571
|
+
else
|
|
572
|
+
tst = <<EOF
|
|
573
|
+
const out: #{clsRespName} = await #{clsRespName}.obtain(args);
|
|
574
|
+
expect(Object.is(out.response, resp)).to.be.true;
|
|
575
|
+
expect(Object.is(out.caller, args.caller)).to.be.true;
|
|
576
|
+
#{status_vars.map { |v| "expect(out.c#{v}).to.be.undefined;" }.join("\n")}
|
|
577
|
+
EOF
|
|
578
|
+
end
|
|
579
|
+
s = <<EOF
|
|
580
|
+
it('#{throwNon2xx ? 'throw non-2xx ' : ''}#{throwBodyMismatch ? 'throwBodyMismatch ' : ''}#{status_code} #{body_kind.to_s}', async function () {
|
|
581
|
+
const [bd, ct, cl] = bodyData('application/json', () => { return { any: 'fails' }; });
|
|
582
|
+
const hdrs = new Headers();
|
|
583
|
+
hdrs.set('content-type', ct);
|
|
584
|
+
hdrs.set('content-length', cl.toString());
|
|
585
|
+
const resp: Response = new Response(bd, {
|
|
586
|
+
status: #{status_code},
|
|
587
|
+
headers: hdrs
|
|
588
|
+
});
|
|
589
|
+
const args: #{clsRespName}Args = {
|
|
590
|
+
#{throwNon2xx.nil? ? '// ' : ''} #{throwNon2xx ? 'throwNon2xx: true,' : 'throwNon2xx: false,'}
|
|
591
|
+
#{throwBodyMismatch.nil? ? '// ' : ''} #{throwBodyMismatch ? 'throwBodyMismatch: true,' : 'throwBodyMismatch: false,'}
|
|
592
|
+
caller: { faker: 'caller' } as any as #{clsName},
|
|
593
|
+
response: resp
|
|
594
|
+
};
|
|
595
|
+
#{tst}
|
|
596
|
+
});
|
|
597
|
+
EOF
|
|
598
|
+
out.push(s) unless status_code == 204 # Response constructor throws.
|
|
599
|
+
elsif body_kind == :none
|
|
600
|
+
if throwNon2xx == true && status_code < 200 || 300 <= status_code
|
|
601
|
+
tst = <<EOF
|
|
602
|
+
try {
|
|
603
|
+
await #{clsRespName}.obtain(args);
|
|
604
|
+
expect(true).to.be.false;
|
|
605
|
+
}
|
|
606
|
+
catch (err) {
|
|
607
|
+
expect(err instanceof #{clsRespError}).to.be.true;
|
|
608
|
+
expect((err as Error).message).to.include('non-2xx');
|
|
609
|
+
}
|
|
610
|
+
EOF
|
|
611
|
+
else
|
|
612
|
+
tst = <<EOF
|
|
613
|
+
const out: #{clsRespName} = await #{clsRespName}.obtain(args);
|
|
614
|
+
expect(Object.is(out.response, resp)).to.be.true;
|
|
615
|
+
expect(Object.is(out.caller, args.caller)).to.be.true;
|
|
616
|
+
expect(out).to.have.property('#{info[:status_var]}').not.to.be.undefined;
|
|
617
|
+
#{status_vars.reject { |v| "c#{v}" == info[:status_var] }.map { |v| "expect(out.c#{v}).to.be.undefined;" }.join("\n")}
|
|
618
|
+
EOF
|
|
619
|
+
end
|
|
620
|
+
s = <<EOF
|
|
621
|
+
it('#{throwNon2xx ? 'throw non-2xx ' : ''}#{throwBodyMismatch ? 'throwBodyMismatch ' : ''}#{status_code} #{body_kind.to_s}', async function () {
|
|
622
|
+
const resp: Response = new Response(null, {
|
|
623
|
+
status: #{status_code}
|
|
624
|
+
});
|
|
625
|
+
const args: #{clsRespName}Args = {
|
|
626
|
+
#{throwNon2xx.nil? ? '// ' : ''}throwNon2xx: #{throwNon2xx ? 'true' : 'false'},
|
|
627
|
+
#{throwBodyMismatch.nil? ? '// ' : ''}throwBodyMismatch: #{throwBodyMismatch ? 'true' : 'false'},
|
|
628
|
+
caller: { fake: 'caller' } as any as #{clsName},
|
|
629
|
+
response: resp
|
|
630
|
+
};
|
|
631
|
+
#{tst}
|
|
632
|
+
});
|
|
633
|
+
EOF
|
|
634
|
+
out.push(s)
|
|
635
|
+
end
|
|
636
|
+
next
|
|
637
|
+
end
|
|
638
|
+
# Loop over all content types for this status code.
|
|
639
|
+
info[:content].keys.sort!.map do |ct|
|
|
640
|
+
if ct != 'application/json'
|
|
641
|
+
# Support for other types requires user-provided encoding function.
|
|
642
|
+
out.push("// No function to encode body to content-type #{ct}")
|
|
643
|
+
next
|
|
644
|
+
end
|
|
645
|
+
mr = info[:content][ct] # Has mto, rt (response type).
|
|
646
|
+
# rt[:type] is mto[:decoded] is deferenced mto['schema']
|
|
647
|
+
# Bad and good body in response.body.
|
|
648
|
+
# If need bad and no fail maker, skip.
|
|
649
|
+
# If need good and no pass maker, skip.
|
|
650
|
+
schema = mr[:rt][:type]
|
|
651
|
+
next if body_kind != :none && schema[body_kind].empty?
|
|
652
|
+
if body_kind == :pass
|
|
653
|
+
body_chk = <<EOF
|
|
654
|
+
expect(out.mismatch).to.equal(0);
|
|
655
|
+
expect(out).to.have.property('#{mr[:rt][:name]}');
|
|
656
|
+
expect(#{mr[:rt][:type][:is]}(out.#{mr[:rt][:name]})).to.be.true;
|
|
657
|
+
expect(out).to.have.property('#{info[:status_var]}');
|
|
658
|
+
expect(out.#{info[:status_var]}).to.have.property('#{mr[:rt][:name]}');
|
|
659
|
+
expect(Object.is(out.#{info[:status_var]}!.#{mr[:rt][:name]}, out.#{mr[:rt][:name]})).to.be.true;
|
|
660
|
+
EOF
|
|
661
|
+
else # Fail, typefail, and none all fail is-check.
|
|
662
|
+
body_chk = <<EOF
|
|
663
|
+
expect(out.mismatch).to.equal(2);
|
|
664
|
+
expect(out).not.to.have.property('#{info[:status_var]}');
|
|
665
|
+
EOF
|
|
666
|
+
end
|
|
667
|
+
# ct is content-type, pair with loader, ties with encoded body
|
|
668
|
+
# response.body gets a ReadableStream of the encoded body.
|
|
669
|
+
# response.headers gets content-type from ct.
|
|
670
|
+
# response.status is status_code as number if not already. Check default.
|
|
671
|
+
# Keep track of has_500. Ensure at least one is not ok.
|
|
672
|
+
# args.loader gets the loader based on ct.
|
|
673
|
+
|
|
674
|
+
# Default loaders must be covered. They will fail body checks.
|
|
675
|
+
# Then there is custom loader set in multiple levels.
|
|
676
|
+
|
|
677
|
+
# Does json() decode uft-8? One would hope so...
|
|
678
|
+
# Otherwise read blob, decode if content-encoding, proceed.
|
|
679
|
+
if throwNon2xx == true && (status_code < 200 || 300 <= status_code)
|
|
680
|
+
tst = <<EOF
|
|
681
|
+
try {
|
|
682
|
+
await #{clsRespName}.obtain(args);
|
|
683
|
+
expect(true).to.be.false;
|
|
684
|
+
}
|
|
685
|
+
catch (err) {
|
|
686
|
+
expect(err instanceof #{clsRespError}).to.be.true;
|
|
687
|
+
expect((err as Error).message).to.include('non-2xx');
|
|
688
|
+
}
|
|
689
|
+
EOF
|
|
690
|
+
elsif throwBodyMismatch == true && body_kind != :pass
|
|
691
|
+
tst = <<EOF
|
|
692
|
+
try {
|
|
693
|
+
await #{clsRespName}.obtain(args);
|
|
694
|
+
expect(true).to.be.false;
|
|
695
|
+
}
|
|
696
|
+
catch (err) {
|
|
697
|
+
expect(err instanceof #{clsRespError}).to.be.true;
|
|
698
|
+
expect((err as Error).message).to.include('body mismatch');
|
|
699
|
+
}
|
|
700
|
+
EOF
|
|
701
|
+
elsif body_kind != :pass
|
|
702
|
+
tst = <<EOF
|
|
703
|
+
const out: #{clsRespName} = await #{clsRespName}.obtain(args);
|
|
704
|
+
expect(Object.is(out.response, resp)).to.be.true;
|
|
705
|
+
expect(Object.is(out.caller, args.caller)).to.be.true;
|
|
706
|
+
#{status_vars.map { |v| "expect(out.c#{v}).to.be.undefined;" }.join("\n")}
|
|
707
|
+
EOF
|
|
708
|
+
else
|
|
709
|
+
tst = <<EOF
|
|
710
|
+
const out: #{clsRespName} = await #{clsRespName}.obtain(args);
|
|
711
|
+
expect(Object.is(out.response, resp)).to.be.true;
|
|
712
|
+
expect(Object.is(out.caller, args.caller)).to.be.true;
|
|
713
|
+
expect(out).to.have.property('#{info[:status_var]}').not.to.be.undefined;
|
|
714
|
+
#{status_vars.reject { |v| "c#{v}" == info[:status_var] }.map { |v| "expect(out.c#{v}).to.be.undefined;" }.join("\n")}
|
|
715
|
+
#{body_chk}
|
|
716
|
+
EOF
|
|
717
|
+
end
|
|
718
|
+
if body_kind == :none
|
|
719
|
+
prep = <<EOF
|
|
720
|
+
const [bd, ct, cl] = bodyData('#{ct}', {});
|
|
721
|
+
const hdrs = new Headers();
|
|
722
|
+
hdrs.set('content-type', ct);
|
|
723
|
+
hdrs.set('content-length', cl.toString());
|
|
724
|
+
const resp: Response = new Response(null, {
|
|
725
|
+
status: #{status_code},
|
|
726
|
+
headers: hdrs
|
|
727
|
+
});
|
|
728
|
+
EOF
|
|
729
|
+
else
|
|
730
|
+
mak = schema[body_kind].first
|
|
731
|
+
unless mak.nil?
|
|
732
|
+
prep = <<EOF
|
|
733
|
+
const [bd, ct, cl] = bodyData('#{ct}', #{mak}());
|
|
734
|
+
const hdrs = new Headers();
|
|
735
|
+
hdrs.set('content-type', ct);
|
|
736
|
+
hdrs.set('content-length', cl.toString());
|
|
737
|
+
const resp: Response = new Response(bd, {
|
|
738
|
+
status: #{status_code},
|
|
739
|
+
headers: hdrs
|
|
740
|
+
});
|
|
741
|
+
EOF
|
|
742
|
+
else
|
|
743
|
+
prep = nil
|
|
744
|
+
end
|
|
745
|
+
end
|
|
746
|
+
unless prep.nil?
|
|
747
|
+
s = <<EOF
|
|
748
|
+
it('#{throwNon2xx ? 'throw non-2xx ' : ''}#{throwBodyMismatch ? 'throwBodyMismatch ' : ''}#{status_code} #{body_kind.to_s}', async function () {
|
|
749
|
+
#{prep}
|
|
750
|
+
const args: #{clsRespName}Args = {
|
|
751
|
+
#{throwNon2xx.nil? ? '// ' : ''} #{throwNon2xx ? 'throwNon2xx: true,' : 'throwNon2xx: false,'}
|
|
752
|
+
#{throwBodyMismatch.nil? ? '// ' : ''} #{throwBodyMismatch ? 'throwBodyMismatch: true,' : 'throwBodyMismatch: false,'}
|
|
753
|
+
caller: { fake: 'caller' } as any as #{clsName},
|
|
754
|
+
response: resp
|
|
755
|
+
};
|
|
756
|
+
#{tst}
|
|
757
|
+
});
|
|
758
|
+
EOF
|
|
759
|
+
out.push(s)
|
|
760
|
+
end
|
|
761
|
+
end
|
|
762
|
+
end
|
|
763
|
+
end
|
|
764
|
+
end
|
|
765
|
+
next if info[:content].empty?
|
|
766
|
+
# Add content-type invalid header value test here. Otherwise like pass.
|
|
767
|
+
mr = info[:content][info[:content].keys.first]
|
|
768
|
+
schema = mr[:rt][:type]
|
|
769
|
+
ct = 'a' * (1 + info[:content].keys.map(&:size).max);
|
|
770
|
+
mak = schema[:pass].first
|
|
771
|
+
unless mak.nil?
|
|
772
|
+
s = <<EOF
|
|
773
|
+
it('#{status_code} unexpected header', async function () {
|
|
774
|
+
const [bd, ct, cl] = bodyData('#{ct}', #{mak}());
|
|
775
|
+
const hdrs = new Headers();
|
|
776
|
+
hdrs.set('content-type', ct);
|
|
777
|
+
hdrs.set('content-length', cl.toString());
|
|
778
|
+
const resp: Response = new Response(bd, {
|
|
779
|
+
status: #{status_code},
|
|
780
|
+
headers: hdrs
|
|
781
|
+
});
|
|
782
|
+
const args: #{clsRespName}Args = {
|
|
783
|
+
throwNon2xx: false,
|
|
784
|
+
throwBodyMismatch: false,
|
|
785
|
+
caller: { fake: 'caller' } as any as #{clsName},
|
|
786
|
+
response: resp
|
|
787
|
+
};
|
|
788
|
+
try {
|
|
789
|
+
await #{clsRespName}.obtain(args);
|
|
790
|
+
expect(true).to.be.false;
|
|
791
|
+
}
|
|
792
|
+
catch (err) {
|
|
793
|
+
expect(err instanceof #{clsRespError}).to.be.true;
|
|
794
|
+
expect((err as Error).message).to.include('unexpected content-type');
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
EOF
|
|
798
|
+
out.push(s)
|
|
799
|
+
end
|
|
800
|
+
end
|
|
801
|
+
unless has_default || has_599
|
|
802
|
+
[ nil, false, true ].each do |throwNon2xx|
|
|
803
|
+
status_code = 599
|
|
804
|
+
if throwNon2xx
|
|
805
|
+
tst = <<EOF
|
|
806
|
+
try {
|
|
807
|
+
await #{clsRespName}.obtain(args);
|
|
808
|
+
expect(true).to.be.false;
|
|
809
|
+
}
|
|
810
|
+
catch (err) {
|
|
811
|
+
expect(err instanceof #{clsRespError}).to.be.true;
|
|
812
|
+
expect((err as Error).message).to.include('non-2xx');
|
|
813
|
+
}
|
|
814
|
+
EOF
|
|
815
|
+
else
|
|
816
|
+
tst = <<EOF
|
|
817
|
+
const out: #{clsRespName} = await #{clsRespName}.obtain(args);
|
|
818
|
+
expect(Object.is(out.response, resp)).to.be.true;
|
|
819
|
+
expect(Object.is(out.caller, args.caller)).to.be.true;
|
|
820
|
+
#{status_vars.map { |v| "expect(out.c#{v}).to.be.undefined;" }.join("\n")}
|
|
821
|
+
expect(out).to.have.property('mismatch', 2);
|
|
822
|
+
EOF
|
|
823
|
+
end
|
|
824
|
+
s = <<EOF
|
|
825
|
+
it('#{throwNon2xx ? 'throw non-2xx ' : (throwNon2xx.nil? ? '' : 'no-throw ')}#{status_code}', async function () {
|
|
826
|
+
const resp: Response = new Response(null, {
|
|
827
|
+
status: #{status_code}
|
|
828
|
+
});
|
|
829
|
+
const args: #{clsRespName}Args = {
|
|
830
|
+
#{throwNon2xx.nil? ? '// ' : ''}throwNon2xx: #{throwNon2xx ? 'true' : 'false'},
|
|
831
|
+
caller: { fake: 'caller' } as any as #{clsName},
|
|
832
|
+
response: resp
|
|
833
|
+
};
|
|
834
|
+
#{tst}
|
|
835
|
+
});
|
|
836
|
+
EOF
|
|
837
|
+
out.push(s)
|
|
838
|
+
end
|
|
839
|
+
end
|
|
840
|
+
out.push('});')
|
|
841
|
+
end
|
|
842
|
+
end
|
|
843
|
+
Gen.output.join(out)
|
|
844
|
+
%>
|