Files
redsen-lean-harness/tests/helpers.mjs
T
mozempkandCopilot 5ccf26f859 fix: tests depended on ambient CONTEXT7_API_KEY, broke in CI
\`lh doctor\`'s context7 check is a required, env-only check by design
(ADR: never inferred, never persisted). Tests that shell out to \`lh\`
or \`scripts/onboard.mjs\` inherited whatever CONTEXT7_API_KEY the
developer's shell happened to export, so \`doctor passes once the repo
is initialised\` and the onboard-script e2e test only ever passed on
machines with a real key set — never verified in a clean environment
until this CI run (no secret configured, correctly).

Fixed by injecting an obviously-fake fixture key (TEST_ENV in
helpers.mjs, exported and reused by onboard.test.mjs) into every
subprocess these tests spawn, so behaviour no longer depends on the
ambient shell. Verified locally with \`env -u CONTEXT7_API_KEY\` to
reproduce the CI environment exactly: 58/58 pass either way now.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-09 23:15:53 +02:00

65 lines
2.2 KiB
JavaScript

import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, dirname } from 'node:path';
import { execFileSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
export const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
export const CLI = join(ROOT, 'src', 'cli.mjs');
const created = [];
/**
* `lh doctor`/`lh init` treat CONTEXT7_API_KEY as a required, env-only value
* (never persisted, never inferred). Tests must not depend on whatever the
* developer's shell happens to export — inject an obviously-fake fixture so
* behaviour is identical locally and in CI, without ever touching a real key.
*/
export const TEST_ENV = { ...process.env, CONTEXT7_API_KEY: process.env.CONTEXT7_API_KEY ?? 'fixture-not-a-real-key' };
export function tempRepo(files = {}) {
const dir = mkdtempSync(join(tmpdir(), 'lh-test-'));
created.push(dir);
git(dir, ['init', '-q', '-b', 'main']);
git(dir, ['config', 'user.email', 'test@example.com']);
git(dir, ['config', 'user.name', 'Test']);
git(dir, ['config', 'commit.gpgsign', 'false']);
for (const [rel, body] of Object.entries(files)) write(dir, rel, body);
if (Object.keys(files).length) commitAll(dir, 'init');
return dir;
}
export function write(dir, rel, body) {
const full = join(dir, rel);
mkdirSync(dirname(full), { recursive: true });
writeFileSync(full, body);
return full;
}
export function git(dir, args) {
return execFileSync('git', args, { cwd: dir, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
}
export function commitAll(dir, msg) {
git(dir, ['add', '-A']);
git(dir, ['commit', '-qm', msg]);
}
/** Run the lh CLI. Never throws — returns {code, stdout, stderr}. */
export function lh(dir, args) {
try {
const stdout = execFileSync(process.execPath, [CLI, ...args], {
cwd: dir, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], env: TEST_ENV,
});
return { code: 0, stdout, stderr: '' };
} catch (e) {
return { code: e.status ?? 1, stdout: e.stdout ?? '', stderr: e.stderr ?? '' };
}
}
export function cleanup() {
while (created.length) {
try { rmSync(created.pop(), { recursive: true, force: true }); } catch {}
}
}