-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Test against Node 20, update tests for compatibility #7636
Changes from all commits
06b42c4
3a75978
782bcc8
d2fba10
3bb0709
9a61ae2
6265dcd
e6e1aa0
c7e0570
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
'@apollo/server-integration-testsuite': patch | ||
'@apollo/server': patch | ||
--- | ||
|
||
Update test suite for compatibility with Node v20 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -193,7 +193,7 @@ it('supporting doubly-encoded variables example from migration guide', async () | |
query: 'query Hello($s: String!){hello(s: $s)}', | ||
variables: '{malformed JSON}', | ||
}) | ||
.expect(400, 'Unexpected token m in JSON at position 1'); | ||
.expect(400, /in JSON at position 1/); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Error message changed in Node 20, this substring is compatible across all of our supported versions. |
||
|
||
await server.stop(); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,7 +32,6 @@ const a: any = require('awaiting'); | |
const request: any = require('requisition'); | ||
import fs from 'fs'; | ||
import { Stopper } from '../../../plugin/drainHttpServer/stoppable'; | ||
import child from 'child_process'; | ||
import path from 'path'; | ||
import type { AddressInfo } from 'net'; | ||
import { describe, it, expect, afterEach, beforeEach } from '@jest/globals'; | ||
|
@@ -113,7 +112,7 @@ Object.keys(schemes).forEach((schemeName) => { | |
const err = await a.failure( | ||
request(`${schemeName}://localhost:${p}`).agent(scheme.agent()), | ||
); | ||
expect(err.message).toMatch(/ECONNREFUSED/); | ||
expect(err.code).toMatch(/ECONNREFUSED/); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
expect(closed).toBe(1); | ||
}); | ||
|
||
|
@@ -135,9 +134,54 @@ Object.keys(schemes).forEach((schemeName) => { | |
scheme.agent({ keepAlive: true }), | ||
), | ||
); | ||
expect(err.message).toMatch(/ECONNREFUSED/); | ||
expect(closed).toBe(0); | ||
expect(err.code).toMatch(/ECONNREFUSED/); | ||
|
||
// Node 19 (http) and 20.4+ (https) more aggressively close idle | ||
// connections. `Stopper` is no longer needed for the purpose of closing | ||
// idle connections in these versions. However, `Stopper` _is_ still | ||
// useful for gracefully finishing in-flight requests within the timeout | ||
// (and aborting requests beyond the timeout). | ||
const isNode20 = !!process.version.match(/^v20\./); | ||
expect(closed).toBe(isNode20 ? 1 : 0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (currently) unexplained change in behavior in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or nodejs/node#46331 ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For future reference, @glasser helped me pin this to changes made in v19 and v20.3 |
||
}); | ||
|
||
// This test specifically added for Node 20 fails for Node 14. Just going | ||
// to skip it since we're dropping Node 14 soon anyway. | ||
const node14 = !!process.version.match(/^v14\./); | ||
(node14 ? it.skip : it)( | ||
'with unfinished requests', | ||
async () => { | ||
const server = scheme.server(async (_req, res) => { | ||
res.writeHead(200); | ||
res.write('hi'); // note lack of end()! | ||
}); | ||
// The server will prevent itself from closing while the connection | ||
// remains open (default no timeout). This will close the connection | ||
// after 100ms so the test can finish. | ||
server.setTimeout(100); | ||
|
||
server.listen(0); | ||
const p = port(server); | ||
|
||
const response = await request( | ||
`${schemeName}://localhost:${p}`, | ||
).agent(scheme.agent({ keepAlive: true })); | ||
// ensure we got the headers, etc. | ||
expect(response.status).toBe(200); | ||
|
||
server.close(); | ||
await a.event(server, 'close'); | ||
|
||
try { | ||
await response.text(); | ||
} catch (e: any) { | ||
expect(e.code).toMatch(/ECONNRESET/); | ||
} | ||
// ensure the expectation in the catch block is reached (+ the one above) | ||
expect.assertions(2); | ||
}, | ||
35000, | ||
); | ||
}); | ||
|
||
describe('Stopper', function () { | ||
|
@@ -159,7 +203,7 @@ Object.keys(schemes).forEach((schemeName) => { | |
const err = await a.failure( | ||
request(`${schemeName}://localhost:${p}`).agent(scheme.agent()), | ||
); | ||
expect(err.message).toMatch(/ECONNREFUSED/); | ||
expect(err.code).toMatch(/ECONNREFUSED/); | ||
|
||
expect(closed).toBe(1); | ||
expect(gracefully).toBe(true); | ||
|
@@ -185,7 +229,7 @@ Object.keys(schemes).forEach((schemeName) => { | |
scheme.agent({ keepAlive: true }), | ||
), | ||
); | ||
expect(err.message).toMatch(/ECONNREFUSED/); | ||
expect(err.code).toMatch(/ECONNREFUSED/); | ||
|
||
expect(closed).toBe(1); | ||
expect(gracefully).toBe(true); | ||
|
@@ -301,12 +345,21 @@ Object.keys(schemes).forEach((schemeName) => { | |
|
||
if (schemeName === 'http') { | ||
it('with in-flights finishing before grace period ends', async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test was causing an open handle / |
||
const file = path.join(__dirname, 'stoppable', 'server.js'); | ||
const server = child.spawn('node', [file]); | ||
const [data] = await a.event(server.stdout, 'data'); | ||
const port = +data.toString(); | ||
expect(typeof port).toBe('number'); | ||
const res = await request(`${schemeName}://localhost:${port}/`).agent( | ||
let stopper: Stopper; | ||
const killServerBarrier = resolvable(); | ||
const server = http.createServer(async (_, res) => { | ||
res.writeHead(200); | ||
res.write('hello'); | ||
|
||
await killServerBarrier; | ||
res.end('world'); | ||
await stopper.stop(); | ||
}); | ||
stopper = new Stopper(server); | ||
server.listen(0); | ||
const p = port(server); | ||
|
||
const res = await request(`${schemeName}://localhost:${p}/`).agent( | ||
scheme.agent({ keepAlive: true }), | ||
); | ||
let gotBody = false; | ||
|
@@ -320,13 +373,13 @@ Object.keys(schemes).forEach((schemeName) => { | |
expect(gotBody).toBe(false); | ||
|
||
// Tell the server that its request should finish. | ||
server.kill('SIGUSR1'); | ||
killServerBarrier.resolve(); | ||
|
||
const body = await bodyPromise; | ||
expect(gotBody).toBe(true); | ||
expect(body).toBe('helloworld'); | ||
|
||
// Wait for subprocess to go away. | ||
// Wait for server to close. | ||
await a.event(server, 'close'); | ||
}); | ||
} | ||
|
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error message changed in Node 20, this substring is compatible across all of our supported versions.