Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions __tests__/production-smoke.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import { spawn } from 'node:child_process';
import { copyFile, mkdtemp, rm } from 'node:fs/promises';
import { createServer } from 'node:http';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

import { describe, expect, it, vi } from 'vitest';

import {
Expand Down Expand Up @@ -122,4 +128,111 @@ describe('production smoke harness', () => {
errors: ['connection refused'],
});
});

it('runs the CLI from a path containing spaces and exits nonzero when a public check fails', async () => {
const requestedPaths: string[] = [];
const server = createServer((request, response) => {
const path = new URL(request.url ?? '/', 'http://127.0.0.1').pathname;
requestedPaths.push(path);

if (path === '/api/proof/truehire-preview') {
response.writeHead(400, { 'content-type': 'application/json' });
response.end(JSON.stringify({ ok: false, error: 'Invalid preview URL.' }));
return;
}

if (path === '/jobs') {
response.writeHead(503, { 'content-type': 'text/html' });
response.end('<h1>Temporarily unavailable</h1>');
return;
}

response.writeHead(200, { 'content-type': 'text/html' });
response.end(
{
'/': '<h1>RolePatch</h1>',
'/pricing': '<h1>Tokens</h1>',
'/proof': '<h1>TrueHire proof project</h1><h2>Candidate proof profile</h2>',
'/settings': '<h1>Operational readiness</h1><h2>Chrome extension</h2>',
}[path] ?? '<h1>Unexpected route</h1>'
);
});

await new Promise<void>((resolve, reject) => {
server.once('error', reject);
server.listen(0, '127.0.0.1', resolve);
});

const address = server.address();
if (!address || typeof address === 'string') throw new Error('Expected an ephemeral TCP port');

const tempDirectory = await mkdtemp(join(process.cwd(), '.rolepatch smoke cli '));
try {
const sourcePath = join(
dirname(fileURLToPath(import.meta.url)),
'../scripts/production-smoke.mjs'
);
const spacedScriptPath = join(tempDirectory, 'production-smoke.mjs');
await copyFile(sourcePath, spacedScriptPath);

const { code, stdout, stderr } = await new Promise<{
code: number | null;
stdout: string;
stderr: string;
}>((resolve, reject) => {
const child = spawn(
process.execPath,
[spacedScriptPath, '--base-url', `http://127.0.0.1:${address.port}`],
{
env: { PATH: process.env.PATH ?? '', NODE_ENV: 'test' },
stdio: ['ignore', 'pipe', 'pipe'],
}
);
let childStdout = '';
let childStderr = '';
child.stdout.setEncoding('utf8').on('data', (chunk: string) => {
childStdout += chunk;
});
child.stderr.setEncoding('utf8').on('data', (chunk: string) => {
childStderr += chunk;
});
child.once('error', reject);
child.once('close', (exitCode) =>
resolve({ code: exitCode, stdout: childStdout, stderr: childStderr })
);
});

expect(spacedScriptPath).toContain(' ');
expect(stdout).toContain('RolePatch production smoke: 5/6 passed');
expect(code).toBe(1);
expect(stderr).toBe('');
expect(
stdout
.split(/\r?\n/)
.filter((line) => /^(PASS|FAIL) /.test(line))
.map((line) => line.replace(/^(PASS|FAIL) /, '').replace(/ \d+ \d+ms.*$/, ''))
).toEqual([
'landing',
'jobs browser',
'pricing',
'proof project',
'truehire proof preview guard',
'settings readiness',
]);
expect(stdout).toContain('FAIL jobs browser 503');
expect(requestedPaths).toEqual([
'/',
'/jobs',
'/pricing',
'/proof',
'/api/proof/truehire-preview',
'/settings',
]);
} finally {
await new Promise<void>((resolve, reject) =>
server.close((error) => (error ? reject(error) : resolve()))
);
await rm(tempDirectory, { recursive: true, force: true });
}
});
});
4 changes: 3 additions & 1 deletion scripts/production-smoke.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/usr/bin/env node

import { pathToFileURL } from 'node:url';

const DEFAULT_BASE_URL = 'https://rolepatch.com';
const DEFAULT_TIMEOUT_MS = 10_000;

Expand Down Expand Up @@ -208,7 +210,7 @@ async function main() {
if (summary.failed > 0) process.exitCode = 1;
}

if (import.meta.url === `file://${process.argv[1]}`) {
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
main().catch((err) => {
console.error(err instanceof Error ? err.message : String(err));
process.exitCode = 1;
Expand Down
Loading