galbe 0.12.0 → 0.12.2

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 (42) hide show
  1. package/bin/commands/generate/code/openapi.parser.ts +2 -2
  2. package/package.json +6 -1
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -35
  4. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -23
  5. package/.github/workflows/build_test.yml +0 -17
  6. package/.github/workflows/deploy_website.yml +0 -20
  7. package/.github/workflows/release.yml +0 -39
  8. package/.prettierrc +0 -10
  9. package/bun.lock +0 -904
  10. package/bunfig.toml +0 -2
  11. package/docs/CONTRIBUTING.md +0 -105
  12. package/docs/cli.md +0 -343
  13. package/docs/configuration.md +0 -80
  14. package/docs/context.md +0 -104
  15. package/docs/error-handler.md +0 -54
  16. package/docs/getting-started.md +0 -164
  17. package/docs/handler.md +0 -119
  18. package/docs/hooks.md +0 -90
  19. package/docs/plugins.md +0 -146
  20. package/docs/router.md +0 -10
  21. package/docs/routes.md +0 -133
  22. package/docs/schemas.md +0 -327
  23. package/test/hooks.test.ts +0 -200
  24. package/test/parser.test.ts +0 -1358
  25. package/test/plugins.test.ts +0 -239
  26. package/test/requests.test.ts +0 -917
  27. package/test/resources/image.png +0 -0
  28. package/test/resources/object.badSyntax.json +0 -8
  29. package/test/resources/object.json +0 -8
  30. package/test/resources/object.missing.json +0 -6
  31. package/test/resources/static/chameleon.png +0 -0
  32. package/test/resources/static/index.html +0 -13
  33. package/test/resources/static/sub/index.html +0 -13
  34. package/test/resources/static/sub/other.html +0 -13
  35. package/test/resources/test.route.comment.ts +0 -58
  36. package/test/resources/test.route.empty.ts +0 -9
  37. package/test/responses.test.ts +0 -483
  38. package/test/routeFiles.test.ts +0 -239
  39. package/test/router.test.ts +0 -231
  40. package/test/test.utils.ts +0 -109
  41. package/test/types.test.ts +0 -909
  42. package/tsconfig.json +0 -23
@@ -1,917 +0,0 @@
1
- import { expect, test, describe, beforeAll } from 'bun:test'
2
- import { Galbe, $T } from '../src'
3
- import { formdata, type Case, fileHash, handleBody, isAsyncIterator } from './test.utils'
4
-
5
- const port = 7358
6
- const METHODS = ['get', 'post', 'put', 'patch', 'delete', 'options', 'head']
7
-
8
- describe('requests', () => {
9
- beforeAll(async () => {
10
- const galbe = new Galbe()
11
-
12
- galbe.static('/www', './test/resources/static', {})
13
-
14
- galbe.get('/test', () => {})
15
- galbe.post('/test', () => {})
16
- galbe.put('/test', () => {})
17
- galbe.patch('/test', () => {})
18
- galbe.delete('/test', () => {})
19
- galbe.options('/test', () => {})
20
- galbe.head('/test', () => {})
21
-
22
- galbe.get('/headers', ctx => {
23
- return ctx.headers
24
- })
25
- galbe.get(
26
- '/headers/schema',
27
- {
28
- headers: {
29
- string: $T.string(),
30
- zero: $T.number(),
31
- number: $T.number(),
32
- 'neg-number': $T.number(),
33
- float: $T.number(),
34
- integer: $T.integer(),
35
- 'boolean-true': $T.boolean(),
36
- 'boolean-false': $T.boolean(),
37
- },
38
- },
39
- ctx => {
40
- return ctx.headers
41
- }
42
- )
43
-
44
- galbe.get('/params/:param1/separate/:param2/:param3', ctx => {
45
- return ctx.params
46
- })
47
- galbe.get(
48
- '/params/schema/:p1/:p2/:p3/:p4',
49
- {
50
- params: {
51
- p1: $T.string(),
52
- p2: $T.number(),
53
- p3: $T.integer(),
54
- p4: $T.boolean(),
55
- },
56
- },
57
- ctx => {
58
- return ctx.params
59
- }
60
- )
61
-
62
- galbe.get('/query/params', ctx => {
63
- return ctx.query
64
- })
65
- galbe.get(
66
- '/query/params/schema',
67
- {
68
- query: {
69
- p1: $T.string(),
70
- p2: $T.number(),
71
- p3: $T.boolean(),
72
- p4: $T.union([$T.number(), $T.boolean()]),
73
- p5: $T.optional($T.string()),
74
- p6: $T.array($T.union([$T.string()])),
75
- },
76
- },
77
- ctx => {
78
- return ctx.query
79
- }
80
- )
81
-
82
- galbe.post('/none', handleBody)
83
- galbe.post('/null', { body: $T.null() }, handleBody)
84
- galbe.post('/ba', { body: { byteArray: $T.byteArray() } }, handleBody)
85
- galbe.post('/bool', { body: { json: $T.boolean() } }, handleBody)
86
- galbe.post('/num', { body: { json: $T.number() } }, handleBody)
87
- galbe.post('/jsonStr', { body: { json: $T.string() } }, handleBody)
88
- galbe.post('/str', { body: { text: $T.string() } }, handleBody)
89
- galbe.post('/txtBool', { body: { text: $T.boolean() } }, handleBody)
90
- galbe.post('/txtNum', { body: { text: $T.number() } }, handleBody)
91
- galbe.post('/arr', { body: { json: $T.array() } }, handleBody)
92
- galbe.post('/obj', { body: { json: $T.object($T.any()) } }, handleBody)
93
-
94
- galbe.post('/form', { body: { urlForm: $T.object() } }, handleBody)
95
- galbe.post('/mp', { body: { multipart: $T.multipartForm() } }, handleBody)
96
-
97
- galbe.post('/stream/ba', { body: { byteArray: $T.stream($T.byteArray()) } }, handleBody)
98
- galbe.post('/stream/str', { body: { text: $T.stream($T.string()) } }, handleBody)
99
- galbe.post('/stream/form', { body: { urlForm: $T.stream($T.object()) } }, async ctx => {
100
- let resp: Record<string, any> = {}
101
- for await (const [k, v] of ctx.body) {
102
- if (k in resp) {
103
- resp[k] = Array.isArray(resp[k]) ? [...resp[k], v] : [resp[k], v]
104
- } else resp[k] = v
105
- }
106
- return { type: 'object', content: resp }
107
- })
108
- galbe.post('/stream/mp', { body: { multipart: $T.stream($T.multipartForm()) } }, async ctx => {
109
- let resp: Record<string, any> = {}
110
- for await (const field of ctx.body) {
111
- let k = field?.headers.name
112
- if (k && k in resp) {
113
- resp[k].content = Array.isArray(resp[k]?.content)
114
- ? [...resp[k].content, field?.content]
115
- : [resp[k]?.content, field?.content]
116
- } else resp[k] = field
117
- }
118
- return { type: 'object', content: resp }
119
- })
120
-
121
- let schema_jsonFile = {
122
- string: $T.string(),
123
- number: $T.number(),
124
- bool: $T.boolean(),
125
- arrayStr: $T.array($T.string()),
126
- arrayNumber: $T.array($T.number()),
127
- arrayBool: $T.array($T.boolean()),
128
- }
129
-
130
- galbe.post(
131
- '/mp/file',
132
- { body: { multipart: $T.multipartForm({ imgFile: $T.byteArray(), jsonFile: $T.object(schema_jsonFile) }) } },
133
- handleBody
134
- )
135
- galbe.post(
136
- '/mp/stream/file',
137
- {
138
- body: {
139
- multipart: $T.stream($T.multipartForm({ imgFile: $T.byteArray(), jsonFile: $T.object(schema_jsonFile) })),
140
- },
141
- },
142
- async ctx => {
143
- if (isAsyncIterator(ctx.body)) {
144
- const chunks: any[] = []
145
- for await (const chunk of ctx.body) {
146
- const c = { ...chunk } as any
147
- if (chunk.content instanceof Uint8Array) c.content = await fileHash(chunk.content)
148
- chunks.push(c)
149
- }
150
- return { type: 'AsyncIterator', content: chunks }
151
- } else {
152
- return { type: null, content: 'error' }
153
- }
154
- }
155
- )
156
- galbe.post('/ba/file', { body: { byteArray: $T.byteArray() } }, async ctx => {
157
- if (ctx?.body instanceof Uint8Array) {
158
- return { type: 'byteArray', content: await fileHash(ctx.body) }
159
- } else {
160
- return { type: null, content: 'error' }
161
- }
162
- })
163
- galbe.post('/ba/stream/file', { body: { byteArray: $T.stream($T.byteArray()) } }, async ctx => {
164
- if (isAsyncIterator(ctx.body)) {
165
- let bytes = new Uint8Array()
166
- for await (const b of ctx.body) {
167
- bytes = new Uint8Array([...bytes, ...b])
168
- }
169
- return { type: 'AsyncIterator', content: await fileHash(bytes) }
170
- } else {
171
- return { type: null, content: 'error' }
172
- }
173
- })
174
-
175
- await galbe.listen(port)
176
- })
177
-
178
- test('static directory', async () => {
179
- const testCases = [
180
- {
181
- path: `http://localhost:${port}/www`,
182
- file: 'test/resources/static/index.html',
183
- type: 'text/html;charset=utf-8',
184
- },
185
- {
186
- path: `http://localhost:${port}/www/index.html`,
187
- file: 'test/resources/static/index.html',
188
- type: 'text/html;charset=utf-8',
189
- },
190
- {
191
- path: `http://localhost:${port}/www/sub`,
192
- file: 'test/resources/static/sub/index.html',
193
- type: 'text/html;charset=utf-8',
194
- },
195
- {
196
- path: `http://localhost:${port}/www/sub/index.html`,
197
- file: 'test/resources/static/sub/index.html',
198
- type: 'text/html;charset=utf-8',
199
- },
200
- {
201
- path: `http://localhost:${port}/www/sub/other`,
202
- file: 'test/resources/static/sub/other.html',
203
- type: 'text/html;charset=utf-8',
204
- },
205
- {
206
- path: `http://localhost:${port}/www/chameleon.png`,
207
- file: 'test/resources/static/chameleon.png',
208
- type: 'image/png',
209
- },
210
- ]
211
- for (const tc of testCases) {
212
- let resp = await fetch(tc.path)
213
- expect(resp.ok).toBeTrue()
214
- const expected = await Bun.file(tc.file).text()
215
- expect(await resp.text()).toBe(expected)
216
- expect(resp.headers.get('content-type')).toBe(tc.type)
217
- }
218
- })
219
-
220
- test('methods, empty calls', async () => {
221
- for (let method of METHODS) {
222
- let resp = await fetch(`http://localhost:${port}/test`, { method: method.toUpperCase() })
223
- expect(resp.ok).toBeTrue()
224
- }
225
- })
226
-
227
- test('methods, path not found', async () => {
228
- for (let method of METHODS) {
229
- let resp = await fetch(`http://localhost:${port}/does/not/exist`, { method: method.toUpperCase() })
230
- expect(resp.status).toBe(404)
231
- }
232
- })
233
-
234
- test('headers', async () => {
235
- const headers = {
236
- String: 'hello mom',
237
- Number: '42',
238
- Boolean: 'true',
239
- }
240
- const expected = Object.fromEntries(Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v]))
241
-
242
- let resp = await fetch(`http://localhost:${port}/headers`, { headers })
243
- let body = await resp.json()
244
-
245
- expect(body).toMatchObject(expected)
246
- })
247
-
248
- test('path params', async () => {
249
- const expected = {
250
- param1: 'one',
251
- param2: '2',
252
- param3: 'true',
253
- }
254
- let resp = await fetch(`http://localhost:${port}/params/one/separate/2/true`)
255
- let body = await resp.json()
256
-
257
- expect(body).toEqual(expected)
258
- })
259
-
260
- test('query params', async () => {
261
- const expected = {
262
- param1: 'one',
263
- param2: '2',
264
- param3: 'true',
265
- }
266
- let resp = await fetch(`http://localhost:${port}/query/params?param1=one&param2=2&param3=true`)
267
- let body = await resp.json()
268
-
269
- expect(body).toEqual(expected)
270
- })
271
-
272
- test('no body, no header', async () => {
273
- const cases: Case[] = [
274
- { body: null, type: undefined, schema: 'none', expected: { status: 200, resp: null } },
275
- { body: null, type: undefined, schema: 'null', expected: { status: 200, resp: null } },
276
- { body: null, type: undefined, schema: 'ba', expected: { status: 400 } },
277
- { body: null, type: undefined, schema: 'bool', expected: { status: 400 } },
278
- { body: null, type: undefined, schema: 'num', expected: { status: 400 } },
279
- { body: null, type: undefined, schema: 'str', expected: { status: 400 } },
280
- { body: null, type: undefined, schema: 'arr', expected: { status: 400 } },
281
- { body: null, type: undefined, schema: 'obj', expected: { status: 400 } },
282
- { body: null, type: undefined, schema: 'form', expected: { status: 400 } },
283
- { body: null, type: undefined, schema: 'mp', expected: { status: 400 } },
284
- { body: null, type: undefined, schema: 'stream/ba', expected: { status: 400 } },
285
- { body: null, type: undefined, schema: 'stream/str', expected: { status: 400 } },
286
- { body: null, type: undefined, schema: 'stream/form', expected: { status: 400 } },
287
- { body: null, type: undefined, schema: 'stream/mp', expected: { status: 400 } },
288
- ]
289
- for (let { body, type, schema, expected } of cases) {
290
- let resp = await fetch(`http://localhost:${port}/${schema}`, {
291
- method: 'POST',
292
- body,
293
- headers: {
294
- ...(type ? { 'content-type': type } : {}),
295
- },
296
- })
297
-
298
- let respBody = (await resp.json()) as { type: string; content: any }
299
-
300
- expect(resp.status).toBe(expected.status)
301
- if (expected.type) expect(respBody.type).toEqual(expected.type)
302
- if (respBody.type === 'object' && expected.resp === null) expect(respBody.content).toBeNull
303
- else expect(respBody.content).toEqual(expected.resp)
304
- }
305
- })
306
-
307
- test('no body, application/octet-stream', async () => {
308
- const contentType = 'application/octet-stream'
309
- const cases: Case[] = [
310
- { body: null, type: contentType, schema: 'none', expected: { status: 200, type: 'byteArray', resp: '' } },
311
- { body: null, type: contentType, schema: 'ba', expected: { status: 200, type: 'byteArray', resp: '' } },
312
- { body: null, type: contentType, schema: 'bool', expected: { status: 400 } },
313
- { body: null, type: contentType, schema: 'num', expected: { status: 400 } },
314
- { body: null, type: contentType, schema: 'str', expected: { status: 400 } },
315
- { body: null, type: contentType, schema: 'arr', expected: { status: 400 } },
316
- { body: null, type: contentType, schema: 'obj', expected: { status: 400 } },
317
- { body: null, type: contentType, schema: 'form', expected: { status: 400 } },
318
- { body: null, type: contentType, schema: 'mp', expected: { status: 400 } },
319
- {
320
- body: null,
321
- type: contentType,
322
- schema: 'stream/ba',
323
- expected: { status: 200, type: 'ReadableStream', resp: '' },
324
- },
325
- { body: null, type: contentType, schema: 'stream/str', expected: { status: 400 } },
326
- { body: null, type: contentType, schema: 'stream/form', expected: { status: 400 } },
327
- { body: null, type: contentType, schema: 'stream/mp', expected: { status: 400 } },
328
- ]
329
-
330
- for (let { body, type, schema, expected } of cases) {
331
- let resp = await fetch(`http://localhost:${port}/${schema}`, {
332
- method: 'POST',
333
- body,
334
- headers: {
335
- ...(type ? { 'content-type': type } : {}),
336
- },
337
- })
338
-
339
- let respBody = (await resp.json()) as { type: string; content: any }
340
-
341
- expect(resp.status).toBe(expected.status)
342
- if (expected.type) expect(respBody.type).toEqual(expected.type)
343
- if (respBody.type === 'object' && expected.resp === null) expect(respBody.content).toBeNull
344
- else expect(respBody.content).toEqual(expected.resp)
345
- }
346
- })
347
-
348
- test('no body, application/json', async () => {
349
- const contentType = 'application/json'
350
- const cases: Case[] = [
351
- { body: null, type: contentType, schema: 'none', expected: { status: 200, resp: null } },
352
- { body: null, type: contentType, schema: 'ba', expected: { status: 400 } },
353
- { body: null, type: contentType, schema: 'bool', expected: { status: 400 } },
354
- { body: null, type: contentType, schema: 'num', expected: { status: 400 } },
355
- { body: null, type: contentType, schema: 'str', expected: { status: 400 } },
356
- { body: null, type: contentType, schema: 'arr', expected: { status: 400 } },
357
- { body: null, type: contentType, schema: 'obj', expected: { status: 200, resp: null } },
358
- { body: null, type: contentType, schema: 'form', expected: { status: 400 } },
359
- { body: null, type: contentType, schema: 'mp', expected: { status: 400 } },
360
- { body: null, type: contentType, schema: 'stream/ba', expected: { status: 400 } },
361
- { body: null, type: contentType, schema: 'stream/str', expected: { status: 400 } },
362
- { body: null, type: contentType, schema: 'stream/form', expected: { status: 400 } },
363
- { body: null, type: contentType, schema: 'stream/mp', expected: { status: 400 } },
364
- ]
365
-
366
- for (let { body, type, schema, expected } of cases) {
367
- let resp = await fetch(`http://localhost:${port}/${schema}`, {
368
- method: 'POST',
369
- body,
370
- headers: {
371
- ...(type ? { 'content-type': type } : {}),
372
- },
373
- })
374
-
375
- let respBody = (await resp.json()) as { type: string; content: any }
376
-
377
- expect(resp.status).toBe(expected.status)
378
- if (expected.type) expect(respBody.type).toEqual(expected.type)
379
- if (respBody.type === 'object' && expected.resp === null) expect(respBody.content).toBeNull
380
- else expect(respBody.content).toEqual(expected.resp)
381
- }
382
- })
383
-
384
- test('no body, text/plain', async () => {
385
- const contentType = 'text/plain'
386
- const cases: Case[] = [
387
- { body: null, type: contentType, schema: 'none', expected: { status: 200, resp: '' } },
388
- { body: null, type: contentType, schema: 'ba', expected: { status: 400 } },
389
- { body: null, type: contentType, schema: 'bool', expected: { status: 400 } },
390
- { body: null, type: contentType, schema: 'num', expected: { status: 400 } },
391
- { body: null, type: contentType, schema: 'str', expected: { status: 200, resp: '' } },
392
- { body: null, type: contentType, schema: 'arr', expected: { status: 400 } },
393
- { body: null, type: contentType, schema: 'obj', expected: { status: 400 } },
394
- { body: null, type: contentType, schema: 'form', expected: { status: 400 } },
395
- { body: null, type: contentType, schema: 'mp', expected: { status: 400 } },
396
- { body: null, type: contentType, schema: 'stream/ba', expected: { status: 400 } },
397
- {
398
- body: null,
399
- type: contentType,
400
- schema: 'stream/str',
401
- expected: { status: 200, type: 'ReadableStream', resp: '' },
402
- },
403
- { body: null, type: contentType, schema: 'stream/form', expected: { status: 400 } },
404
- { body: null, type: contentType, schema: 'stream/mp', expected: { status: 400 } },
405
- ]
406
-
407
- for (let { body, type, schema, expected } of cases) {
408
- let resp = await fetch(`http://localhost:${port}/${schema}`, {
409
- method: 'POST',
410
- body,
411
- headers: {
412
- ...(type ? { 'content-type': type } : {}),
413
- },
414
- })
415
-
416
- let respBody = (await resp.json()) as { type: string; content: any }
417
-
418
- expect(resp.status).toBe(expected.status)
419
- if (expected.type) expect(respBody.type).toEqual(expected.type)
420
- if (respBody.type === 'object' && expected.resp === null) expect(respBody.content).toBeNull
421
- else expect(respBody.content).toEqual(expected.resp)
422
- }
423
- })
424
-
425
- test('no body, application/x-www-form-urlencoded', async () => {
426
- const contentType = 'application/x-www-form-urlencoded'
427
- const cases: Case[] = [
428
- { body: null, type: contentType, schema: 'none', expected: { status: 200, resp: {} } },
429
- { body: null, type: contentType, schema: 'ba', expected: { status: 400 } },
430
- { body: null, type: contentType, schema: 'bool', expected: { status: 400 } },
431
- { body: null, type: contentType, schema: 'num', expected: { status: 400 } },
432
- { body: null, type: contentType, schema: 'str', expected: { status: 400 } },
433
- { body: null, type: contentType, schema: 'arr', expected: { status: 400 } },
434
- { body: null, type: contentType, schema: 'obj', expected: { status: 400 } },
435
- { body: null, type: contentType, schema: 'form', expected: { status: 200, resp: {} } },
436
- { body: null, type: contentType, schema: 'mp', expected: { status: 400 } },
437
- { body: null, type: contentType, schema: 'stream/ba', expected: { status: 400 } },
438
- { body: null, type: contentType, schema: 'stream/str', expected: { status: 400 } },
439
- { body: null, type: contentType, schema: 'stream/form', expected: { status: 200, resp: {} } },
440
- { body: null, type: contentType, schema: 'stream/mp', expected: { status: 400 } },
441
- ]
442
-
443
- for (let { body, type, schema, expected } of cases) {
444
- let resp = await fetch(`http://localhost:${port}/${schema}`, {
445
- method: 'POST',
446
- body,
447
- headers: {
448
- ...(type ? { 'content-type': type } : {}),
449
- },
450
- })
451
-
452
- let respBody = (await resp.json()) as { type: string; content: any }
453
-
454
- expect(resp.status).toBe(expected.status)
455
- if (expected.type) expect(respBody.type).toEqual(expected.type)
456
- if (respBody.type === 'object' && expected.resp === null) expect(respBody.content).toBeNull
457
- else expect(respBody.content).toEqual(expected.resp)
458
- }
459
- })
460
-
461
- test('no body, multipart/form-data', async () => {
462
- const contentType = 'multipart/form-data'
463
- const cases: Case[] = [
464
- { body: null, type: contentType, schema: 'none', expected: { status: 200, resp: {} } },
465
- { body: null, type: contentType, schema: 'ba', expected: { status: 400 } },
466
- { body: null, type: contentType, schema: 'bool', expected: { status: 400 } },
467
- { body: null, type: contentType, schema: 'num', expected: { status: 400 } },
468
- { body: null, type: contentType, schema: 'str', expected: { status: 400 } },
469
- { body: null, type: contentType, schema: 'arr', expected: { status: 400 } },
470
- { body: null, type: contentType, schema: 'obj', expected: { status: 400 } },
471
- { body: null, type: contentType, schema: 'form', expected: { status: 400 } },
472
- { body: null, type: contentType, schema: 'mp', expected: { status: 200, resp: {} } },
473
- { body: null, type: contentType, schema: 'stream/ba', expected: { status: 400 } },
474
- { body: null, type: contentType, schema: 'stream/str', expected: { status: 400 } },
475
- { body: null, type: contentType, schema: 'stream/form', expected: { status: 400 } },
476
- {
477
- body: null,
478
- type: contentType,
479
- schema: 'stream/mp',
480
- expected: { status: 200, resp: { undefined: { headers: {} } } },
481
- },
482
- ]
483
-
484
- for (let { body, type, schema, expected } of cases) {
485
- let resp = await fetch(`http://localhost:${port}/${schema}`, {
486
- method: 'POST',
487
- body,
488
- headers: {
489
- ...(type ? { 'content-type': type } : {}),
490
- },
491
- })
492
-
493
- let respBody = (await resp.json()) as { type: string; content: any }
494
-
495
- expect(resp.status).toBe(expected.status)
496
- if (expected.type) expect(respBody.type).toEqual(expected.type)
497
- if (respBody.type === 'object' && expected.resp === null) expect(respBody.content).toBeNull
498
- else expect(respBody.content).toEqual(expected.resp)
499
- }
500
- })
501
-
502
- test('body, no header', async () => {
503
- const cases: Case[] = [
504
- {
505
- body: 'test',
506
- schema: 'none',
507
- expected: { status: 200, type: 'AsyncIterator', resp: Object.fromEntries([116, 101, 115, 116].entries()) },
508
- },
509
- { body: 'test', schema: 'null', expected: { status: 400 } },
510
- { body: 'test', schema: 'ba', expected: { status: 400 } },
511
- { body: 'true', schema: 'bool', expected: { status: 400 } },
512
- { body: 'false', schema: 'bool', expected: { status: 400 } },
513
- { body: 'test', schema: 'bool', expected: { status: 400 } },
514
- { body: '0', schema: 'num', expected: { status: 400 } },
515
- { body: '42', schema: 'num', expected: { status: 400 } },
516
- { body: '-8000', schema: 'num', expected: { status: 400 } },
517
- { body: '3.14', schema: 'num', expected: { status: 400 } },
518
- { body: 'test', schema: 'num', expected: { status: 400 } },
519
- { body: 'test', schema: 'str', expected: { status: 400 } },
520
- { body: '42', schema: 'str', expected: { status: 400 } },
521
- { body: 'false', schema: 'str', expected: { status: 400 } },
522
- { body: '[]', schema: 'arr', expected: { status: 400 } },
523
- {
524
- body: '["a", 42, true, null, {"foo": "bar"}]',
525
- schema: 'arr',
526
- expected: { status: 400 },
527
- },
528
- { body: 'test', schema: 'arr', expected: { status: 400 } },
529
- { body: '{}', schema: 'obj', expected: { status: 400 } },
530
- { body: '{"foo": "bar"}', schema: 'obj', expected: { status: 400 } },
531
- { body: 'test', schema: 'obj', expected: { status: 400 } },
532
- { body: 'test', schema: 'form', expected: { status: 400 } },
533
- { body: 'test', schema: 'mp', expected: { status: 400 } },
534
- {
535
- body: 'test',
536
- schema: 'stream/ba',
537
- expected: { status: 400 },
538
- },
539
- { body: 'test', schema: 'stream/str', expected: { status: 400 } },
540
- { body: 'test', schema: 'stream/form', expected: { status: 400 } },
541
- { body: 'test', schema: 'stream/mp', expected: { status: 400 } },
542
- ]
543
-
544
- for (let { body, type, schema, expected } of cases) {
545
- let resp = await fetch(`http://localhost:${port}/${schema}`, {
546
- method: 'POST',
547
- body,
548
- headers: {
549
- ...(type ? { 'content-type': type } : {}),
550
- },
551
- })
552
-
553
- let respBody = (await resp.json()) as { type: string; content: any }
554
-
555
- expect(resp.status).toBe(expected.status)
556
- if (expected.type) expect(respBody.type).toEqual(expected.type)
557
- if (respBody.type === 'object' && expected.resp === null) expect(respBody.content).toBeNull
558
- // else if (respBody.type === 'AsyncIterator') expect(new Uint8Array(Object.values(respBody.content))).toEqual(expected.resp)
559
- else expect(respBody.content).toEqual(expected.resp)
560
- }
561
- })
562
-
563
- test('body, application/octet-stream', async () => {
564
- const contentType = 'application/octet-stream'
565
- const cases: Case[] = [
566
- { body: 'test', type: contentType, schema: 'none', expected: { status: 200, type: 'byteArray', resp: 'test' } },
567
- { body: 'test', type: contentType, schema: 'ba', expected: { status: 200, type: 'byteArray', resp: 'test' } },
568
- { body: 'true', type: contentType, schema: 'bool', expected: { status: 400 } },
569
- { body: 'false', type: contentType, schema: 'bool', expected: { status: 400 } },
570
- { body: 'test', type: contentType, schema: 'bool', expected: { status: 400 } },
571
- { body: '0', type: contentType, schema: 'num', expected: { status: 400 } },
572
- { body: '42', type: contentType, schema: 'num', expected: { status: 400 } },
573
- { body: '-8000', type: contentType, schema: 'num', expected: { status: 400 } },
574
- { body: '3.14', type: contentType, schema: 'num', expected: { status: 400 } },
575
- { body: 'test', type: contentType, schema: 'num', expected: { status: 400 } },
576
- { body: 'test', type: contentType, schema: 'str', expected: { status: 400 } },
577
- { body: '42', type: contentType, schema: 'str', expected: { status: 400 } },
578
- { body: 'false', type: contentType, schema: 'str', expected: { status: 400 } },
579
- { body: '[]', type: contentType, schema: 'arr', expected: { status: 400 } },
580
- {
581
- body: '["a", 42, true, null, {"foo": "bar"}]',
582
- type: contentType,
583
- schema: 'arr',
584
- expected: { status: 400 },
585
- },
586
- { body: 'test', type: contentType, schema: 'arr', expected: { status: 400 } },
587
- { body: '{}', type: contentType, schema: 'obj', expected: { status: 400 } },
588
- {
589
- body: '{"foo": "bar"}',
590
- type: contentType,
591
- schema: 'obj',
592
- expected: { status: 400 },
593
- },
594
- { body: 'test', type: contentType, schema: 'obj', expected: { status: 400 } },
595
- { body: 'test', type: contentType, schema: 'form', expected: { status: 400 } },
596
- { body: 'test', type: contentType, schema: 'mp', expected: { status: 400 } },
597
- {
598
- body: 'test',
599
- type: contentType,
600
- schema: 'stream/ba',
601
- expected: {
602
- status: 200,
603
- type: 'AsyncIterator',
604
- resp: Object.fromEntries(new Uint8Array([116, 101, 115, 116]).entries()),
605
- },
606
- },
607
- {
608
- body: 'test',
609
- type: contentType,
610
- schema: 'stream/str',
611
- expected: { status: 400 },
612
- },
613
- { body: 'test', type: contentType, schema: 'stream/form', expected: { status: 400 } },
614
- { body: 'test', type: contentType, schema: 'stream/mp', expected: { status: 400 } },
615
- ]
616
-
617
- for (let { body, type, schema, expected } of cases) {
618
- let resp = await fetch(`http://localhost:${port}/${schema}`, {
619
- method: 'POST',
620
- body,
621
- headers: {
622
- ...(type ? { 'content-type': type } : {}),
623
- },
624
- })
625
-
626
- let respBody = (await resp.json()) as { type: string; content: any }
627
-
628
- expect(resp.status).toBe(expected.status)
629
- if (expected.type) expect(respBody.type).toEqual(expected.type)
630
- if (respBody.type === 'object' && expected.resp === null) expect(respBody.content).toBeNull
631
- else expect(respBody.content).toEqual(expected.resp)
632
- }
633
- })
634
-
635
- test('body, application/json', async () => {
636
- const contentType = 'application/json'
637
- const cases: Case[] = [
638
- {
639
- body: '{"foo":"bar"}',
640
- type: contentType,
641
- schema: 'none',
642
- expected: { status: 200, type: 'object', resp: { foo: 'bar' } },
643
- },
644
- { body: 'test', type: contentType, schema: 'none', expected: { status: 400 } },
645
- { body: 'test', type: contentType, schema: 'ba', expected: { status: 400 } },
646
- { body: 'true', type: contentType, schema: 'bool', expected: { status: 200, type: 'boolean', resp: true } },
647
- { body: 'false', type: contentType, schema: 'bool', expected: { status: 200, type: 'boolean', resp: false } },
648
- { body: 'test', type: contentType, schema: 'bool', expected: { status: 400 } },
649
- { body: '0', type: contentType, schema: 'bool', expected: { status: 400 } },
650
- { body: '0', type: contentType, schema: 'num', expected: { status: 200, type: 'number', resp: 0 } },
651
- { body: '42', type: contentType, schema: 'num', expected: { status: 200, type: 'number', resp: 42 } },
652
- { body: '-8000', type: contentType, schema: 'num', expected: { status: 200, type: 'number', resp: -8000 } },
653
- { body: '3.14', type: contentType, schema: 'num', expected: { status: 200, type: 'number', resp: 3.14 } },
654
- { body: 'test', type: contentType, schema: 'num', expected: { status: 400 } },
655
- { body: '"test"', type: contentType, schema: 'str', expected: { status: 400 } },
656
- { body: '"42"', type: contentType, schema: 'str', expected: { status: 400 } },
657
- { body: '"false"', type: contentType, schema: 'str', expected: { status: 400 } },
658
- { body: 'test', type: contentType, schema: 'str', expected: { status: 400 } },
659
- { body: '"test"', type: contentType, schema: 'jsonStr', expected: { status: 200, type: 'string', resp: 'test' } },
660
- { body: '"42"', type: contentType, schema: 'jsonStr', expected: { status: 200, type: 'string', resp: '42' } },
661
- {
662
- body: '"false"',
663
- type: contentType,
664
- schema: 'jsonStr',
665
- expected: { status: 200, type: 'string', resp: 'false' },
666
- },
667
- { body: 'test', type: contentType, schema: 'jsonStr', expected: { status: 400 } },
668
- { body: '[]', type: contentType, schema: 'arr', expected: { status: 200, type: 'array', resp: [] } },
669
- {
670
- body: '["a", 42, true, null, {"foo": "bar"}]',
671
- type: contentType,
672
- schema: 'arr',
673
- expected: { status: 200, type: 'array', resp: ['a', 42, true, null, { foo: 'bar' }] },
674
- },
675
- { body: 'test', type: contentType, schema: 'arr', expected: { status: 400 } },
676
- { body: '{}', type: contentType, schema: 'obj', expected: { status: 200, type: 'object', resp: {} } },
677
- {
678
- body: '{"foo": "bar"}',
679
- type: contentType,
680
- schema: 'obj',
681
- expected: { status: 200, type: 'object', resp: { foo: 'bar' } },
682
- },
683
- { body: 'test', type: contentType, schema: 'obj', expected: { status: 400 } },
684
- { body: 'test', type: contentType, schema: 'form', expected: { status: 400 } },
685
- { body: 'test', type: contentType, schema: 'mp', expected: { status: 400 } },
686
- {
687
- body: 'test',
688
- type: contentType,
689
- schema: 'stream/ba',
690
- expected: { status: 400 },
691
- },
692
- {
693
- body: 'test',
694
- type: contentType,
695
- schema: 'stream/str',
696
- expected: { status: 400 },
697
- },
698
- { body: 'test', type: contentType, schema: 'stream/form', expected: { status: 400 } },
699
- { body: 'test', type: contentType, schema: 'stream/mp', expected: { status: 400 } },
700
- ]
701
-
702
- for (let { body, type, schema, expected } of cases) {
703
- let resp = await fetch(`http://localhost:${port}/${schema}`, {
704
- method: 'POST',
705
- body,
706
- headers: {
707
- ...(type ? { 'content-type': type } : {}),
708
- },
709
- })
710
-
711
- let respBody = (await resp.json()) as { type: string; content: any }
712
-
713
- expect(resp.status).toBe(expected.status)
714
- if (expected.type) expect(respBody.type).toEqual(expected.type)
715
- if (respBody.type === 'object' && expected.resp === null) expect(respBody.content).toBeNull
716
- else expect(respBody.content).toEqual(expected.resp)
717
- }
718
- })
719
-
720
- test('body, text/plain', async () => {
721
- const contentType = 'text/plain'
722
- const cases: Case[] = [
723
- { body: 'test', type: contentType, schema: 'none', expected: { status: 200, type: 'string', resp: 'test' } },
724
- { body: 'test', type: contentType, schema: 'ba', expected: { status: 400 } },
725
- { body: 'true', type: contentType, schema: 'bool', expected: { status: 400 } },
726
- { body: 'false', type: contentType, schema: 'bool', expected: { status: 400 } },
727
- { body: 'test', type: contentType, schema: 'bool', expected: { status: 400 } },
728
- { body: '0', type: contentType, schema: 'bool', expected: { status: 400 } },
729
- { body: 'true', type: contentType, schema: 'txtBool', expected: { status: 200, type: 'boolean', resp: true } },
730
- { body: 'false', type: contentType, schema: 'txtBool', expected: { status: 200, type: 'boolean', resp: false } },
731
- { body: 'test', type: contentType, schema: 'txtBool', expected: { status: 400 } },
732
- { body: '0', type: contentType, schema: 'txtBool', expected: { status: 400 } },
733
- { body: '0', type: contentType, schema: 'num', expected: { status: 400 } },
734
- { body: '42', type: contentType, schema: 'num', expected: { status: 400 } },
735
- { body: '-8000', type: contentType, schema: 'num', expected: { status: 400 } },
736
- { body: '3.14', type: contentType, schema: 'num', expected: { status: 400 } },
737
- { body: 'test', type: contentType, schema: 'num', expected: { status: 400 } }, //
738
- { body: '0', type: contentType, schema: 'txtNum', expected: { status: 200, type: 'number', resp: 0 } },
739
- { body: '42', type: contentType, schema: 'txtNum', expected: { status: 200, type: 'number', resp: 42 } },
740
- { body: '-8000', type: contentType, schema: 'txtNum', expected: { status: 200, type: 'number', resp: -8000 } },
741
- { body: '3.14', type: contentType, schema: 'txtNum', expected: { status: 200, type: 'number', resp: 3.14 } },
742
- { body: 'test', type: contentType, schema: 'txtNum', expected: { status: 400 } },
743
- { body: 'test', type: contentType, schema: 'str', expected: { status: 200, type: 'string', resp: 'test' } },
744
- { body: '"test"', type: contentType, schema: 'str', expected: { status: 200, type: 'string', resp: '"test"' } },
745
- { body: '42', type: contentType, schema: 'str', expected: { status: 200, type: 'string', resp: '42' } },
746
- { body: 'false', type: contentType, schema: 'str', expected: { status: 200, type: 'string', resp: 'false' } },
747
- { body: '[]', type: contentType, schema: 'arr', expected: { status: 400 } },
748
- {
749
- body: '["a", 42, true, null, {"foo": "bar"}]',
750
- type: contentType,
751
- schema: 'arr',
752
- expected: { status: 400 },
753
- },
754
- { body: 'test', type: contentType, schema: 'arr', expected: { status: 400 } },
755
- { body: '{}', type: contentType, schema: 'obj', expected: { status: 400 } },
756
- {
757
- body: '{"foo": "bar"}',
758
- type: contentType,
759
- schema: 'obj',
760
- expected: { status: 400 },
761
- },
762
- { body: 'test', type: contentType, schema: 'obj', expected: { status: 400 } },
763
- { body: 'test', type: contentType, schema: 'form', expected: { status: 400 } },
764
- { body: 'test', type: contentType, schema: 'mp', expected: { status: 400 } },
765
- {
766
- body: 'test',
767
- type: contentType,
768
- schema: 'stream/ba',
769
- expected: {
770
- status: 400,
771
- },
772
- },
773
- {
774
- body: 'test',
775
- type: contentType,
776
- schema: 'stream/str',
777
- expected: { status: 200, type: 'AsyncIterator', resp: 'test' },
778
- },
779
- { body: 'test', type: contentType, schema: 'stream/form', expected: { status: 400 } },
780
- { body: 'test', type: contentType, schema: 'stream/mp', expected: { status: 400 } },
781
- ]
782
-
783
- for (let { body, type, schema, expected } of cases) {
784
- let resp = await fetch(`http://localhost:${port}/${schema}`, {
785
- method: 'POST',
786
- body,
787
- headers: {
788
- ...(type ? { 'content-type': type } : {}),
789
- },
790
- })
791
-
792
- let respBody = (await resp.json()) as { type: string; content: any }
793
-
794
- expect(resp.status).toBe(expected.status)
795
- if (expected.type) expect(respBody.type).toEqual(expected.type)
796
- if (respBody.type === 'object' && expected.resp === null) expect(respBody.content).toBeNull
797
- else expect(respBody.content).toEqual(expected.resp)
798
- }
799
- })
800
-
801
- test('body, application/x-www-form-urlencoded', async () => {
802
- const contentType = 'application/x-www-form-urlencoded'
803
- const strBody = 'string=text&number=42&boolean=true&encoded%3D=%3D'
804
- const objBody = { string: 'text', number: '42', boolean: 'true', 'encoded=': '=' }
805
- const cases: Case[] = [
806
- { body: strBody, type: contentType, schema: 'none', expected: { status: 200, type: 'object', resp: objBody } },
807
- { body: strBody, type: contentType, schema: 'ba', expected: { status: 400 } },
808
- { body: strBody, type: contentType, schema: 'bool', expected: { status: 400 } },
809
- { body: strBody, type: contentType, schema: 'num', expected: { status: 400 } },
810
- { body: strBody, type: contentType, schema: 'str', expected: { status: 400 } },
811
- { body: strBody, type: contentType, schema: 'arr', expected: { status: 400 } },
812
- { body: strBody, type: contentType, schema: 'obj', expected: { status: 400 } },
813
- { body: strBody, type: contentType, schema: 'form', expected: { status: 200, type: 'object', resp: objBody } },
814
- { body: strBody, type: contentType, schema: 'mp', expected: { status: 400 } },
815
- {
816
- body: strBody,
817
- type: contentType,
818
- schema: 'stream/ba',
819
- expected: {
820
- status: 400,
821
- },
822
- },
823
- {
824
- body: strBody,
825
- type: contentType,
826
- schema: 'stream/str',
827
- expected: { status: 400 },
828
- },
829
- {
830
- body: strBody,
831
- type: contentType,
832
- schema: 'stream/form',
833
- expected: { status: 200, type: 'object', resp: objBody },
834
- },
835
- { body: strBody, type: contentType, schema: 'stream/mp', expected: { status: 400 } },
836
- ]
837
-
838
- for (let { body, type, schema, expected } of cases) {
839
- let resp = await fetch(`http://localhost:${port}/${schema}`, {
840
- method: 'POST',
841
- body,
842
- headers: {
843
- ...(type ? { 'content-type': type } : {}),
844
- },
845
- })
846
-
847
- let respBody = (await resp.json()) as { type: string; content: any }
848
-
849
- expect(resp.status).toBe(expected.status)
850
- if (expected.type) expect(respBody.type).toEqual(expected.type)
851
- if (respBody.type === 'object' && expected.resp === null) expect(respBody.content).toBeNull
852
- else expect(respBody.content).toEqual(expected.resp)
853
- }
854
- })
855
-
856
- test('body, multipart/form-data', async () => {
857
- const objBody = { string: 'text', number: '42', boolean: 'true' }
858
- const objResp = {
859
- string: {
860
- content: 'text',
861
- headers: {
862
- name: 'string',
863
- },
864
- },
865
- number: {
866
- content: '42',
867
- headers: {
868
- name: 'number',
869
- },
870
- },
871
- boolean: {
872
- content: 'true',
873
- headers: {
874
- name: 'boolean',
875
- },
876
- },
877
- }
878
- const form = formdata(objBody)
879
-
880
- const cases: Case[] = [
881
- { body: form, schema: 'none', expected: { status: 200, type: 'object', resp: objResp } },
882
- { body: form, schema: 'ba', expected: { status: 400 } },
883
- { body: form, schema: 'bool', expected: { status: 400 } },
884
- { body: form, schema: 'num', expected: { status: 400 } },
885
- { body: form, schema: 'str', expected: { status: 400 } },
886
- { body: form, schema: 'arr', expected: { status: 400 } },
887
- { body: form, schema: 'obj', expected: { status: 400 } },
888
- { body: form, schema: 'form', expected: { status: 400 } },
889
- { body: form, schema: 'mp', expected: { status: 200, type: 'object', resp: objResp } },
890
- {
891
- body: form,
892
- schema: 'stream/str',
893
- expected: { status: 400 },
894
- },
895
- { body: form, schema: 'stream/form', expected: { status: 400 } },
896
- { body: form, schema: 'stream/mp', expected: { status: 200, type: 'object', resp: objResp } },
897
- ]
898
-
899
- for (let { body, type, schema, expected } of cases) {
900
- let resp = await fetch(`http://localhost:${port}/${schema}`, {
901
- method: 'POST',
902
- body,
903
- headers: {
904
- ...(type ? { 'content-type': type } : {}),
905
- },
906
- })
907
-
908
- let respBody = (await resp.json()) as { type: string; content: any }
909
-
910
- expect(resp.status).toBe(expected.status)
911
- if (expected.type) expect(respBody.type).toEqual(expected.type)
912
- if (respBody.type === 'object' && expected.resp === null) expect(respBody.content).toBeNull
913
- else if (expected.resp instanceof RegExp) expect(respBody.content).toMatch(expected.resp)
914
- else expect(respBody.content).toEqual(expected.resp)
915
- }
916
- })
917
- })