Skip to content

Commit 64273a7

Browse files
Fix wrangler test process helpers
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
1 parent 7f0f5e4 commit 64273a7

3 files changed

Lines changed: 91 additions & 148 deletions

File tree

mcp/mcp-server-e2e.test.ts

Lines changed: 14 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,22 @@ import {
1414
type ContentBlock,
1515
} from '@modelcontextprotocol/sdk/types.js'
1616
import getPort from 'get-port'
17-
import { spawn, type ChildProcess } from 'node:child_process'
17+
import { spawn } from 'node:child_process'
1818
import { mkdtemp, readdir, rm } from 'node:fs/promises'
1919
import { tmpdir } from 'node:os'
2020
import { join } from 'node:path'
2121
import { fileURLToPath } from 'node:url'
22+
import {
23+
captureOutput,
24+
createExitPromise,
25+
formatOutput,
26+
stopProcess,
27+
type TrackedProcess,
28+
} from '#test-support/process-utils.ts'
2229

2330
const projectRoot = fileURLToPath(new URL('..', import.meta.url))
2431
const migrationsDir = join(projectRoot, 'migrations')
25-
const nodeBin = process.execPath
26-
const wranglerCli = join(projectRoot, 'node_modules', 'wrangler', 'wrangler-dist', 'cli.js')
32+
const bunBin = process.execPath
2733
const defaultTimeoutMs = 60_000
2834
const calculatorUiResourceUri = 'ui://calculator-app/entry-point.html'
2935

@@ -32,11 +38,6 @@ const passwordSaltBytes = 16
3238
const passwordHashBytes = 32
3339
const passwordHashIterations = 100_000
3440

35-
type TrackedProcess = {
36-
proc: ChildProcess
37-
exitPromise: Promise<number | null>
38-
}
39-
4041
function delay(ms: number) {
4142
return new Promise((resolve) => setTimeout(resolve, ms))
4243
}
@@ -77,8 +78,8 @@ function escapeSql(value: string) {
7778

7879
async function runWrangler(args: Array<string>) {
7980
const proc = spawn(
80-
nodeBin,
81-
['--no-warnings', '--experimental-vm-modules', wranglerCli, ...args],
81+
bunBin,
82+
['x', 'wrangler', ...args],
8283
{
8384
cwd: projectRoot,
8485
stdio: ['ignore', 'pipe', 'pipe'],
@@ -188,32 +189,6 @@ function streamToText(
188189
})
189190
}
190191

191-
function captureOutput(stream: NodeJS.ReadableStream | null | undefined) {
192-
let output = ''
193-
if (!stream) {
194-
return () => output
195-
}
196-
stream.setEncoding('utf8')
197-
stream.on('data', (chunk) => {
198-
output += chunk
199-
})
200-
stream.on('error', () => {
201-
// Ignore stream errors while capturing output.
202-
})
203-
return () => output
204-
}
205-
206-
function formatOutput(stdout: string, stderr: string) {
207-
const snippets: Array<string> = []
208-
if (stdout.trim()) {
209-
snippets.push(`stdout: ${stdout.trim().slice(-2000)}`)
210-
}
211-
if (stderr.trim()) {
212-
snippets.push(`stderr: ${stderr.trim().slice(-2000)}`)
213-
}
214-
return snippets.length > 0 ? ` Output:\n${snippets.join('\n')}` : ''
215-
}
216-
217192
async function waitForServer(
218193
origin: string,
219194
process: TrackedProcess,
@@ -262,36 +237,6 @@ async function waitForServer(
262237
)
263238
}
264239

265-
function createExitPromise(proc: ChildProcess): Promise<number | null> {
266-
if (proc.exitCode !== null || proc.signalCode !== null) {
267-
return Promise.resolve(proc.exitCode)
268-
}
269-
return new Promise((resolve) => {
270-
const finalize = (code: number | null) => {
271-
proc.off('error', onError)
272-
proc.off('exit', onExit)
273-
resolve(code)
274-
}
275-
const onError = () => finalize(null)
276-
const onExit = (code: number | null) => finalize(code)
277-
proc.once('error', onError)
278-
proc.once('exit', onExit)
279-
})
280-
}
281-
282-
async function stopProcess({ proc, exitPromise }: TrackedProcess) {
283-
let exited = false
284-
void exitPromise.then(() => {
285-
exited = true
286-
})
287-
proc.kill('SIGINT')
288-
await Promise.race([exitPromise, delay(5_000)])
289-
if (!exited) {
290-
proc.kill('SIGKILL')
291-
await exitPromise
292-
}
293-
}
294-
295240
async function startDevServer(persistDir: string) {
296241
const port = await getPort({ host: '127.0.0.1' })
297242
const inspectorPortBase =
@@ -305,11 +250,10 @@ async function startDevServer(persistDir: string) {
305250
})
306251
const origin = `http://127.0.0.1:${port}`
307252
const proc = spawn(
308-
nodeBin,
253+
bunBin,
309254
[
310-
'--no-warnings',
311-
'--experimental-vm-modules',
312-
wranglerCli,
255+
'x',
256+
'wrangler',
313257
'dev',
314258
'--local',
315259
'--env',

mock-servers/resend/resend-mock.test.ts

Lines changed: 15 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,22 @@
11
/// <reference types="bun" />
22
import { expect, test } from 'vitest'
33
import getPort from 'get-port'
4-
import { spawn, spawnSync, type ChildProcess } from 'node:child_process'
4+
import { spawn, spawnSync } from 'node:child_process'
55
import { setTimeout as delay } from 'node:timers/promises'
6-
import { join } from 'node:path'
76
import { createTemporaryDirectory } from '#tools/temp-directory.ts'
7+
import {
8+
captureOutput,
9+
createExitPromise,
10+
formatOutput,
11+
stopProcess,
12+
type TrackedProcess,
13+
} from '#test-support/process-utils.ts'
814

915
const workerConfig = 'mock-servers/resend/wrangler.jsonc'
1016
const projectRoot = process.cwd()
11-
const nodeBin = process.execPath
12-
const wranglerCli = join(
13-
projectRoot,
14-
'node_modules',
15-
'wrangler',
16-
'wrangler-dist',
17-
'cli.js',
18-
)
17+
const bunBin = process.execPath
1918
const defaultTimeoutMs = 60_000
2019

21-
type TrackedProcess = {
22-
proc: ChildProcess
23-
exitPromise: Promise<number | null>
24-
}
25-
26-
function captureOutput(stream: NodeJS.ReadableStream | null | undefined) {
27-
let output = ''
28-
if (!stream) {
29-
return () => output
30-
}
31-
stream.setEncoding('utf8')
32-
stream.on('data', (chunk) => {
33-
output += chunk
34-
})
35-
stream.on('error', () => {
36-
// Ignore stream errors while capturing output.
37-
})
38-
return () => output
39-
}
40-
41-
function formatOutput(stdout: string, stderr: string) {
42-
const snippets: Array<string> = []
43-
if (stdout.trim()) {
44-
snippets.push(`stdout: ${stdout.trim().slice(-2000)}`)
45-
}
46-
if (stderr.trim()) {
47-
snippets.push(`stderr: ${stderr.trim().slice(-2000)}`)
48-
}
49-
return snippets.length > 0 ? ` Output:\n${snippets.join('\n')}` : ''
50-
}
51-
5220
function bufferToText(buffer: Uint8Array<ArrayBufferLike> | null | undefined) {
5321
return buffer ? Buffer.from(buffer).toString() : ''
5422
}
@@ -103,11 +71,10 @@ async function waitForMockServer(
10371

10472
function applyResendMockMigrations(persistDir: string) {
10573
const proc = spawnSync(
106-
nodeBin,
74+
bunBin,
10775
[
108-
'--no-warnings',
109-
'--experimental-vm-modules',
110-
wranglerCli,
76+
'x',
77+
'wrangler',
11178
'd1',
11279
'migrations',
11380
'apply',
@@ -136,35 +103,6 @@ function applyResendMockMigrations(persistDir: string) {
136103
}
137104
}
138105

139-
function createExitPromise(proc: ChildProcess): Promise<number | null> {
140-
if (proc.exitCode !== null || proc.signalCode !== null) {
141-
return Promise.resolve(proc.exitCode)
142-
}
143-
return new Promise((resolve) => {
144-
const finalize = (code: number | null) => {
145-
proc.off('error', onError)
146-
proc.off('exit', onExit)
147-
resolve(code)
148-
}
149-
const onError = () => finalize(null)
150-
const onExit = (code: number | null) => finalize(code)
151-
proc.once('error', onError)
152-
proc.once('exit', onExit)
153-
})
154-
}
155-
156-
async function stopProcess({ proc, exitPromise }: TrackedProcess) {
157-
proc.kill('SIGINT')
158-
const exited = await Promise.race([
159-
exitPromise.then(() => true),
160-
delay(5_000).then(() => false),
161-
])
162-
if (!exited) {
163-
proc.kill('SIGKILL')
164-
await exitPromise
165-
}
166-
}
167-
168106
async function startMockResendWorker(persistDir: string, token: string) {
169107
const port = await getPort({ host: '127.0.0.1' })
170108
const inspectorPortBase =
@@ -179,11 +117,10 @@ async function startMockResendWorker(persistDir: string, token: string) {
179117
const origin = `http://127.0.0.1:${port}`
180118
applyResendMockMigrations(persistDir)
181119
const proc = spawn(
182-
nodeBin,
120+
bunBin,
183121
[
184-
'--no-warnings',
185-
'--experimental-vm-modules',
186-
wranglerCli,
122+
'x',
123+
'wrangler',
187124
'dev',
188125
'--local',
189126
'--env',

test-support/process-utils.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { setTimeout as delay } from 'node:timers/promises'
2+
import type { ChildProcess } from 'node:child_process'
3+
4+
export type TrackedProcess = {
5+
proc: ChildProcess
6+
exitPromise: Promise<number | null>
7+
}
8+
9+
export function captureOutput(stream: NodeJS.ReadableStream | null | undefined) {
10+
let output = ''
11+
if (!stream) {
12+
return () => output
13+
}
14+
stream.setEncoding('utf8')
15+
stream.on('data', (chunk) => {
16+
output += chunk
17+
})
18+
stream.on('error', () => {
19+
// Ignore stream errors while capturing output.
20+
})
21+
return () => output
22+
}
23+
24+
export function formatOutput(stdout: string, stderr: string) {
25+
const snippets: Array<string> = []
26+
if (stdout.trim()) {
27+
snippets.push(`stdout: ${stdout.trim().slice(-2000)}`)
28+
}
29+
if (stderr.trim()) {
30+
snippets.push(`stderr: ${stderr.trim().slice(-2000)}`)
31+
}
32+
return snippets.length > 0 ? ` Output:\n${snippets.join('\n')}` : ''
33+
}
34+
35+
export function createExitPromise(proc: ChildProcess): Promise<number | null> {
36+
if (proc.exitCode !== null || proc.signalCode !== null) {
37+
return Promise.resolve(proc.exitCode)
38+
}
39+
return new Promise((resolve) => {
40+
const finalize = (code: number | null) => {
41+
proc.off('error', onError)
42+
proc.off('exit', onExit)
43+
resolve(code)
44+
}
45+
const onError = () => finalize(null)
46+
const onExit = (code: number | null) => finalize(code)
47+
proc.once('error', onError)
48+
proc.once('exit', onExit)
49+
})
50+
}
51+
52+
export async function stopProcess({ proc, exitPromise }: TrackedProcess) {
53+
proc.kill('SIGINT')
54+
const exited = await Promise.race([
55+
exitPromise.then(() => true),
56+
delay(5_000).then(() => false),
57+
])
58+
if (!exited) {
59+
proc.kill('SIGKILL')
60+
await exitPromise
61+
}
62+
}

0 commit comments

Comments
 (0)