Skip to content

Commit 1ea0bdc

Browse files
zoidbergclawdclaude
andcommitted
Fix deploy phase tests for built-in static server
Update tests that were asserting npx serve spawn behavior to verify startStaticServer() is called with the correct directory instead. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 02a7aea commit 1ea0bdc

1 file changed

Lines changed: 22 additions & 47 deletions

File tree

backend/src/tests/behavioral/deployPhase.web.test.ts

Lines changed: 22 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { EventEmitter } from 'node:events';
88
import type { PhaseContext } from '../../services/phases/types.js';
99
import type { BuildSession } from '../../models/session.js';
1010

11-
// Mock child_process to prevent spawning real servers and opening browser tabs
11+
// Mock child_process to prevent spawning real processes
1212
vi.mock('node:child_process', async (importOriginal) => {
1313
const original = await importOriginal<typeof import('node:child_process')>();
1414
return {
@@ -20,15 +20,19 @@ vi.mock('node:child_process', async (importOriginal) => {
2020
proc.stderr = new EventEmitter();
2121
proc.pid = 99999;
2222
proc.kill = vi.fn();
23-
// Simulate serve printing its listen URL on stderr (serve v14 uses stderr for info)
24-
setTimeout(() => {
25-
proc.stderr.emit('data', Buffer.from(' INFO Accepting connections at http://localhost:4567\n'));
26-
}, 50);
23+
setTimeout(() => proc.emit('close', 0), 50);
2724
return proc;
2825
}),
2926
};
3027
});
3128

29+
// Mock staticServer to prevent binding real ports
30+
vi.mock('../../utils/staticServer.js', () => ({
31+
startStaticServer: vi.fn((_dir: string, port: number) =>
32+
Promise.resolve({ url: `http://localhost:${port}`, close: vi.fn() }),
33+
),
34+
}));
35+
3236
// Inline mock factories for services
3337
function makeMockHardwareService() {
3438
return {
@@ -196,10 +200,9 @@ describe('DeployPhase - deployWeb', () => {
196200
if (result.process) result.process.kill();
197201
});
198202

199-
it('spawns serve with cwd and -p flag, not positional dir arg (serve v14 compat)', async () => {
200-
const { spawn } = await import('node:child_process');
201-
// Clear mock calls from prior tests so we only see this test's spawn
202-
(spawn as any).mockClear();
203+
it('uses built-in static server with correct serve directory', async () => {
204+
const { startStaticServer } = await import('../../utils/staticServer.js');
205+
(startStaticServer as any).mockClear();
203206
const { ctx } = makeCtx({ spec: { deployment: { target: 'web' } } });
204207
ctx.nuggetDir = tmpDir;
205208
// Create index.html in src/ subdir so serveDir resolves to src/
@@ -209,53 +212,25 @@ describe('DeployPhase - deployWeb', () => {
209212

210213
const result = await phase.deployWeb(ctx);
211214

212-
// Find the spawn call for 'npx serve'
213-
const calls = (spawn as any).mock.calls as any[][];
214-
const serveCall = calls.find(
215-
(c: any[]) => c[0] === 'npx' && c[1]?.includes('serve'),
216-
);
217-
expect(serveCall).toBeDefined();
218-
219-
const [, args, opts] = serveCall!;
220-
// Must use -p flag for port, NOT -l with raw number
221-
expect(args).toContain('-p');
222-
expect(args).not.toContain('-l');
223-
// Must NOT pass directory as positional arg (broken in serve v14)
224-
expect(args).not.toContain(srcDir);
225-
expect(args).not.toContain(tmpDir);
226-
// Must use cwd to set serve directory
227-
expect(opts.cwd).toBe(srcDir);
228-
// Must not use removed --no-clipboard flag
229-
expect(args).not.toContain('--no-clipboard');
215+
expect(startStaticServer).toHaveBeenCalledOnce();
216+
const [dir] = (startStaticServer as any).mock.calls[0];
217+
expect(dir).toBe(srcDir);
218+
expect(result.url).toMatch(/^http:\/\/localhost:\d+$/);
230219

231-
if (result.process) result.process.kill();
220+
if (result.staticServer) result.staticServer.close();
232221
});
233222

234-
it('uses URL from serve stdout instead of assumed port', async () => {
223+
it('emits deploy_complete with URL from static server', async () => {
235224
const { ctx, events } = makeCtx({ spec: { deployment: { target: 'web' } } });
236225
ctx.nuggetDir = tmpDir;
237226

238227
const result = await phase.deployWeb(ctx);
239228

240-
// The mock emits "Accepting connections at http://localhost:4567"
241-
// Deploy should parse this and use port 4567, not the findFreePort result
242-
const complete = events.find(e => e.type === 'deploy_complete');
243-
expect(complete.url).toBe('http://localhost:4567');
244-
expect(result.url).toBe('http://localhost:4567');
245-
246-
if (result.process) result.process.kill();
247-
});
248-
249-
it('does not auto-open browser after starting server', async () => {
250-
const { execFile } = await import('node:child_process');
251-
const { ctx } = makeCtx({ spec: { deployment: { target: 'web' } } });
252-
ctx.nuggetDir = tmpDir;
253-
254-
const result = await phase.deployWeb(ctx);
255-
256-
expect(execFile).not.toHaveBeenCalled();
229+
const complete = events.find((e: any) => e.type === 'deploy_complete');
230+
expect(complete.url).toBeDefined();
231+
expect(result.url).toBe(complete.url);
257232

258-
if (result.process) result.process.kill();
233+
if (result.staticServer) result.staticServer.close();
259234
});
260235

261236
it('does not send deploy_checklist when no before_deploy rules exist', async () => {

0 commit comments

Comments
 (0)