|
1 | 1 | import zlib from 'zlib';
|
| 2 | +import type http from 'http'; |
2 | 3 |
|
3 | 4 | import type { Server as Restify } from 'restify';
|
4 | 5 | import connect from 'connect';
|
@@ -81,6 +82,12 @@ function urlString(urlParams?: { [param: string]: string }): string {
|
81 | 82 | return string;
|
82 | 83 | }
|
83 | 84 |
|
| 85 | +function sleep() { |
| 86 | + return new Promise((r) => { |
| 87 | + setTimeout(r, 1); |
| 88 | + }); |
| 89 | +} |
| 90 | + |
84 | 91 | describe('GraphQL-HTTP tests for connect', () => {
|
85 | 92 | runTests(() => {
|
86 | 93 | const app = connect();
|
@@ -2389,9 +2396,7 @@ function runTests(server: Server) {
|
2389 | 2396 | graphqlHTTP(() => ({
|
2390 | 2397 | schema: TestSchema,
|
2391 | 2398 | async *customExecuteFn() {
|
2392 |
| - await new Promise((r) => { |
2393 |
| - setTimeout(r, 1); |
2394 |
| - }); |
| 2399 | + await sleep(); |
2395 | 2400 | yield {
|
2396 | 2401 | data: {
|
2397 | 2402 | test2: 'Modification',
|
@@ -2436,6 +2441,131 @@ function runTests(server: Server) {
|
2436 | 2441 | ].join('\r\n'),
|
2437 | 2442 | );
|
2438 | 2443 | });
|
| 2444 | + |
| 2445 | + it('calls return on underlying async iterable when connection is closed', async () => { |
| 2446 | + const app = server(); |
| 2447 | + const fakeReturn = sinon.fake(); |
| 2448 | + |
| 2449 | + app.get( |
| 2450 | + urlString(), |
| 2451 | + graphqlHTTP(() => ({ |
| 2452 | + schema: TestSchema, |
| 2453 | + // custom iterable keeps yielding until return is called |
| 2454 | + customExecuteFn() { |
| 2455 | + let returned = false; |
| 2456 | + return { |
| 2457 | + [Symbol.asyncIterator]: () => ({ |
| 2458 | + next: async () => { |
| 2459 | + await sleep(); |
| 2460 | + if (returned) { |
| 2461 | + return { value: undefined, done: true }; |
| 2462 | + } |
| 2463 | + return { |
| 2464 | + value: { data: { test: 'Hello, World' }, hasNext: true }, |
| 2465 | + done: false, |
| 2466 | + }; |
| 2467 | + }, |
| 2468 | + return: () => { |
| 2469 | + returned = true; |
| 2470 | + fakeReturn(); |
| 2471 | + return Promise.resolve({ value: undefined, done: true }); |
| 2472 | + }, |
| 2473 | + }), |
| 2474 | + }; |
| 2475 | + }, |
| 2476 | + })), |
| 2477 | + ); |
| 2478 | + |
| 2479 | + const request = app |
| 2480 | + .request() |
| 2481 | + .get(urlString({ query: '{test}' })) |
| 2482 | + .parse((res, cb) => { |
| 2483 | + res.on('data', (data) => { |
| 2484 | + res.text = `${res.text || ''}${data.toString('utf8') as string}`; |
| 2485 | + ((res as unknown) as http.IncomingMessage).destroy(); |
| 2486 | + }); |
| 2487 | + res.on('end', (err) => { |
| 2488 | + cb(err, null); |
| 2489 | + }); |
| 2490 | + }); |
| 2491 | + |
| 2492 | + const response = await request; |
| 2493 | + await sleep(); |
| 2494 | + expect(response.status).to.equal(200); |
| 2495 | + expect(response.text).to.equal( |
| 2496 | + [ |
| 2497 | + '', |
| 2498 | + '---', |
| 2499 | + 'Content-Type: application/json; charset=utf-8', |
| 2500 | + 'Content-Length: 47', |
| 2501 | + '', |
| 2502 | + '{"data":{"test":"Hello, World"},"hasNext":true}', |
| 2503 | + '', |
| 2504 | + ].join('\r\n'), |
| 2505 | + ); |
| 2506 | + expect(fakeReturn.callCount).to.equal(1); |
| 2507 | + }); |
| 2508 | + |
| 2509 | + it('handles return function on async iterable that throws', async () => { |
| 2510 | + const app = server(); |
| 2511 | + |
| 2512 | + app.get( |
| 2513 | + urlString(), |
| 2514 | + graphqlHTTP(() => ({ |
| 2515 | + schema: TestSchema, |
| 2516 | + // custom iterable keeps yielding until return is called |
| 2517 | + customExecuteFn() { |
| 2518 | + let returned = false; |
| 2519 | + return { |
| 2520 | + [Symbol.asyncIterator]: () => ({ |
| 2521 | + next: async () => { |
| 2522 | + await sleep(); |
| 2523 | + if (returned) { |
| 2524 | + return { value: undefined, done: true }; |
| 2525 | + } |
| 2526 | + return { |
| 2527 | + value: { data: { test: 'Hello, World' }, hasNext: true }, |
| 2528 | + done: false, |
| 2529 | + }; |
| 2530 | + }, |
| 2531 | + return: () => { |
| 2532 | + returned = true; |
| 2533 | + return Promise.reject(new Error('Throws!')); |
| 2534 | + }, |
| 2535 | + }), |
| 2536 | + }; |
| 2537 | + }, |
| 2538 | + })), |
| 2539 | + ); |
| 2540 | + |
| 2541 | + const request = app |
| 2542 | + .request() |
| 2543 | + .get(urlString({ query: '{test}' })) |
| 2544 | + .parse((res, cb) => { |
| 2545 | + res.on('data', (data) => { |
| 2546 | + res.text = `${res.text || ''}${data.toString('utf8') as string}`; |
| 2547 | + ((res as unknown) as http.IncomingMessage).destroy(); |
| 2548 | + }); |
| 2549 | + res.on('end', (err) => { |
| 2550 | + cb(err, null); |
| 2551 | + }); |
| 2552 | + }); |
| 2553 | + |
| 2554 | + const response = await request; |
| 2555 | + await sleep(); |
| 2556 | + expect(response.status).to.equal(200); |
| 2557 | + expect(response.text).to.equal( |
| 2558 | + [ |
| 2559 | + '', |
| 2560 | + '---', |
| 2561 | + 'Content-Type: application/json; charset=utf-8', |
| 2562 | + 'Content-Length: 47', |
| 2563 | + '', |
| 2564 | + '{"data":{"test":"Hello, World"},"hasNext":true}', |
| 2565 | + '', |
| 2566 | + ].join('\r\n'), |
| 2567 | + ); |
| 2568 | + }); |
2439 | 2569 | });
|
2440 | 2570 |
|
2441 | 2571 | describe('Custom parse function', () => {
|
|
0 commit comments