@nextrush/adapter-edge 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @nextrush/adapter-edge - Body Source Tests
3
+ */
4
+
5
+ import { BodyConsumedError, BodyTooLargeError, EmptyBodySource as RuntimeEmptyBodySource } from '@nextrush/runtime';
6
+ import { beforeEach, describe, expect, it } from 'vitest';
7
+ import { createEmptyBodySource, EmptyBodySource } from '../body-source';
8
+
9
+ describe('EmptyBodySource', () => {
10
+ let emptySource: EmptyBodySource;
11
+
12
+ beforeEach(() => {
13
+ emptySource = new EmptyBodySource();
14
+ });
15
+
16
+ it('should have contentLength of 0', () => {
17
+ expect(emptySource.contentLength).toBe(0);
18
+ });
19
+
20
+ it('should have undefined contentType', () => {
21
+ expect(emptySource.contentType).toBeUndefined();
22
+ });
23
+
24
+ it('should not be consumed', () => {
25
+ expect(emptySource.consumed).toBe(false);
26
+ });
27
+
28
+ it('should return empty string from text()', async () => {
29
+ const text = await emptySource.text();
30
+ expect(text).toBe('');
31
+ });
32
+
33
+ it('should return empty Uint8Array from buffer()', async () => {
34
+ const buffer = await emptySource.buffer();
35
+ expect(buffer).toBeInstanceOf(Uint8Array);
36
+ expect(buffer.length).toBe(0);
37
+ });
38
+
39
+ it('should throw BadRequestError from json()', async () => {
40
+ await expect(emptySource.json()).rejects.toThrow('Request body is empty');
41
+ });
42
+
43
+ it('should return empty ReadableStream from stream()', async () => {
44
+ const stream = emptySource.stream();
45
+ expect(stream).toBeInstanceOf(ReadableStream);
46
+
47
+ const reader = stream.getReader();
48
+ const { done, value } = await reader.read();
49
+ expect(done).toBe(true);
50
+ expect(value).toBeUndefined();
51
+ });
52
+ });
53
+
54
+ describe('BodyConsumedError', () => {
55
+ it('should have correct message', () => {
56
+ const error = new BodyConsumedError();
57
+ expect(error.message).toBe('Body has already been consumed');
58
+ expect(error.name).toBe('BodyConsumedError');
59
+ });
60
+ });
61
+
62
+ describe('BodyTooLargeError', () => {
63
+ it('should have correct message and properties', () => {
64
+ const error = new BodyTooLargeError(100, 200);
65
+
66
+ expect(error.message).toContain('200 bytes');
67
+ expect(error.message).toContain('100 bytes');
68
+ expect(error.name).toBe('BodyTooLargeError');
69
+ expect(error.limit).toBe(100);
70
+ expect(error.received).toBe(200);
71
+ });
72
+ });
73
+
74
+ describe('createEmptyBodySource', () => {
75
+ it('should create EmptyBodySource instance', () => {
76
+ const bodySource = createEmptyBodySource();
77
+ expect(bodySource).toBeInstanceOf(EmptyBodySource);
78
+ });
79
+ });
80
+
81
+ describe('F-04a — shared WebBodySource reuse', () => {
82
+ it('EmptyBodySource is the shared runtime EmptyBodySource', () => {
83
+ expect(EmptyBodySource).toBe(RuntimeEmptyBodySource);
84
+ });
85
+ });
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @nextrush/adapter-edge — HP-5-web lazy `ctx.raw` regression contract
3
+ *
4
+ * OpenSpec change `web-adapters-context-response-microtrims` (HP-5-web): the
5
+ * `{ req, res }` wrapper is built lazily by a memoized getter, with `req` held
6
+ * in a private field used by the `signal` getter and `triggerTimeout`. These
7
+ * are behavior-preserving characterization tests pinning the observable
8
+ * contract of `ctx.raw`, `ctx.signal`, and timeout across the refactor. The
9
+ * "no wrapper allocated when unread" proof is the allocation micro-bench
10
+ * (`apps/benchmark/scripts/web-context-alloc.js`, task 5.2), not a unit test.
11
+ */
12
+
13
+ import { describe, expect, it } from 'vitest';
14
+ import { EdgeContext } from '../context';
15
+
16
+ function makeRequest(init?: RequestInit): Request {
17
+ return new Request('http://localhost/', init);
18
+ }
19
+
20
+ describe('HP-5-web: Edge ctx.raw lazy memoized wrapper', () => {
21
+ it('returns the { req, res: undefined } shape', () => {
22
+ const request = makeRequest();
23
+ const ctx = new EdgeContext(request);
24
+ expect(ctx.raw.req).toBe(request);
25
+ expect(ctx.raw.res).toBeUndefined();
26
+ });
27
+
28
+ it('is memoized — repeated reads return the same object', () => {
29
+ const ctx = new EdgeContext(makeRequest());
30
+ expect(ctx.raw).toBe(ctx.raw);
31
+ });
32
+ });
33
+
34
+ describe('HP-5-web: Edge ctx.signal still combines request signal + timeout', () => {
35
+ it('aborts ctx.signal when the underlying request signal aborts', () => {
36
+ const controller = new AbortController();
37
+ const ctx = new EdgeContext(makeRequest({ signal: controller.signal }));
38
+ const signal = ctx.signal;
39
+ expect(signal).toBeInstanceOf(AbortSignal);
40
+ expect(signal.aborted).toBe(false);
41
+ controller.abort();
42
+ expect(signal.aborted).toBe(true);
43
+ });
44
+
45
+ it('aborts ctx.signal when triggerTimeout fires', () => {
46
+ const ctx = new EdgeContext(makeRequest());
47
+ const signal = ctx.signal;
48
+ expect(signal.aborted).toBe(false);
49
+ ctx.triggerTimeout();
50
+ expect(signal.aborted).toBe(true);
51
+ });
52
+ });
@@ -0,0 +1,536 @@
1
+ /**
2
+ * @nextrush/adapter-edge - Context Tests
3
+ *
4
+ * Tests for EdgeContext using Web API Request/Response.
5
+ * Edge runtimes (Cloudflare Workers, Vercel Edge, Netlify Edge) all use
6
+ * standard Web APIs, so we can test with Node.js/vitest.
7
+ */
8
+
9
+ import { HttpError } from '@nextrush/errors';
10
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
11
+ import { EdgeContext, createEdgeContext, type EdgeExecutionContext } from '../context';
12
+
13
+ /**
14
+ * Create a mock Web Request
15
+ */
16
+ function createMockRequest(url: string = 'http://localhost/', init?: RequestInit): Request {
17
+ return new Request(url, init);
18
+ }
19
+
20
+ /**
21
+ * Create a mock execution context (like Cloudflare Workers)
22
+ */
23
+ function createMockExecutionContext(): EdgeExecutionContext {
24
+ return {
25
+ waitUntil: vi.fn(),
26
+ passThroughOnException: vi.fn(),
27
+ };
28
+ }
29
+
30
+ describe('EdgeContext', () => {
31
+ let request: Request;
32
+ let ctx: EdgeContext;
33
+
34
+ beforeEach(() => {
35
+ request = createMockRequest('http://localhost/');
36
+ ctx = new EdgeContext(request);
37
+ });
38
+
39
+ describe('constructor', () => {
40
+ it('should create context with default values', () => {
41
+ expect(ctx.method).toBe('GET');
42
+ expect(ctx.path).toBe('/');
43
+ expect(ctx.status).toBe(200);
44
+ expect(ctx.params).toEqual({});
45
+ expect(ctx.body).toBeUndefined();
46
+ expect(ctx.runtime).toBe('edge');
47
+ });
48
+
49
+ it('should parse URL path', () => {
50
+ request = createMockRequest('http://localhost/users/123');
51
+ ctx = new EdgeContext(request);
52
+
53
+ expect(ctx.path).toBe('/users/123');
54
+ expect(ctx.url).toBe('/users/123');
55
+ });
56
+
57
+ it('should parse query string', () => {
58
+ request = createMockRequest('http://localhost/search?q=test&limit=10');
59
+ ctx = new EdgeContext(request);
60
+
61
+ expect(ctx.path).toBe('/search');
62
+ expect(ctx.query).toEqual({ q: 'test', limit: '10' });
63
+ });
64
+
65
+ it('should handle URL without query string', () => {
66
+ request = createMockRequest('http://localhost/users');
67
+ ctx = new EdgeContext(request);
68
+
69
+ expect(ctx.path).toBe('/users');
70
+ expect(ctx.query).toEqual({});
71
+ });
72
+
73
+ it('should get HTTP method', () => {
74
+ request = createMockRequest('http://localhost/', { method: 'POST' });
75
+ ctx = new EdgeContext(request);
76
+
77
+ expect(ctx.method).toBe('POST');
78
+ });
79
+
80
+ it('should uppercase method', () => {
81
+ request = createMockRequest('http://localhost/', { method: 'post' });
82
+ ctx = new EdgeContext(request);
83
+
84
+ expect(ctx.method).toBe('POST');
85
+ });
86
+
87
+ it('should extract headers', () => {
88
+ request = createMockRequest('http://localhost/', {
89
+ headers: { 'Content-Type': 'application/json', 'X-Custom': 'value' },
90
+ });
91
+ ctx = new EdgeContext(request);
92
+
93
+ expect(ctx.headers['content-type']).toBe('application/json');
94
+ expect(ctx.headers['x-custom']).toBe('value');
95
+ });
96
+
97
+ it('should extract IP from cf-connecting-ip header (Cloudflare)', () => {
98
+ request = createMockRequest('http://localhost/', {
99
+ headers: { 'cf-connecting-ip': '192.168.1.100' },
100
+ });
101
+ ctx = new EdgeContext(request, undefined, true);
102
+
103
+ expect(ctx.ip).toBe('192.168.1.100');
104
+ });
105
+
106
+ it('should extract IP from x-forwarded-for header', () => {
107
+ request = createMockRequest('http://localhost/', {
108
+ headers: { 'x-forwarded-for': '10.0.0.1, 10.0.0.2' },
109
+ });
110
+ ctx = new EdgeContext(request, undefined, true);
111
+
112
+ expect(ctx.ip).toBe('10.0.0.1');
113
+ });
114
+
115
+ it('should extract IP from x-real-ip header', () => {
116
+ request = createMockRequest('http://localhost/', {
117
+ headers: { 'x-real-ip': '172.16.0.1' },
118
+ });
119
+ ctx = new EdgeContext(request, undefined, true);
120
+
121
+ expect(ctx.ip).toBe('172.16.0.1');
122
+ });
123
+
124
+ it('should not trust proxy headers when trustProxy is false', () => {
125
+ request = createMockRequest('http://localhost/', {
126
+ headers: { 'x-forwarded-for': '10.0.0.1', 'cf-connecting-ip': '192.168.1.100' },
127
+ });
128
+ ctx = new EdgeContext(request);
129
+
130
+ expect(ctx.ip).toBe('');
131
+ });
132
+
133
+ it('should store execution context', () => {
134
+ const execCtx = createMockExecutionContext();
135
+ ctx = new EdgeContext(request, execCtx);
136
+
137
+ expect(ctx.executionContext).toBe(execCtx);
138
+ });
139
+ });
140
+
141
+ describe('json()', () => {
142
+ it('should build JSON response', () => {
143
+ ctx.json({ message: 'hello' });
144
+
145
+ const response = ctx.getResponse();
146
+ expect(response.headers.get('Content-Type')).toBe('application/json; charset=utf-8');
147
+ expect(response.status).toBe(200);
148
+ });
149
+
150
+ it('should set status code', () => {
151
+ ctx.status = 201;
152
+ ctx.json({ created: true });
153
+
154
+ const response = ctx.getResponse();
155
+ expect(response.status).toBe(201);
156
+ });
157
+
158
+ it('should not send response twice', () => {
159
+ ctx.json({ first: true });
160
+ ctx.json({ second: true });
161
+
162
+ expect(ctx.responded).toBe(true);
163
+ });
164
+
165
+ it('should serialize data correctly', async () => {
166
+ ctx.json({ users: [1, 2, 3] });
167
+
168
+ const response = ctx.getResponse();
169
+ const body = await response.json();
170
+ expect(body).toEqual({ users: [1, 2, 3] });
171
+ });
172
+ });
173
+
174
+ describe('send()', () => {
175
+ it('should send string response', async () => {
176
+ ctx.send('Hello World');
177
+
178
+ const response = ctx.getResponse();
179
+ expect(response.headers.get('Content-Type')).toBe('text/plain; charset=utf-8');
180
+
181
+ const text = await response.text();
182
+ expect(text).toBe('Hello World');
183
+ });
184
+
185
+ it('should send Uint8Array response', () => {
186
+ const buffer = new Uint8Array([72, 101, 108, 108, 111]);
187
+ ctx.send(buffer);
188
+
189
+ const response = ctx.getResponse();
190
+ expect(response.headers.get('Content-Type')).toBe('application/octet-stream');
191
+ });
192
+
193
+ it('should handle null response', async () => {
194
+ ctx.send(null);
195
+
196
+ const response = ctx.getResponse();
197
+ const text = await response.text();
198
+ expect(text).toBe('');
199
+ });
200
+
201
+ it('should handle undefined response', async () => {
202
+ ctx.send(undefined);
203
+
204
+ const response = ctx.getResponse();
205
+ const text = await response.text();
206
+ expect(text).toBe('');
207
+ });
208
+
209
+ it('should send object as JSON', async () => {
210
+ ctx.send({ data: 'test' });
211
+
212
+ const response = ctx.getResponse();
213
+ const body = await response.json();
214
+ expect(body).toEqual({ data: 'test' });
215
+ });
216
+ });
217
+
218
+ describe('html()', () => {
219
+ it('should send HTML response', async () => {
220
+ ctx.html('<h1>Hello</h1>');
221
+
222
+ const response = ctx.getResponse();
223
+ expect(response.headers.get('Content-Type')).toBe('text/html; charset=utf-8');
224
+
225
+ const text = await response.text();
226
+ expect(text).toBe('<h1>Hello</h1>');
227
+ });
228
+ });
229
+
230
+ describe('redirect()', () => {
231
+ it('should redirect with default 302 status', () => {
232
+ ctx.redirect('/login');
233
+
234
+ const response = ctx.getResponse();
235
+ expect(response.status).toBe(302);
236
+ expect(response.headers.get('Location')).toBe('/login');
237
+ });
238
+
239
+ it('should redirect with custom status', () => {
240
+ ctx.redirect('/new-page', 301);
241
+
242
+ const response = ctx.getResponse();
243
+ expect(response.status).toBe(301);
244
+ });
245
+ });
246
+
247
+ describe('set()', () => {
248
+ it('should set response header', () => {
249
+ ctx.set('X-Custom', 'value');
250
+
251
+ const response = ctx.getResponse();
252
+ expect(response.headers.get('X-Custom')).toBe('value');
253
+ });
254
+
255
+ it('should set numeric header value', () => {
256
+ ctx.set('X-Count', 100);
257
+
258
+ const response = ctx.getResponse();
259
+ expect(response.headers.get('X-Count')).toBe('100');
260
+ });
261
+
262
+ it('should accumulate multiple Set-Cookie headers', () => {
263
+ ctx.set('Set-Cookie', 'session=abc; Path=/');
264
+ ctx.set('Set-Cookie', 'csrf=xyz; Path=/');
265
+
266
+ const response = ctx.getResponse();
267
+ const cookies = response.headers.getSetCookie();
268
+ expect(cookies).toHaveLength(2);
269
+ expect(cookies).toContain('session=abc; Path=/');
270
+ expect(cookies).toContain('csrf=xyz; Path=/');
271
+ });
272
+
273
+ it('should replace all Set-Cookie headers when array is passed', () => {
274
+ ctx.set('Set-Cookie', 'old=value; Path=/');
275
+ ctx.set('Set-Cookie', ['new1=a; Path=/', 'new2=b; Path=/']);
276
+
277
+ const response = ctx.getResponse();
278
+ const cookies = response.headers.getSetCookie();
279
+ expect(cookies).toHaveLength(2);
280
+ expect(cookies).toContain('new1=a; Path=/');
281
+ expect(cookies).toContain('new2=b; Path=/');
282
+ });
283
+
284
+ it('should overwrite non-cookie headers on repeated set', () => {
285
+ ctx.set('X-Custom', 'first');
286
+ ctx.set('X-Custom', 'second');
287
+
288
+ const response = ctx.getResponse();
289
+ expect(response.headers.get('X-Custom')).toBe('second');
290
+ });
291
+ });
292
+
293
+ describe('get()', () => {
294
+ it('should get request header', () => {
295
+ request = createMockRequest('http://localhost/', {
296
+ headers: { 'content-type': 'application/json' },
297
+ });
298
+ ctx = new EdgeContext(request);
299
+
300
+ expect(ctx.get('content-type')).toBe('application/json');
301
+ });
302
+
303
+ it('should be case-insensitive', () => {
304
+ request = createMockRequest('http://localhost/', {
305
+ headers: { 'Content-Type': 'application/json' },
306
+ });
307
+ ctx = new EdgeContext(request);
308
+
309
+ expect(ctx.get('content-type')).toBe('application/json');
310
+ });
311
+
312
+ it('should return undefined for missing header', () => {
313
+ expect(ctx.get('x-missing')).toBeUndefined();
314
+ });
315
+ });
316
+
317
+ describe('next()', () => {
318
+ it('should call next function when set', async () => {
319
+ const nextFn = vi.fn().mockResolvedValue(undefined);
320
+ ctx.setNext(nextFn);
321
+
322
+ await ctx.next();
323
+
324
+ expect(nextFn).toHaveBeenCalled();
325
+ });
326
+
327
+ it('should not throw when next is not set', async () => {
328
+ await expect(ctx.next()).resolves.toBeUndefined();
329
+ });
330
+ });
331
+
332
+ describe('state', () => {
333
+ it('should allow storing state', () => {
334
+ ctx.state.user = { id: 1, name: 'John' };
335
+
336
+ expect(ctx.state.user).toEqual({ id: 1, name: 'John' });
337
+ });
338
+ });
339
+
340
+ describe('raw', () => {
341
+ it('should provide access to raw request', () => {
342
+ expect(ctx.raw.req).toBe(request);
343
+ expect(ctx.raw.res).toBeUndefined();
344
+ });
345
+ });
346
+
347
+ describe('responded', () => {
348
+ it('should be false initially', () => {
349
+ expect(ctx.responded).toBe(false);
350
+ });
351
+
352
+ it('should be true after json()', () => {
353
+ ctx.json({});
354
+ expect(ctx.responded).toBe(true);
355
+ });
356
+
357
+ it('should be true after send()', () => {
358
+ ctx.send('test');
359
+ expect(ctx.responded).toBe(true);
360
+ });
361
+
362
+ it('should be true after html()', () => {
363
+ ctx.html('<p>test</p>');
364
+ expect(ctx.responded).toBe(true);
365
+ });
366
+
367
+ it('should be true after redirect()', () => {
368
+ ctx.redirect('/');
369
+ expect(ctx.responded).toBe(true);
370
+ });
371
+
372
+ it('should be true after markResponded()', () => {
373
+ ctx.markResponded();
374
+ expect(ctx.responded).toBe(true);
375
+ });
376
+ });
377
+
378
+ describe('getResponse()', () => {
379
+ it('should return Response object', () => {
380
+ ctx.json({ test: true });
381
+
382
+ const response = ctx.getResponse();
383
+ expect(response).toBeInstanceOf(Response);
384
+ });
385
+
386
+ it('should sync status from context', () => {
387
+ ctx.status = 404;
388
+
389
+ const response = ctx.getResponse();
390
+ expect(response.status).toBe(404);
391
+ });
392
+ });
393
+
394
+ describe('waitUntil()', () => {
395
+ it('should call execution context waitUntil', () => {
396
+ const execCtx = createMockExecutionContext();
397
+ ctx = new EdgeContext(request, execCtx);
398
+
399
+ const promise = Promise.resolve();
400
+ ctx.waitUntil(promise);
401
+
402
+ expect(execCtx.waitUntil).toHaveBeenCalledWith(promise);
403
+ });
404
+
405
+ it('should not throw when execution context is not available', () => {
406
+ ctx = new EdgeContext(request);
407
+ const promise = Promise.resolve();
408
+
409
+ expect(() => ctx.waitUntil(promise)).not.toThrow();
410
+ });
411
+ });
412
+ });
413
+
414
+ describe('HttpError', () => {
415
+ it('should create error with status and message', () => {
416
+ const error = new HttpError(404, 'User not found');
417
+
418
+ expect(error.message).toBe('User not found');
419
+ expect(error.status).toBe(404);
420
+ expect(error.name).toBe('HttpError');
421
+ });
422
+
423
+ it('should use default message for status', () => {
424
+ const error = new HttpError(401);
425
+
426
+ expect(error.message).toBe('Unauthorized');
427
+ expect(error.status).toBe(401);
428
+ });
429
+
430
+ it('should set expose=true for client errors (4xx)', () => {
431
+ const error = new HttpError(400);
432
+ expect(error.expose).toBe(true);
433
+
434
+ const notFound = new HttpError(404);
435
+ expect(notFound.expose).toBe(true);
436
+ });
437
+
438
+ it('should set expose=false for server errors (5xx)', () => {
439
+ const error = new HttpError(500);
440
+ expect(error.expose).toBe(false);
441
+
442
+ const unavailable = new HttpError(503);
443
+ expect(unavailable.expose).toBe(false);
444
+ });
445
+ });
446
+
447
+ describe('throw()', () => {
448
+ let ctx: EdgeContext;
449
+
450
+ beforeEach(() => {
451
+ ctx = new EdgeContext(createMockRequest());
452
+ });
453
+
454
+ it('should throw HttpError with status and message', () => {
455
+ expect(() => ctx.throw(404, 'User not found')).toThrow('User not found');
456
+ });
457
+
458
+ it('should throw HttpError with default message', () => {
459
+ try {
460
+ ctx.throw(401);
461
+ } catch (error) {
462
+ expect((error as Error).message).toBe('Unauthorized');
463
+ expect((error as HttpError).status).toBe(401);
464
+ }
465
+ });
466
+
467
+ it('should throw correct status codes', () => {
468
+ const codes = [400, 401, 403, 404, 500, 502, 503];
469
+
470
+ for (const code of codes) {
471
+ try {
472
+ ctx.throw(code);
473
+ } catch (error) {
474
+ expect((error as HttpError).status).toBe(code);
475
+ }
476
+ }
477
+ });
478
+ });
479
+
480
+ describe('assert()', () => {
481
+ let ctx: EdgeContext;
482
+
483
+ beforeEach(() => {
484
+ ctx = new EdgeContext(createMockRequest());
485
+ });
486
+
487
+ it('should not throw when condition is truthy', () => {
488
+ expect(() => ctx.assert(true, 400)).not.toThrow();
489
+ expect(() => ctx.assert(1, 400)).not.toThrow();
490
+ expect(() => ctx.assert('value', 400)).not.toThrow();
491
+ expect(() => ctx.assert({}, 400)).not.toThrow();
492
+ });
493
+
494
+ it('should throw when condition is false', () => {
495
+ expect(() => ctx.assert(false, 400, 'Validation failed')).toThrow('Validation failed');
496
+ });
497
+
498
+ it('should throw when condition is null', () => {
499
+ expect(() => ctx.assert(null, 404, 'Not found')).toThrow('Not found');
500
+ });
501
+
502
+ it('should throw when condition is undefined', () => {
503
+ expect(() => ctx.assert(undefined, 404, 'Not found')).toThrow('Not found');
504
+ });
505
+
506
+ it('should use default message when not provided', () => {
507
+ try {
508
+ ctx.assert(false, 400);
509
+ } catch (error) {
510
+ expect((error as Error).message).toBe('Bad Request');
511
+ }
512
+ });
513
+
514
+ it('should narrow type with asserts', () => {
515
+ const maybeUser: { name: string } | null = { name: 'John' };
516
+ ctx.assert(maybeUser, 404, 'User not found');
517
+ expect(maybeUser.name).toBe('John');
518
+ });
519
+ });
520
+
521
+ describe('createEdgeContext', () => {
522
+ it('should create EdgeContext instance', () => {
523
+ const request = createMockRequest();
524
+ const ctx = createEdgeContext(request);
525
+
526
+ expect(ctx).toBeInstanceOf(EdgeContext);
527
+ });
528
+
529
+ it('should pass execution context', () => {
530
+ const request = createMockRequest();
531
+ const execCtx = createMockExecutionContext();
532
+ const ctx = createEdgeContext(request, execCtx);
533
+
534
+ expect(ctx.executionContext).toBe(execCtx);
535
+ });
536
+ });