@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.
- package/README.ko.md +73 -11
- package/README.md +71 -9
- package/dist/app.d.ts +2 -2
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +1 -1
- package/dist/babel-decorators-plugin.d.ts +2 -2
- package/dist/babel-decorators-plugin.d.ts.map +1 -1
- package/dist/babel-decorators-plugin.js +26 -12
- package/dist/conformance/fetch-style-websocket-conformance.d.ts +12 -0
- package/dist/conformance/fetch-style-websocket-conformance.d.ts.map +1 -1
- package/dist/conformance/fetch-style-websocket-conformance.js +14 -0
- package/dist/conformance/platform-conformance.d.ts +21 -0
- package/dist/conformance/platform-conformance.d.ts.map +1 -1
- package/dist/conformance/platform-conformance.js +27 -0
- package/dist/mock.d.ts +17 -0
- package/dist/mock.d.ts.map +1 -1
- package/dist/mock.js +19 -0
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +135 -22
- package/dist/portability/http-adapter-portability.d.ts +15 -0
- package/dist/portability/http-adapter-portability.d.ts.map +1 -1
- package/dist/portability/http-adapter-portability.js +225 -30
- package/dist/portability/web-runtime-adapter-portability.d.ts +13 -0
- package/dist/portability/web-runtime-adapter-portability.d.ts.map +1 -1
- package/dist/portability/web-runtime-adapter-portability.js +76 -20
- package/dist/types.d.ts +7 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -9
|
@@ -210,18 +210,75 @@ export class HttpAdapterPortabilityHarness {
|
|
|
210
210
|
await closeSilently(app);
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
|
-
async
|
|
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: [
|
|
220
|
-
c: [_UploadController,
|
|
276
|
+
e: [_initProto4],
|
|
277
|
+
c: [_UploadController, _initClass4]
|
|
221
278
|
} = _applyDecs(this, [Controller('/uploads')], [[Post('/'), 2, "upload"]]));
|
|
222
279
|
}
|
|
223
280
|
constructor() {
|
|
224
|
-
|
|
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
|
-
|
|
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
|
|
391
|
+
let _initProto6, _initClass6;
|
|
277
392
|
let _EventsController;
|
|
278
393
|
class EventsController {
|
|
279
394
|
static {
|
|
280
395
|
({
|
|
281
|
-
e: [
|
|
282
|
-
c: [_EventsController,
|
|
396
|
+
e: [_initProto6],
|
|
397
|
+
c: [_EventsController, _initClass6]
|
|
283
398
|
} = _applyDecs(this, [Controller('/events')], [[Get('/'), 2, "stream"]]));
|
|
284
399
|
}
|
|
285
400
|
constructor() {
|
|
286
|
-
|
|
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
|
-
|
|
412
|
+
queueMicrotask(() => {
|
|
298
413
|
stream.close();
|
|
299
|
-
}
|
|
414
|
+
});
|
|
300
415
|
return stream;
|
|
301
416
|
}
|
|
302
417
|
static {
|
|
303
|
-
|
|
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
|
|
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: [
|
|
355
|
-
c: [_HealthController,
|
|
533
|
+
e: [_initProto8],
|
|
534
|
+
c: [_HealthController, _initClass8]
|
|
356
535
|
} = _applyDecs(this, [Controller('/health')], [[Get('/'), 2, "getHealth"]]));
|
|
357
536
|
}
|
|
358
537
|
constructor() {
|
|
359
|
-
|
|
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
|
-
|
|
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
|
|
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: [
|
|
418
|
-
c: [_HealthController2,
|
|
596
|
+
e: [_initProto9],
|
|
597
|
+
c: [_HealthController2, _initClass9]
|
|
419
598
|
} = _applyDecs(this, [Controller('/health')], [[Get('/'), 2, "getHealth"]]));
|
|
420
599
|
}
|
|
421
600
|
constructor() {
|
|
422
|
-
|
|
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
|
-
|
|
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
|
|
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: [
|
|
476
|
-
c: [_HealthController3,
|
|
654
|
+
e: [_initProto0],
|
|
655
|
+
c: [_HealthController3, _initClass0]
|
|
477
656
|
} = _applyDecs(this, [Controller('/health')], [[Get('/'), 2, "getHealth"]]));
|
|
478
657
|
}
|
|
479
658
|
constructor() {
|
|
480
|
-
|
|
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
|
-
|
|
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)
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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: [
|
|
29
|
-
c: [_CookieController,
|
|
77
|
+
e: [_initProto2],
|
|
78
|
+
c: [_CookieController, _initClass2]
|
|
30
79
|
} = _applyDecs(this, [Controller('/cookies')], [[Get('/'), 2, "readCookies"]]));
|
|
31
80
|
}
|
|
32
81
|
constructor() {
|
|
33
|
-
|
|
82
|
+
_initProto2(this);
|
|
34
83
|
}
|
|
35
84
|
readCookies(_input, context) {
|
|
36
85
|
return context.request.cookies;
|
|
37
86
|
}
|
|
38
87
|
static {
|
|
39
|
-
|
|
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
|
|
116
|
+
let _initProto3, _initClass3;
|
|
68
117
|
let _WebhookController;
|
|
69
118
|
class WebhookController {
|
|
70
119
|
static {
|
|
71
120
|
({
|
|
72
|
-
e: [
|
|
73
|
-
c: [_WebhookController,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
191
|
+
let _initProto4, _initClass4;
|
|
143
192
|
let _UploadController;
|
|
144
193
|
class UploadController {
|
|
145
194
|
static {
|
|
146
195
|
({
|
|
147
|
-
e: [
|
|
148
|
-
c: [_UploadController,
|
|
196
|
+
e: [_initProto4],
|
|
197
|
+
c: [_UploadController, _initClass4]
|
|
149
198
|
} = _applyDecs(this, [Controller('/uploads')], [[Post('/'), 2, "upload"]]));
|
|
150
199
|
}
|
|
151
200
|
constructor() {
|
|
152
|
-
|
|
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
|
-
|
|
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
|
|
250
|
+
let _initProto5, _initClass5;
|
|
202
251
|
let _EventsController;
|
|
203
252
|
class EventsController {
|
|
204
253
|
static {
|
|
205
254
|
({
|
|
206
|
-
e: [
|
|
207
|
-
c: [_EventsController,
|
|
255
|
+
e: [_initProto5],
|
|
256
|
+
c: [_EventsController, _initClass5]
|
|
208
257
|
} = _applyDecs(this, [Controller('/events')], [[Get('/'), 2, "stream"]]));
|
|
209
258
|
}
|
|
210
259
|
constructor() {
|
|
211
|
-
|
|
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
|
-
|
|
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
|
*/
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC9F,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|