@fluojs/testing 1.0.0-beta.1 → 1.0.0-beta.3

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.
@@ -210,18 +210,75 @@ export class HttpAdapterPortabilityHarness {
210
210
  await closeSilently(app);
211
211
  }
212
212
  }
213
- async assertExcludesRawBodyForMultipart() {
213
+ async assertPreservesExactRawBodyBytesForByteSensitivePayloads() {
214
214
  let _initProto3, _initClass3;
215
+ let _WebhookController2;
216
+ class WebhookController {
217
+ static {
218
+ ({
219
+ e: [_initProto3],
220
+ c: [_WebhookController2, _initClass3]
221
+ } = _applyDecs(this, [Controller('/webhooks')], [[Post('/bytes'), 2, "handleBytes"]]));
222
+ }
223
+ constructor() {
224
+ _initProto3(this);
225
+ }
226
+ handleBytes(_input, context) {
227
+ return {
228
+ rawBytes: Array.from(context.request.rawBody ?? new Uint8Array())
229
+ };
230
+ }
231
+ static {
232
+ _initClass3();
233
+ }
234
+ }
235
+ class AppModule {}
236
+ defineModule(AppModule, {
237
+ controllers: [_WebhookController2]
238
+ });
239
+ const port = await findAvailablePort();
240
+ const app = await this.options.bootstrap(AppModule, {
241
+ cors: false,
242
+ port,
243
+ rawBody: true
244
+ });
245
+ await this.options.prepareExactRawBodyByteTest?.(app);
246
+ await app.listen();
247
+ try {
248
+ const payload = Uint8Array.from([0xe9, 0x41]);
249
+ const contentType = this.options.exactRawBodyByteContentType ?? 'text/plain; charset=latin1';
250
+ const response = await fetch(`http://127.0.0.1:${String(port)}/webhooks/bytes`, {
251
+ body: payload,
252
+ headers: {
253
+ 'content-type': contentType
254
+ },
255
+ method: 'POST'
256
+ });
257
+ if (response.status !== 201) {
258
+ throw new Error(`${this.options.name} adapter changed byte-sensitive rawBody response status semantics.`);
259
+ }
260
+ const body = await response.json();
261
+ if (JSON.stringify(body) !== JSON.stringify({
262
+ rawBytes: Array.from(payload)
263
+ })) {
264
+ throw new Error(`${this.options.name} adapter changed exact-byte rawBody semantics.`);
265
+ }
266
+ } finally {
267
+ await closeSilently(app);
268
+ }
269
+ }
270
+ async assertExcludesRawBodyForMultipart() {
271
+ let _initProto4, _initClass4;
215
272
  let _UploadController;
216
273
  class UploadController {
217
274
  static {
218
275
  ({
219
- e: [_initProto3],
220
- c: [_UploadController, _initClass3]
276
+ e: [_initProto4],
277
+ c: [_UploadController, _initClass4]
221
278
  } = _applyDecs(this, [Controller('/uploads')], [[Post('/'), 2, "upload"]]));
222
279
  }
223
280
  constructor() {
224
- _initProto3(this);
281
+ _initProto4(this);
225
282
  }
226
283
  upload(_input, context) {
227
284
  return {
@@ -231,7 +288,7 @@ export class HttpAdapterPortabilityHarness {
231
288
  };
232
289
  }
233
290
  static {
234
- _initClass3();
291
+ _initClass4();
235
292
  }
236
293
  }
237
294
  class AppModule {}
@@ -272,18 +329,76 @@ export class HttpAdapterPortabilityHarness {
272
329
  await closeSilently(app);
273
330
  }
274
331
  }
332
+ async assertDefaultsMultipartTotalLimitToMaxBodySize() {
333
+ let _initProto5, _initClass5;
334
+ let _UploadController2;
335
+ class UploadController {
336
+ static {
337
+ ({
338
+ e: [_initProto5],
339
+ c: [_UploadController2, _initClass5]
340
+ } = _applyDecs(this, [Controller('/uploads')], [[Post('/'), 2, "upload"]]));
341
+ }
342
+ constructor() {
343
+ _initProto5(this);
344
+ }
345
+ upload(_input, context) {
346
+ return {
347
+ body: context.request.body,
348
+ fileCount: context.request.files?.length ?? 0
349
+ };
350
+ }
351
+ static {
352
+ _initClass5();
353
+ }
354
+ }
355
+ class AppModule {}
356
+ defineModule(AppModule, {
357
+ controllers: [_UploadController2]
358
+ });
359
+ const port = await findAvailablePort();
360
+ const app = await this.options.bootstrap(AppModule, {
361
+ cors: false,
362
+ maxBodySize: 8,
363
+ multipart: {
364
+ maxFileSize: 1024
365
+ },
366
+ port
367
+ });
368
+ await app.listen();
369
+ try {
370
+ const form = new FormData();
371
+ form.set('name', 'Ada');
372
+ form.set('payload', new Blob(['12345678'], {
373
+ type: 'text/plain'
374
+ }), 'payload.txt');
375
+ const response = await fetch(`http://127.0.0.1:${String(port)}/uploads`, {
376
+ body: form,
377
+ method: 'POST'
378
+ });
379
+ if (response.status !== 413) {
380
+ throw new Error(`${this.options.name} adapter did not default multipart.maxTotalSize to maxBodySize.`);
381
+ }
382
+ const body = await response.json();
383
+ if (typeof body !== 'object' || body === null || body.error?.code !== 'PAYLOAD_TOO_LARGE') {
384
+ throw new Error(`${this.options.name} adapter changed multipart limit error semantics.`);
385
+ }
386
+ } finally {
387
+ await closeSilently(app);
388
+ }
389
+ }
275
390
  async assertSupportsSseStreaming() {
276
- let _initProto4, _initClass4;
391
+ let _initProto6, _initClass6;
277
392
  let _EventsController;
278
393
  class EventsController {
279
394
  static {
280
395
  ({
281
- e: [_initProto4],
282
- c: [_EventsController, _initClass4]
396
+ e: [_initProto6],
397
+ c: [_EventsController, _initClass6]
283
398
  } = _applyDecs(this, [Controller('/events')], [[Get('/'), 2, "stream"]]));
284
399
  }
285
400
  constructor() {
286
- _initProto4(this);
401
+ _initProto6(this);
287
402
  }
288
403
  stream(_input, context) {
289
404
  const stream = new SseResponse(context);
@@ -294,13 +409,13 @@ export class HttpAdapterPortabilityHarness {
294
409
  event: 'ready',
295
410
  id: 'evt-1'
296
411
  });
297
- setTimeout(() => {
412
+ queueMicrotask(() => {
298
413
  stream.close();
299
- }, 10);
414
+ });
300
415
  return stream;
301
416
  }
302
417
  static {
303
- _initClass4();
418
+ _initClass6();
304
419
  }
305
420
  }
306
421
  class AppModule {}
@@ -334,8 +449,72 @@ export class HttpAdapterPortabilityHarness {
334
449
  await closeSilently(app);
335
450
  }
336
451
  }
452
+
453
+ /**
454
+ * Asserts that adapter stream backpressure waiters settle when the response
455
+ * closes before a `drain` event is emitted.
456
+ */
457
+ async assertSettlesStreamDrainWaitOnClose() {
458
+ let _initProto7, _initClass7;
459
+ const adapterName = this.options.name;
460
+ let resolveDrainWait;
461
+ const drainWaitSettled = new Promise(resolve => {
462
+ resolveDrainWait = resolve;
463
+ });
464
+ let _EventsController2;
465
+ class EventsController {
466
+ static {
467
+ ({
468
+ e: [_initProto7],
469
+ c: [_EventsController2, _initClass7]
470
+ } = _applyDecs(this, [Controller('/events')], [[Get('/'), 2, "stream"]]));
471
+ }
472
+ constructor() {
473
+ _initProto7(this);
474
+ }
475
+ async stream(_input, context) {
476
+ const stream = new SseResponse(context);
477
+ const responseStream = context.response.stream;
478
+ if (!responseStream?.waitForDrain) {
479
+ throw new Error(`${adapterName} adapter did not expose response.stream.waitForDrain().`);
480
+ }
481
+ const drainWait = responseStream.waitForDrain();
482
+ stream.close();
483
+ await drainWait;
484
+ resolveDrainWait();
485
+ return stream;
486
+ }
487
+ static {
488
+ _initClass7();
489
+ }
490
+ }
491
+ class AppModule {}
492
+ defineModule(AppModule, {
493
+ controllers: [_EventsController2]
494
+ });
495
+ const port = await findAvailablePort();
496
+ const app = await this.options.bootstrap(AppModule, {
497
+ cors: false,
498
+ port
499
+ });
500
+ await app.listen();
501
+ try {
502
+ const response = await fetch(`http://127.0.0.1:${String(port)}/events`, {
503
+ headers: {
504
+ accept: 'text/event-stream'
505
+ }
506
+ });
507
+ if (response.status !== 200) {
508
+ throw new Error(`${this.options.name} adapter changed closed stream response status semantics.`);
509
+ }
510
+ await response.text();
511
+ await withTimeout(drainWaitSettled, 2_000, `${this.options.name} adapter left response.stream.waitForDrain() pending after close.`);
512
+ } finally {
513
+ await closeSilently(app);
514
+ }
515
+ }
337
516
  async assertReportsConfiguredHostInStartupLogs() {
338
- let _initProto5, _initClass5;
517
+ let _initProto8, _initClass8;
339
518
  const loggerEvents = [];
340
519
  const logger = {
341
520
  debug() {},
@@ -351,12 +530,12 @@ export class HttpAdapterPortabilityHarness {
351
530
  class HealthController {
352
531
  static {
353
532
  ({
354
- e: [_initProto5],
355
- c: [_HealthController, _initClass5]
533
+ e: [_initProto8],
534
+ c: [_HealthController, _initClass8]
356
535
  } = _applyDecs(this, [Controller('/health')], [[Get('/'), 2, "getHealth"]]));
357
536
  }
358
537
  constructor() {
359
- _initProto5(this);
538
+ _initProto8(this);
360
539
  }
361
540
  getHealth() {
362
541
  return {
@@ -364,7 +543,7 @@ export class HttpAdapterPortabilityHarness {
364
543
  };
365
544
  }
366
545
  static {
367
- _initClass5();
546
+ _initClass8();
368
547
  }
369
548
  }
370
549
  class AppModule {}
@@ -398,7 +577,7 @@ export class HttpAdapterPortabilityHarness {
398
577
  }
399
578
  }
400
579
  async assertReportsHttpsStartupUrl(https) {
401
- let _initProto6, _initClass6;
580
+ let _initProto9, _initClass9;
402
581
  const loggerEvents = [];
403
582
  const logger = {
404
583
  debug() {},
@@ -414,12 +593,12 @@ export class HttpAdapterPortabilityHarness {
414
593
  class HealthController {
415
594
  static {
416
595
  ({
417
- e: [_initProto6],
418
- c: [_HealthController2, _initClass6]
596
+ e: [_initProto9],
597
+ c: [_HealthController2, _initClass9]
419
598
  } = _applyDecs(this, [Controller('/health')], [[Get('/'), 2, "getHealth"]]));
420
599
  }
421
600
  constructor() {
422
- _initProto6(this);
601
+ _initProto9(this);
423
602
  }
424
603
  getHealth() {
425
604
  return {
@@ -427,7 +606,7 @@ export class HttpAdapterPortabilityHarness {
427
606
  };
428
607
  }
429
608
  static {
430
- _initClass6();
609
+ _initClass9();
431
610
  }
432
611
  }
433
612
  class AppModule {}
@@ -461,7 +640,7 @@ export class HttpAdapterPortabilityHarness {
461
640
  }
462
641
  }
463
642
  async assertRemovesShutdownSignalListenersAfterClose() {
464
- let _initProto7, _initClass7;
643
+ let _initProto0, _initClass0;
465
644
  const logger = {
466
645
  debug() {},
467
646
  error() {},
@@ -472,12 +651,12 @@ export class HttpAdapterPortabilityHarness {
472
651
  class HealthController {
473
652
  static {
474
653
  ({
475
- e: [_initProto7],
476
- c: [_HealthController3, _initClass7]
654
+ e: [_initProto0],
655
+ c: [_HealthController3, _initClass0]
477
656
  } = _applyDecs(this, [Controller('/health')], [[Get('/'), 2, "getHealth"]]));
478
657
  }
479
658
  constructor() {
480
- _initProto7(this);
659
+ _initProto0(this);
481
660
  }
482
661
  getHealth() {
483
662
  return {
@@ -485,7 +664,7 @@ export class HttpAdapterPortabilityHarness {
485
664
  };
486
665
  }
487
666
  static {
488
- _initClass7();
667
+ _initClass0();
489
668
  }
490
669
  }
491
670
  class AppModule {}
@@ -493,7 +672,7 @@ export class HttpAdapterPortabilityHarness {
493
672
  controllers: [_HealthController3]
494
673
  });
495
674
  const signal = 'SIGTERM';
496
- const listenersBefore = process.listeners(signal).length;
675
+ const listenersBefore = new Set(process.listeners(signal));
497
676
  const port = await findAvailablePort();
498
677
  const app = await this.options.run(AppModule, {
499
678
  cors: false,
@@ -501,14 +680,17 @@ export class HttpAdapterPortabilityHarness {
501
680
  port,
502
681
  shutdownSignals: [signal]
503
682
  });
683
+ const registeredListeners = process.listeners(signal).filter(listener => !listenersBefore.has(listener));
504
684
  try {
505
- if (process.listeners(signal).length !== listenersBefore + 1) {
685
+ if (registeredListeners.length === 0) {
506
686
  throw new Error(`${this.options.name} adapter did not register the expected shutdown listener.`);
507
687
  }
508
688
  } finally {
509
689
  await closeSilently(app);
510
690
  }
511
- if (process.listeners(signal).length !== listenersBefore) {
691
+ const remainingListeners = process.listeners(signal);
692
+ const leakedListeners = registeredListeners.filter(listener => remainingListeners.includes(listener));
693
+ if (leakedListeners.length > 0) {
512
694
  throw new Error(`${this.options.name} adapter leaked shutdown signal listeners after close().`);
513
695
  }
514
696
  }
@@ -525,4 +707,17 @@ export class HttpAdapterPortabilityHarness {
525
707
  */
526
708
  export function createHttpAdapterPortabilityHarness(options) {
527
709
  return new HttpAdapterPortabilityHarness(options);
710
+ }
711
+ async function withTimeout(promise, timeoutMs, message) {
712
+ let timeout;
713
+ const timeoutPromise = new Promise((_resolve, reject) => {
714
+ timeout = setTimeout(() => {
715
+ reject(new Error(message));
716
+ }, timeoutMs);
717
+ });
718
+ return await Promise.race([promise, timeoutPromise]).finally(() => {
719
+ if (timeout !== undefined) {
720
+ clearTimeout(timeout);
721
+ }
722
+ });
528
723
  }
@@ -9,18 +9,31 @@ type WebRuntimePortabilityAppLike = {
9
9
  close(): Promise<void>;
10
10
  dispatch(request: Request): Promise<Response>;
11
11
  };
12
+ /**
13
+ * Describes the web runtime http adapter portability harness options contract.
14
+ */
12
15
  export interface WebRuntimeHttpAdapterPortabilityHarnessOptions<TBootstrapOptions extends object, TApp extends WebRuntimePortabilityAppLike = WebRuntimePortabilityAppLike> {
13
16
  bootstrap: (rootModule: ModuleType, options: TBootstrapOptions) => Promise<TApp>;
14
17
  name: string;
15
18
  }
19
+ /**
20
+ * Represents the web runtime http adapter portability harness.
21
+ */
16
22
  export declare class WebRuntimeHttpAdapterPortabilityHarness<TBootstrapOptions extends object, TApp extends WebRuntimePortabilityAppLike = WebRuntimePortabilityAppLike> {
17
23
  private readonly options;
18
24
  constructor(options: WebRuntimeHttpAdapterPortabilityHarnessOptions<TBootstrapOptions, TApp>);
25
+ assertPreservesQueryArraysAndDecoding(): Promise<void>;
19
26
  assertPreservesMalformedCookieValues(): Promise<void>;
20
27
  assertPreservesRawBodyForJsonAndText(): Promise<void>;
21
28
  assertExcludesRawBodyForMultipart(): Promise<void>;
22
29
  assertSupportsSseStreaming(): Promise<void>;
23
30
  }
31
+ /**
32
+ * Create web runtime http adapter portability harness.
33
+ *
34
+ * @param options The options.
35
+ * @returns The create web runtime http adapter portability harness result.
36
+ */
24
37
  export declare function createWebRuntimeHttpAdapterPortabilityHarness<TBootstrapOptions extends object, TApp extends WebRuntimePortabilityAppLike = WebRuntimePortabilityAppLike>(options: WebRuntimeHttpAdapterPortabilityHarnessOptions<TBootstrapOptions, TApp>): WebRuntimeHttpAdapterPortabilityHarness<TBootstrapOptions, TApp>;
25
38
  export {};
26
39
  //# sourceMappingURL=web-runtime-adapter-portability.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"web-runtime-adapter-portability.d.ts","sourceRoot":"","sources":["../../src/portability/web-runtime-adapter-portability.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgB,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEnF,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,gBAAgB;QACxB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;QACvB,OAAO,CAAC,EAAE,UAAU,CAAC;KACtB;CACF;AAED,KAAK,4BAA4B,GAAG;IAClC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC/C,CAAC;AAEF,MAAM,WAAW,8CAA8C,CAC7D,iBAAiB,SAAS,MAAM,EAChC,IAAI,SAAS,4BAA4B,GAAG,4BAA4B;IAExE,SAAS,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjF,IAAI,EAAE,MAAM,CAAC;CACd;AAYD,qBAAa,uCAAuC,CAClD,iBAAiB,SAAS,MAAM,EAChC,IAAI,SAAS,4BAA4B,GAAG,4BAA4B;IAE5D,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,8CAA8C,CAAC,iBAAiB,EAAE,IAAI,CAAC;IAEvG,oCAAoC,IAAI,OAAO,CAAC,IAAI,CAAC;IA4CrD,oCAAoC,IAAI,OAAO,CAAC,IAAI,CAAC;IA2DrD,iCAAiC,IAAI,OAAO,CAAC,IAAI,CAAC;IA2ClD,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC;CA4ClD;AAED,wBAAgB,6CAA6C,CAC3D,iBAAiB,SAAS,MAAM,EAChC,IAAI,SAAS,4BAA4B,GAAG,4BAA4B,EAExE,OAAO,EAAE,8CAA8C,CAAC,iBAAiB,EAAE,IAAI,CAAC,GAC/E,uCAAuC,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAElE"}
1
+ {"version":3,"file":"web-runtime-adapter-portability.d.ts","sourceRoot":"","sources":["../../src/portability/web-runtime-adapter-portability.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgB,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEnF,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,gBAAgB;QACxB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;QACvB,OAAO,CAAC,EAAE,UAAU,CAAC;KACtB;CACF;AAED,KAAK,4BAA4B,GAAG;IAClC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC/C,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,8CAA8C,CAC7D,iBAAiB,SAAS,MAAM,EAChC,IAAI,SAAS,4BAA4B,GAAG,4BAA4B;IAExE,SAAS,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjF,IAAI,EAAE,MAAM,CAAC;CACd;AAYD;;GAEG;AACH,qBAAa,uCAAuC,CAClD,iBAAiB,SAAS,MAAM,EAChC,IAAI,SAAS,4BAA4B,GAAG,4BAA4B;IAE5D,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,8CAA8C,CAAC,iBAAiB,EAAE,IAAI,CAAC;IAEvG,qCAAqC,IAAI,OAAO,CAAC,IAAI,CAAC;IAuCtD,oCAAoC,IAAI,OAAO,CAAC,IAAI,CAAC;IA4CrD,oCAAoC,IAAI,OAAO,CAAC,IAAI,CAAC;IA2DrD,iCAAiC,IAAI,OAAO,CAAC,IAAI,CAAC;IA2ClD,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC;CA4ClD;AAED;;;;;GAKG;AACH,wBAAgB,6CAA6C,CAC3D,iBAAiB,SAAS,MAAM,EAChC,IAAI,SAAS,4BAA4B,GAAG,4BAA4B,EAExE,OAAO,EAAE,8CAA8C,CAAC,iBAAiB,EAAE,IAAI,CAAC,GAC/E,uCAAuC,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAElE"}
@@ -7,6 +7,11 @@ function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side
7
7
  import { Controller, Get, Post, SseResponse } from '@fluojs/http';
8
8
  // @ts-ignore Worktree-local LSP does not resolve workspace package aliases.
9
9
  import { defineModule } from '@fluojs/runtime';
10
+
11
+ /**
12
+ * Describes the web runtime http adapter portability harness options contract.
13
+ */
14
+
10
15
  function decodeUtf8(input) {
11
16
  return new TextDecoder().decode(input ?? new Uint8Array());
12
17
  }
@@ -15,28 +20,72 @@ async function closeSilently(app) {
15
20
  await app.close();
16
21
  } catch {}
17
22
  }
23
+
24
+ /**
25
+ * Represents the web runtime http adapter portability harness.
26
+ */
18
27
  export class WebRuntimeHttpAdapterPortabilityHarness {
19
28
  constructor(options) {
20
29
  this.options = options;
21
30
  }
22
- async assertPreservesMalformedCookieValues() {
31
+ async assertPreservesQueryArraysAndDecoding() {
23
32
  let _initProto, _initClass;
33
+ let _QueryController;
34
+ class QueryController {
35
+ static {
36
+ ({
37
+ e: [_initProto],
38
+ c: [_QueryController, _initClass]
39
+ } = _applyDecs(this, [Controller('/query')], [[Get('/'), 2, "readQuery"]]));
40
+ }
41
+ constructor() {
42
+ _initProto(this);
43
+ }
44
+ readQuery(_input, context) {
45
+ return context.request.query;
46
+ }
47
+ static {
48
+ _initClass();
49
+ }
50
+ }
51
+ class AppModule {}
52
+ defineModule(AppModule, {
53
+ controllers: [_QueryController]
54
+ });
55
+ const app = await this.options.bootstrap(AppModule, {
56
+ cors: false
57
+ });
58
+ try {
59
+ const response = await app.dispatch(new Request('https://runtime.test/query?tag=one&tag=two&encoded=hello+world&flag&bad=%E0%A4%A'));
60
+ if (response.status !== 200) {
61
+ throw new Error(`${this.options.name} adapter changed query response status semantics.`);
62
+ }
63
+ const body = await response.json();
64
+ if (typeof body !== 'object' || body === null || body.bad !== '�%A' || body.encoded !== 'hello world' || !Array.isArray(body.tag) || JSON.stringify(body.tag) !== JSON.stringify(['one', 'two'])) {
65
+ throw new Error(`${this.options.name} adapter changed query decoding semantics.`);
66
+ }
67
+ } finally {
68
+ await closeSilently(app);
69
+ }
70
+ }
71
+ async assertPreservesMalformedCookieValues() {
72
+ let _initProto2, _initClass2;
24
73
  let _CookieController;
25
74
  class CookieController {
26
75
  static {
27
76
  ({
28
- e: [_initProto],
29
- c: [_CookieController, _initClass]
77
+ e: [_initProto2],
78
+ c: [_CookieController, _initClass2]
30
79
  } = _applyDecs(this, [Controller('/cookies')], [[Get('/'), 2, "readCookies"]]));
31
80
  }
32
81
  constructor() {
33
- _initProto(this);
82
+ _initProto2(this);
34
83
  }
35
84
  readCookies(_input, context) {
36
85
  return context.request.cookies;
37
86
  }
38
87
  static {
39
- _initClass();
88
+ _initClass2();
40
89
  }
41
90
  }
42
91
  class AppModule {}
@@ -64,17 +113,17 @@ export class WebRuntimeHttpAdapterPortabilityHarness {
64
113
  }
65
114
  }
66
115
  async assertPreservesRawBodyForJsonAndText() {
67
- let _initProto2, _initClass2;
116
+ let _initProto3, _initClass3;
68
117
  let _WebhookController;
69
118
  class WebhookController {
70
119
  static {
71
120
  ({
72
- e: [_initProto2],
73
- c: [_WebhookController, _initClass2]
121
+ e: [_initProto3],
122
+ c: [_WebhookController, _initClass3]
74
123
  } = _applyDecs(this, [Controller('/webhooks')], [[Post('/json'), 2, "handleJson"], [Post('/text'), 2, "handleText"]]));
75
124
  }
76
125
  constructor() {
77
- _initProto2(this);
126
+ _initProto3(this);
78
127
  }
79
128
  handleJson(_input, context) {
80
129
  return {
@@ -89,7 +138,7 @@ export class WebRuntimeHttpAdapterPortabilityHarness {
89
138
  };
90
139
  }
91
140
  static {
92
- _initClass2();
141
+ _initClass3();
93
142
  }
94
143
  }
95
144
  class AppModule {}
@@ -139,17 +188,17 @@ export class WebRuntimeHttpAdapterPortabilityHarness {
139
188
  }
140
189
  }
141
190
  async assertExcludesRawBodyForMultipart() {
142
- let _initProto3, _initClass3;
191
+ let _initProto4, _initClass4;
143
192
  let _UploadController;
144
193
  class UploadController {
145
194
  static {
146
195
  ({
147
- e: [_initProto3],
148
- c: [_UploadController, _initClass3]
196
+ e: [_initProto4],
197
+ c: [_UploadController, _initClass4]
149
198
  } = _applyDecs(this, [Controller('/uploads')], [[Post('/'), 2, "upload"]]));
150
199
  }
151
200
  constructor() {
152
- _initProto3(this);
201
+ _initProto4(this);
153
202
  }
154
203
  upload(_input, context) {
155
204
  return {
@@ -159,7 +208,7 @@ export class WebRuntimeHttpAdapterPortabilityHarness {
159
208
  };
160
209
  }
161
210
  static {
162
- _initClass3();
211
+ _initClass4();
163
212
  }
164
213
  }
165
214
  class AppModule {}
@@ -198,17 +247,17 @@ export class WebRuntimeHttpAdapterPortabilityHarness {
198
247
  }
199
248
  }
200
249
  async assertSupportsSseStreaming() {
201
- let _initProto4, _initClass4;
250
+ let _initProto5, _initClass5;
202
251
  let _EventsController;
203
252
  class EventsController {
204
253
  static {
205
254
  ({
206
- e: [_initProto4],
207
- c: [_EventsController, _initClass4]
255
+ e: [_initProto5],
256
+ c: [_EventsController, _initClass5]
208
257
  } = _applyDecs(this, [Controller('/events')], [[Get('/'), 2, "stream"]]));
209
258
  }
210
259
  constructor() {
211
- _initProto4(this);
260
+ _initProto5(this);
212
261
  }
213
262
  stream(_input, context) {
214
263
  const stream = new SseResponse(context);
@@ -223,7 +272,7 @@ export class WebRuntimeHttpAdapterPortabilityHarness {
223
272
  return stream;
224
273
  }
225
274
  static {
226
- _initClass4();
275
+ _initClass5();
227
276
  }
228
277
  }
229
278
  class AppModule {}
@@ -255,6 +304,13 @@ export class WebRuntimeHttpAdapterPortabilityHarness {
255
304
  }
256
305
  }
257
306
  }
307
+
308
+ /**
309
+ * Create web runtime http adapter portability harness.
310
+ *
311
+ * @param options The options.
312
+ * @returns The create web runtime http adapter portability harness result.
313
+ */
258
314
  export function createWebRuntimeHttpAdapterPortabilityHarness(options) {
259
315
  return new WebRuntimeHttpAdapterPortabilityHarness(options);
260
316
  }
package/dist/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { Mock } from 'vitest';
2
2
  import type { MaybePromise, Token } from '@fluojs/core';
3
3
  import type { ClassType, Container, ForwardRefFn, OptionalToken, Provider } from '@fluojs/di';
4
- import type { BootstrapResult, BootstrapModuleOptions, ModuleType } from '@fluojs/runtime';
4
+ import type { BootstrapApplicationOptions, BootstrapResult, BootstrapModuleOptions, ModuleType } from '@fluojs/runtime';
5
5
  import type { Guard, Interceptor } from '@fluojs/http';
6
6
  import type { RequestBuilder, TestPrincipal, TestRequest, TestRequestWithOptions, TestResponse } from './http.js';
7
7
  /**
@@ -10,6 +10,12 @@ import type { RequestBuilder, TestPrincipal, TestRequest, TestRequestWithOptions
10
10
  export interface TestingModuleOptions extends BootstrapModuleOptions {
11
11
  rootModule: ModuleType;
12
12
  }
13
+ /**
14
+ * Bootstrap options accepted by `createTestApp(...)`.
15
+ */
16
+ export interface TestingApplicationOptions extends BootstrapApplicationOptions {
17
+ rootModule: ModuleType;
18
+ }
13
19
  /**
14
20
  * Optional request extras accepted by `TestApp.request(...)` overloads.
15
21
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}