Skip to content

Commit 1c5ab3d

Browse files
authored
test: address ENOSPC in integration test runs (#3326)
1 parent 378e4fd commit 1c5ab3d

File tree

4 files changed

+49
-1
lines changed

4 files changed

+49
-1
lines changed

tests/integration/use-cache.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ describe.skipIf(!nextVersionSatisfies('>=15.3.0-canary.13'))('use cache', () =>
165165
vi.stubEnv('NETLIFY_PURGE_API_TOKEN', 'fake-token')
166166
await startMockBlobStore(ctx as FixtureTestContext)
167167

168+
// we use same fixture for all the tests, which normally cleans up after each test
169+
// but here we want to control cleanup ourselves and only do that after all test suites did run
170+
ctx.skipAutoCleanup = true
171+
168172
await createFixture('use-cache', ctx)
169173
await runPlugin(ctx)
170174
})

tests/test-setup.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,7 @@ export async function afterTestCleanup({ cleanup }: FixtureTestContext) {
1414

1515
// cleanup after each test as a fallback if someone forgot to call it
1616
afterEach<FixtureTestContext>(async (ctx) => {
17-
await afterTestCleanup(ctx)
17+
if (ctx.skipAutoCleanup !== true) {
18+
await afterTestCleanup(ctx)
19+
}
1820
})

tests/utils/contexts.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ export interface FixtureTestContext extends TestContext {
1616
edgeFunctionPort: number
1717
edgeFunctionOutput: WriteStream
1818
cleanup?: (() => Promise<void>)[]
19+
skipAutoCleanup?: boolean
1920
}

tests/utils/fixture.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { execaCommand } from 'execa'
99
import getPort from 'get-port'
1010
import { spawn } from 'node:child_process'
1111
import { createWriteStream, existsSync } from 'node:fs'
12+
import { createRequire } from 'node:module'
1213
import { cp, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
1314
import { tmpdir } from 'node:os'
1415
import { basename, dirname, join, parse, relative } from 'node:path'
@@ -37,6 +38,12 @@ const bootstrapURL = await getBootstrapURL()
3738
const actualCwd = await vi.importActual<typeof import('process')>('process').then((p) => p.cwd())
3839
const eszipHelper = join(actualCwd, 'tools/deno/eszip.ts')
3940

41+
const require = createRequire(import.meta.url)
42+
const mod = require('module')
43+
44+
const originalRequire = mod.prototype.require
45+
const originalResolveFilename = mod._resolveFilename
46+
4047
async function installDependencies(cwd: string) {
4148
const NEXT_VERSION = process.env.NEXT_VERSION ?? 'latest'
4249
await setNextVersionInFixture(cwd, NEXT_VERSION, { silent: true })
@@ -110,6 +117,30 @@ export const createFixture = async (fixture: string, ctx: FixtureTestContext) =>
110117
// from any previous function invocations that might have run in the same process
111118
delete globalThis[Symbol.for('next.server.manifests')]
112119

120+
// require hook leaves modified "require" and "require.resolve" modified - we restore here to original
121+
// https://github.com/vercel/next.js/blob/812c26ab8741f68fbd6e2fe095510e0f03eac4c5/packages/next/src/server/require-hook.ts
122+
mod.prototype.require = originalRequire
123+
mod._resolveFilename = originalResolveFilename
124+
125+
// node environment baseline defines global WebSocket getter that requires compiled 'ws' package from first function modules
126+
// we need to reset the getter to not have it attempt to import 'ws' package from unrelated functions that might have already been deleted
127+
// https://github.com/vercel/next.js/blob/812c26ab8741f68fbd6e2fe095510e0f03eac4c5/packages/next/src/server/node-environment-baseline.ts#L11-L27
128+
// note that some next versions didn't have setter, so we can't just do "globalThis.WebSocket = undefined" as that would throw
129+
// "Cannot set property WebSocket of #<Object> which has only a getter" errors
130+
Object.defineProperty(globalThis, 'WebSocket', {
131+
get() {
132+
return undefined
133+
},
134+
set(value) {
135+
Object.defineProperty(globalThis, 'WebSocket', {
136+
configurable: true,
137+
writable: true,
138+
value,
139+
})
140+
},
141+
configurable: true,
142+
})
143+
113144
ctx.cwd = await mkdtemp(join(tmpdir(), 'opennextjs-netlify-'))
114145
vi.spyOn(process, 'cwd').mockReturnValue(ctx.cwd)
115146

@@ -147,6 +178,16 @@ export const createFixture = async (fixture: string, ctx: FixtureTestContext) =>
147178
} catch (error) {
148179
console.log(`Fixture '${fixture}' has failed to cleanup at '${ctx.cwd}'`, error)
149180
}
181+
if (ctx.functionDist) {
182+
try {
183+
await rm(ctx.functionDist, { recursive: true, force: true })
184+
} catch (error) {
185+
console.log(
186+
`Fixture's '${fixture}' bundled serverless function has failed to cleanup at '${ctx.cwd}'`,
187+
error,
188+
)
189+
}
190+
}
150191
})
151192
}
152193

0 commit comments

Comments
 (0)