Skip to content

Commit

Permalink
Fix leaks on integration tests (#3211)
Browse files Browse the repository at this point in the history
* Fix leaks

* fix: use non leaking fetch version

---------

Co-authored-by: Laurin Quast <[email protected]>
  • Loading branch information
ardatan and n1ru4l authored Mar 26, 2024
1 parent ea3e7c0 commit f9133fb
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 89 deletions.
40 changes: 16 additions & 24 deletions examples/fastify/__integration-tests__/fastify.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { FastifyInstance } from 'fastify';
import request from 'supertest';
import { fetch } from '@whatwg-node/fetch';
import { eventStream } from '../../../packages/graphql-yoga/__tests__/utilities.js';
import { buildApp } from '../src/app.js';

describe('fastify example integration', () => {
let app: FastifyInstance;
beforeEach(() => {
[app] = buildApp(false);
return app.ready();
});
afterEach(() => {
return app.close();
});
it('sends GraphiQL', async () => {
const [app] = buildApp(false);
await app.ready();
const response = await request(app.server).get('/graphql').set({
accept: 'text/html',
});
Expand All @@ -15,8 +23,6 @@ describe('fastify example integration', () => {
});

it('handles query operation via POST', async () => {
const [app] = buildApp(false);
await app.ready();
const response = await request(app.server)
.post('/graphql')
.set({ 'content-type': 'application/json' })
Expand All @@ -39,8 +45,6 @@ describe('fastify example integration', () => {
});

it("exposes fastify's request and reply objects", async () => {
const [app] = buildApp(false);
await app.ready();
const response = await request(app.server)
.post('/graphql')
.set({ 'content-type': 'application/json' })
Expand All @@ -63,8 +67,6 @@ describe('fastify example integration', () => {
});

it('handles query operation via GET', async () => {
const [app] = buildApp(false);
await app.ready();
const response = await request(app.server)
.get('/graphql')
.query({
Expand All @@ -84,8 +86,6 @@ describe('fastify example integration', () => {
});

it('handles mutation operation via POST', async () => {
const [app] = buildApp(false);
await app.ready();
const response = await request(app.server)
.post('/graphql')
.set({ 'content-type': 'application/json' })
Expand All @@ -108,8 +108,6 @@ describe('fastify example integration', () => {
});

it('rejects mutation operation via GET with an useful error message', async () => {
const [app] = buildApp(false);
await app.ready();
const response = await request(app.server)
.get('/graphql')
.query({
Expand All @@ -130,8 +128,6 @@ describe('fastify example integration', () => {
});

it('handles subscription operations via GET', async () => {
const [app] = buildApp(false);
await app.ready();
const response = await request(app.server)
.get('/graphql')
.set({ accept: 'text/event-stream' })
Expand Down Expand Up @@ -184,8 +180,6 @@ describe('fastify example integration', () => {
});

it('handles subscription operations via POST', async () => {
const [app] = buildApp(false);
await app.ready();
const response = await request(app.server)
.post('/graphql')
.set({
Expand Down Expand Up @@ -241,8 +235,6 @@ describe('fastify example integration', () => {
});

it('should handle file uploads', async () => {
const [app] = buildApp(false);
await app.ready();
const response = await request(app.server)
.post('/graphql')
.field(
Expand All @@ -266,8 +258,6 @@ describe('fastify example integration', () => {
});

it('request cancelation', async () => {
const [app] = buildApp(false);
await app.ready();
const slowFieldResolverInvoked = createDeferred();
const slowFieldResolverCanceled = createDeferred();
const address = await app.listen({
Expand Down Expand Up @@ -308,16 +298,16 @@ describe('fastify example integration', () => {

await slowFieldResolverInvoked.promise;
abortController.abort();
await expect(response$).rejects.toMatchInlineSnapshot(`DOMException {}`);
await expect(response$).rejects.toMatchInlineSnapshot(
`[AbortError: The operation was aborted]`,
);
await slowFieldResolverCanceled.promise;
} finally {
app.log.info = info;
}
});

it('subscription cancelation', async () => {
const [app] = buildApp(false);
await app.ready();
const cancelationIsLoggedPromise = createDeferred();
const address = await app.listen({
port: 0,
Expand Down Expand Up @@ -357,7 +347,9 @@ describe('fastify example integration', () => {
const next = await iterator.next();
expect(next.value).toEqual({ data: { countdown: 10 } });
abortController.abort();
await expect(iterator.next()).rejects.toMatchInlineSnapshot(`DOMException {}`);
await expect(iterator.next()).rejects.toMatchInlineSnapshot(
`[AbortError: The operation was aborted]`,
);
await cancelationIsLoggedPromise.promise;
} finally {
app.log.info = info;
Expand Down
123 changes: 58 additions & 65 deletions packages/graphql-yoga/__integration-tests__/node-http.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import { fetch } from '@whatwg-node/fetch';
import { createGraphQLError, createSchema, createYoga, Plugin } from '../src/index.js';

describe('node-http', () => {
let server: ReturnType<typeof createServer>;
afterEach(done => {
server.closeAllConnections();
server.close(done);
});
it('should expose Node req and res objects in the context', async () => {
const yoga = createYoga<{
req: IncomingMessage;
Expand All @@ -22,19 +27,15 @@ describe('node-http', () => {
},
}),
});
const server = createServer(yoga);
server = createServer(yoga);
await new Promise<void>(resolve => server.listen(0, resolve));
const port = (server.address() as AddressInfo).port;

try {
const response = await fetch(`http://localhost:${port}/graphql?query=query{isNode}`);
expect(response.status).toBe(200);
const body = await response.json();
expect(body.errors).toBeUndefined();
expect(body.data.isNode).toBe(true);
} finally {
await new Promise<void>(resolve => server.close(() => resolve()));
}
const response = await fetch(`http://localhost:${port}/graphql?query=query{isNode}`);
expect(response.status).toBe(200);
const body = await response.json();
expect(body.errors).toBeUndefined();
expect(body.data.isNode).toBe(true);
});

it('should set status text by status code', async () => {
Expand Down Expand Up @@ -64,7 +65,7 @@ describe('node-http', () => {
}),
logging: false,
});
const server = createServer(yoga);
server = createServer(yoga);
await new Promise<void>(resolve => server.listen(0, resolve));
const port = (server.address() as AddressInfo).port;

Expand Down Expand Up @@ -119,32 +120,28 @@ describe('node-http', () => {
}),
plugins: [plugin],
});
const server = createServer(yoga);
server = createServer(yoga);
await new Promise<void>(resolve => server.listen(0, resolve));
const port = (server.address() as AddressInfo).port;
try {
const controller = new AbortController();
const response$ = fetch(`http://localhost:${port}/graphql`, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
query: /* GraphQL */ `
query {
hi
}
`,
}),
signal: controller.signal,
});
await d.promise;
controller.abort();
await expect(response$).rejects.toThrow('The operation was aborted');
await didAbortD.promise;
} finally {
await new Promise<void>(resolve => server.close(() => resolve()));
}
const controller = new AbortController();
const response$ = fetch(`http://localhost:${port}/graphql`, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
query: /* GraphQL */ `
query {
hi
}
`,
}),
signal: controller.signal,
});
await d.promise;
controller.abort();
await expect(response$).rejects.toThrow('The operation was aborted');
await didAbortD.promise;
});

it('request cancellation causes no more resolvers being invoked', async () => {
Expand Down Expand Up @@ -181,40 +178,36 @@ describe('node-http', () => {
},
}),
});
const server = createServer(yoga);
server = createServer(yoga);
await new Promise<void>(resolve => server.listen(0, resolve));
const port = (server.address() as AddressInfo).port;
try {
const controller = new AbortController();
const response$ = fetch(`http://localhost:${port}/graphql`, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
query: /* GraphQL */ `
query {
slow {
field
}
const controller = new AbortController();
const response$ = fetch(`http://localhost:${port}/graphql`, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
query: /* GraphQL */ `
query {
slow {
field
}
`,
}),
signal: controller.signal,
});
}
`,
}),
signal: controller.signal,
});

await didInvokeSlowResolverD.promise;
controller.abort();
await expect(response$).rejects.toThrow('The operation was aborted');
// wait a few milliseconds to ensure server-side cancellation logic runs
await new Promise<void>(resolve => setTimeout(resolve, 10));
didCancelD.resolve();
// wait a few milliseconds to allow the nested field resolver to run (if cancellation logic is incorrect)
await new Promise<void>(resolve => setTimeout(resolve, 10));
expect(didInvokedNestedField).toBe(false);
} finally {
await new Promise<void>(resolve => server.close(() => resolve()));
}
await didInvokeSlowResolverD.promise;
controller.abort();
await expect(response$).rejects.toThrow('The operation was aborted');
// wait a few milliseconds to ensure server-side cancellation logic runs
await new Promise<void>(resolve => setTimeout(resolve, 10));
didCancelD.resolve();
// wait a few milliseconds to allow the nested field resolver to run (if cancellation logic is incorrect)
await new Promise<void>(resolve => setTimeout(resolve, 10));
expect(didInvokedNestedField).toBe(false);
});
});

Expand Down

0 comments on commit f9133fb

Please sign in to comment.