refactor(lock): Consolidate utilities and fix circular dependencies

Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-02-25 17:09:10 +00:00
1 parent 244dbe1511
commit e7b858d84a
330 files changed
+34090 -3976

No files matched your search

@@ -2,7 +2,8 @@ import { test, after } from 'node:test';
import assert from 'node:assert';
import fs from 'node:fs';
import path from 'node:path';
import { cmdInit, cmdRemove } from '../src/ops.mjs';
import { cmdInit } from '../src/ops.mjs';
import { cmdRemove } from '../src/cmd-remove.mjs';
const MOCK_CONFIG = {
installDir: 'torch',
+6
View File
@@ -0,0 +1,6 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
test('fail fixture stays red', () => {
assert.equal(1, 2);
});
+6
View File
@@ -0,0 +1,6 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
test('pass fixture stays green', () => {
assert.equal(2 + 2, 4);
});
+111
View File
@@ -0,0 +1,111 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { execSync } from 'node:child_process';
import os from 'node:os';
import { fileURLToPath } from 'node:url';
// Calculate paths relative to this file
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(__dirname, '..');
const governanceServicePath = path.resolve(repoRoot, 'src/services/governance/index.js');
async function runTest() {
// Create temp dir
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'torch-gov-test-'));
console.log(`Using temp dir: ${tempDir}`);
// Setup git repo
execSync('git init', { cwd: tempDir, stdio: 'ignore' });
execSync('git config user.name "Test User"', { cwd: tempDir, stdio: 'ignore' });
execSync('git config user.email "[email protected]"', { cwd: tempDir, stdio: 'ignore' });
// Create required directory structure
await fs.mkdir(path.join(tempDir, 'src/prompts/daily'), { recursive: true });
await fs.mkdir(path.join(tempDir, 'src/prompts/weekly'), { recursive: true });
// Proposals dir is created automatically by ensureDirs() but allowlist check needs prompts dir
// Create a target file
const targetFile = 'src/prompts/daily/test-agent.md';
const absoluteTarget = path.join(tempDir, targetFile);
await fs.writeFile(absoluteTarget, 'Original Content');
// Commit initial state so we have a HEAD
execSync(`git add ${targetFile}`, { cwd: tempDir });
execSync('git commit -m "Initial commit"', { cwd: tempDir });
// Switch cwd to tempDir
const originalCwd = process.cwd();
try {
process.chdir(tempDir);
console.log(`Changed cwd to: ${process.cwd()}`);
// Import governance service dynamically so it picks up the new cwd
const governance = await import(governanceServicePath);
// Malicious payload
const maliciousPayload = 'attacker"; touch /tmp/pwned_check; echo "';
// const proposalId = `test-proposal-${Date.now()}`; // unused
// Manually create proposal metadata and files because createProposal also uses git diff
// We can use createProposal from the service, but we need to pass the malicious agent name there.
// Let's try using createProposal first.
const proposalContent = `Shared contract (required):\nRequired startup + artifacts + memory + issue capture\n\nNew Content`;
console.log('Creating proposal...');
const { id } = await governance.createProposal({
agent: maliciousPayload,
target: targetFile,
newContent: proposalContent,
reason: 'Testing security'
});
console.log(`Proposal created with ID: ${id}`);
// Verify metadata has the malicious author
const metaPath = path.join(tempDir, 'src/proposals', id, 'meta.json');
const meta = JSON.parse(await fs.readFile(metaPath, 'utf8'));
if (meta.author !== maliciousPayload) {
throw new Error('Meta author does not match malicious payload');
}
// Apply proposal
console.log('Applying proposal...');
await governance.applyProposal(id);
console.log('Proposal applied.');
// Verification 1: Check if /tmp/pwned_check exists
try {
await fs.access('/tmp/pwned_check');
console.error('VULNERABILITY DETECTED: /tmp/pwned_check exists!');
process.exit(1);
} catch {
console.log('Security check passed: /tmp/pwned_check does not exist.');
}
// Verification 2: Check git log for commit message
const log = execSync('git log -1 --pretty=%B', { cwd: tempDir }).toString();
console.log(`Commit message: ${log}`);
if (!log.includes(maliciousPayload)) {
console.error('Verification failed: Commit message does not contain the payload literally.');
process.exit(1);
}
// Check if the commit message looks "broken" (e.g. if newlines were injected)
// Here we just want to ensure the whole thing is in the message.
console.log('Verification passed: Commit message contains payload.');
} finally {
// Cleanup
process.chdir(originalCwd);
await fs.rm(tempDir, { recursive: true, force: true });
// Also remove /tmp/pwned_check if it exists (it shouldn't)
try { await fs.unlink('/tmp/pwned_check'); } catch { /* ignore */ }
}
}
runTest().catch(err => {
console.error('Test failed:', err);
process.exit(1);
});
+25
View File
@@ -0,0 +1,25 @@
import { test } from 'node:test';
import { execFileSync } from 'node:child_process';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import assert from 'node:assert';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const implPath = path.join(__dirname, 'governance-security-impl.mjs');
test('Governance Service Security: Command Injection Prevention', (_t) => {
// We run the implementation in a separate process to ensure process.cwd() isolation
// and to verify that the dynamic import picks up the correct paths.
try {
const output = execFileSync(process.execPath, [implPath], { encoding: 'utf8' });
// If we reach here, the process exited with code 0 (success)
// We can optionally check the output for specific messages if needed,
// but the implementation script asserts internally.
assert.ok(output.includes('Verification passed: Commit message contains payload.'), 'Output should confirm verification passed');
} catch (err) {
console.error('Test implementation failed.');
if (err.stdout) console.error('STDOUT:', err.stdout);
if (err.stderr) console.error('STDERR:', err.stderr);
assert.fail(`Security test failed with exit code ${err.status}: ${err.message}`);
}
});
+18
View File
@@ -96,6 +96,24 @@ describe('src/lib.mjs', () => {
// unknown agents are not in roster, so available should be full roster (since no roster agent is locked)
assert.deepStrictEqual(result.available, mockRoster.sort());
});
it('suppresses relay query loggers when quiet mode is enabled', async () => {
let receivedQueryDeps = null;
const deps = {
...baseDeps,
json: true,
quiet: true,
queryLocks: async (_relays, _cadence, _dateStr, _namespace, queryDeps) => {
receivedQueryDeps = queryDeps;
return [];
},
};
await cmdCheck('daily', deps);
assert.equal(typeof receivedQueryDeps.errorLogger, 'function');
assert.equal(typeof receivedQueryDeps.healthLogger, 'function');
});
});
describe('cmdLock', () => {
+59 -77
View File
@@ -1,84 +1,66 @@
import { describe, it, mock } from 'node:test';
import assert from 'node:assert/strict';
import { test } from 'node:test';
import assert from 'node:assert';
import { getCompletedAgents } from '../src/lock-utils.mjs';
describe('lock-utils', () => {
describe('getCompletedAgents', () => {
// Helper to create deps
const createDeps = (overrides = {}) => ({
readdir: async () => [],
getDateStr: () => '2023-01-01',
getIsoWeek: () => '2023-W01',
...overrides,
});
test('getCompletedAgents', async (t) => {
await t.test('returns empty set if directory read fails (non-ENOENT)', async () => {
const deps = {
readdir: async () => { throw new Error('Permission denied'); },
getDateStr: () => '2026-02-24',
getIsoWeek: () => '2026-W09'
};
const completed = await getCompletedAgents('daily', 'logs', deps);
assert.strictEqual(completed.size, 0);
});
it('returns completed agents for daily cadence', async () => {
const deps = createDeps({
readdir: async () => [
'2023-01-01T10-00-00Z__agent1__completed.md',
'2023-01-01T11-00-00Z__agent2__failed.md',
'2023-01-02T10-00-00Z__agent3__completed.md', // different day
],
getDateStr: () => '2023-01-01',
});
const result = await getCompletedAgents('daily', 'logs', deps);
assert.deepEqual([...result], ['agent1']);
});
await t.test('returns empty set if directory does not exist (ENOENT)', async () => {
const error = new Error('ENOENT');
error.code = 'ENOENT';
const deps = {
readdir: async () => { throw error; },
getDateStr: () => '2026-02-24',
getIsoWeek: () => '2026-W09'
};
const completed = await getCompletedAgents('daily', 'logs', deps);
assert.strictEqual(completed.size, 0);
});
it('returns completed agents for weekly cadence', async () => {
const deps = createDeps({
readdir: async () => [
'2023-01-01T10-00-00Z__agent1__completed.md', // Week 52 of 2022 (Sunday) - assuming mock logic
'2023-01-02T10-00-00Z__agent2__completed.md', // Week 01 of 2023 (Monday)
],
getIsoWeek: (date) => {
if (!date) return '2023-W01'; // current week
if (date === '2023-01-01') return '2022-W52';
if (date === '2023-01-02') return '2023-W01';
return 'UNKNOWN';
}
});
// cadence is weekly
const result = await getCompletedAgents('weekly', 'logs', deps);
assert.deepEqual([...result], ['agent2']);
});
await t.test('identifies daily completed agents correctly', async () => {
const files = [
'2026-02-24T10-00-00Z__agent-a__completed.md',
'2026-02-24T11-00-00Z__agent-b__failed.md', // failed, not completed
'2026-02-23T10-00-00Z__agent-c__completed.md', // yesterday
'invalid-format.md'
];
const deps = {
readdir: async () => files,
getDateStr: () => '2026-02-24',
getIsoWeek: () => '2026-W09'
};
const completed = await getCompletedAgents('daily', 'logs', deps);
assert.ok(completed.has('agent-a'));
assert.ok(!completed.has('agent-b'));
assert.ok(!completed.has('agent-c'));
assert.strictEqual(completed.size, 1);
});
it('handles ENOENT gracefully', async () => {
const error = new Error('No such file or directory');
error.code = 'ENOENT';
const deps = createDeps({
readdir: async () => { throw error; }
});
// Spy on console.error to ensure it is NOT called
const consoleError = mock.method(console, 'error');
const result = await getCompletedAgents('daily', 'logs', deps);
assert.equal(consoleError.mock.callCount(), 0);
assert.deepEqual([...result], []);
consoleError.mock.restore();
});
it('logs error for non-ENOENT errors', async () => {
const error = new Error('Permission denied');
error.code = 'EACCES';
const deps = createDeps({
readdir: async () => { throw error; }
});
// Spy on console.error to verify it IS called
const consoleError = mock.method(console, 'error');
const result = await getCompletedAgents('daily', 'logs', deps);
assert.equal(consoleError.mock.callCount(), 1);
const call = consoleError.mock.calls[0];
assert.match(call.arguments[0], /Warning: Failed to read log dir/);
assert.match(call.arguments[0], /Permission denied/);
assert.deepEqual([...result], []);
consoleError.mock.restore();
});
await t.test('identifies weekly completed agents correctly', async () => {
const files = [
'2026-02-24T10-00-00Z__agent-a__completed.md', // this week (W09)
'2026-02-17T10-00-00Z__agent-b__completed.md', // last week (W08)
];
const deps = {
readdir: async () => files,
getDateStr: () => '2026-02-24',
getIsoWeek: (date) => {
if (date === '2026-02-24') return '2026-W09';
if (date === '2026-02-17') return '2026-W08';
return '2026-W09'; // Default for current
}
};
const completed = await getCompletedAgents('weekly', 'logs', deps);
assert.ok(completed.has('agent-a'));
assert.ok(!completed.has('agent-b'));
assert.strictEqual(completed.size, 1);
});
});
+50 -31
View File
@@ -2,9 +2,13 @@ import { test, after } from 'node:test';
import assert from 'node:assert';
import fs from 'node:fs';
import path from 'node:path';
import { execSync } from 'node:child_process';
import { exec } from 'node:child_process';
import { promisify } from 'node:util';
import { WebSocketServer } from 'ws';
import { cmdInit, cmdUpdate } from '../src/ops.mjs';
const execAsync = promisify(exec);
const MOCK_CONFIG = {
installDir: 'torch',
namespace: 'test-namespace',
@@ -115,45 +119,60 @@ test('cmdUpdate creates backup', async () => {
});
test('torch-lock check respects local roster', async () => {
const projectRoot = path.join(tempBase, 'project_roster');
fs.mkdirSync(projectRoot, { recursive: true });
const wss = new WebSocketServer({ port: 0 });
const port = wss.address().port;
const mockRelayUrl = `ws://127.0.0.1:${port}`;
// Init
await cmdInit(false, projectRoot, MOCK_CONFIG);
// Modify roster
const rosterPath = path.join(projectRoot, 'torch', 'roster.json');
const roster = JSON.parse(fs.readFileSync(rosterPath, 'utf8'));
roster.daily.push('custom-agent-007');
fs.writeFileSync(rosterPath, JSON.stringify(roster, null, 2));
// Run CLI
const binPath = path.resolve(process.cwd(), 'bin/torch-lock.mjs');
const output = execSync(`${process.execPath} ${binPath} check --cadence daily`, {
cwd: projectRoot,
encoding: 'utf8'
wss.on('connection', (ws) => {
ws.on('message', (message) => {
try {
const data = JSON.parse(message);
if (data[0] === 'REQ') {
const subId = data[1];
ws.send(JSON.stringify(['EOSE', subId]));
}
} catch (_err) {
// ignore
}
});
});
const result = JSON.parse(output);
assert.ok(result.available.includes('custom-agent-007'), 'Custom agent found in roster');
try {
const projectRoot = path.join(tempBase, 'project_roster');
fs.mkdirSync(projectRoot, { recursive: true });
// Use local mock relay
const localConfig = { ...MOCK_CONFIG, relays: [mockRelayUrl] };
// Init
await cmdInit(false, projectRoot, localConfig);
// Modify roster
const rosterPath = path.join(projectRoot, 'torch', 'roster.json');
const roster = JSON.parse(fs.readFileSync(rosterPath, 'utf8'));
roster.daily.push('custom-agent-007');
fs.writeFileSync(rosterPath, JSON.stringify(roster, null, 2));
// Run CLI
const binPath = path.resolve(process.cwd(), 'bin/torch-lock.mjs');
const { stdout } = await execAsync(`${process.execPath} ${binPath} check --cadence daily`, {
cwd: projectRoot,
encoding: 'utf8',
env: { ...process.env, NOSTR_LOCK_RELAYS: undefined, NOSTR_LOCK_RELAY_FALLBACKS: '' }
});
const result = JSON.parse(stdout);
assert.ok(result.available.includes('custom-agent-007'), 'Custom agent found in roster');
} finally {
wss.close();
}
});
test('cmdInit creates torch-config.json with random namespace', async () => {
const projectRoot = path.join(tempBase, 'project_config_init');
fs.mkdirSync(projectRoot, { recursive: true });
// Do NOT pass mockAnswers here to test random namespace generation,
// but we know it hangs.
// Wait, if I want to test random namespace, I MUST let it call interactiveInit?
// No, I should probably change cmdInit to allow optional parts of config or just test the random part separately.
// Actually, for this test, let's just pass a partial mock if possible?
// No, cmdInit takes full mockAnswers.
// I'll skip this test's random part or just accept it uses the mock.
// Actually, if I pass a mock, it WON'T test random namespace.
await cmdInit(false, projectRoot, MOCK_CONFIG);
const configPath = path.join(projectRoot, 'torch-config.json');
+78
View File
@@ -0,0 +1,78 @@
import { test, expect } from '@playwright/test';
import fs from 'fs';
import path from 'path';
// Prepare logging directory
const logDir = 'test_logs';
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir);
}
const logFile = path.join(logDir, 'playwright_console.log');
// Clear previous log
fs.writeFileSync(logFile, '');
test.use({
extraHTTPHeaders: {
// base64('admin:password') = 'YWRtaW46cGFzc3dvcmQ='
'Authorization': 'Basic YWRtaW46cGFzc3dvcmQ='
}
});
test.describe.serial('TORCH Dashboard', () => {
test.beforeEach(async ({ page }) => {
page.on('console', msg => {
const logLine = `[${msg.type()}] ${msg.text()}\n`;
fs.appendFileSync(logFile, logLine);
// Also log to stdout so it appears in the runner output
// console.log(`[Browser Console] ${msg.text()}`);
});
});
test('should load the dashboard and verify title', async ({ page }) => {
await page.goto('/');
// Check for title "TORCH Dashboard" or similar. Checking regex /TORCH/ is safer.
await expect(page).toHaveTitle(/TORCH/);
// Check for some basic element presence to ensure rendering happened
// Assuming there is a header or main content
// We can inspect dashboard/index.html to be sure
// But generic check for body visibility is a start
await expect(page.locator('body')).toBeVisible();
// Verify console log capture
await page.evaluate(() => console.log('Test log from browser'));
});
test('should fail authentication with wrong credentials', async ({ browser }) => {
const context = await browser.newContext({
extraHTTPHeaders: {
'Authorization': 'Basic YWRtaW46d3Jvbmc=' // admin:wrong
}
});
const page = await context.newPage();
const response = await page.goto('/');
expect(response.status()).toBe(401);
await context.close();
});
test('should serve public config correctly', async ({ request }) => {
const response = await request.get('/torch-config.json');
expect(response.ok()).toBeTruthy();
const config = await response.json();
expect(config.nostrLock.namespace).toBe('test-torch');
// Security check: Auth should be stripped
expect(config.dashboard.auth).toBeUndefined();
});
test('should shutdown gracefully to save coverage', async ({ request }) => {
// Attempt to shutdown. It might fail if already down (unlikely in serial).
try {
// We don't expect a response because the server dies
await request.get('/shutdown', { timeout: 1000 }).catch(() => {});
} catch (_) {
// Ignore
}
// Wait a bit for file write
await new Promise(resolve => setTimeout(resolve, 2000));
});
});
@@ -0,0 +1,30 @@
{
"nostrLock": {
"namespace": "test-torch",
"relays": ["wss://relay.damus.io"],
"relayFallbacks": [],
"ttlSeconds": 60,
"queryTimeoutMs": 5000,
"publishTimeoutMs": 5000,
"minSuccessfulRelayPublishes": 1,
"minActiveRelayPool": 1
},
"dashboard": {
"auth": "admin:password",
"defaultCadenceView": "daily",
"defaultStatusView": "active",
"relays": ["wss://relay.damus.io"],
"namespace": "test-torch",
"hashtag": "test-torch-lock"
},
"scheduler": {
"firstPromptByCadence": {
"daily": "src/prompts/daily/agent.md",
"weekly": "src/prompts/weekly/agent.md"
},
"paused": {
"daily": [],
"weekly": []
}
}
}
+113 -3
View File
@@ -59,6 +59,9 @@ async function setupFixture({
roster = ['agent-a'],
lockHealthPreflight = undefined,
schedulerPolicy = {},
handoffCommand = 'echo HANDOFF_OK',
validationCommands = ['true'],
lockCheckOutput = `echo '{"excluded":[]}'`,
preflightScript = '#!/usr/bin/env node\nconsole.log(JSON.stringify({ ok: true, relays: ["wss://relay.test"] }));\nprocess.exit(0);\n',
}) {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'scheduler-memory-policy-'));
@@ -103,8 +106,8 @@ async function setupFixture({
JSON.stringify({
scheduler: {
firstPromptByCadence: { daily: 'agent-a' },
handoffCommandByCadence: { daily: 'echo HANDOFF_OK' },
validationCommandsByCadence: { daily: ['true'] },
handoffCommandByCadence: { daily: handoffCommand },
validationCommandsByCadence: { daily: validationCommands },
memoryPolicyByCadence: { daily: memoryPolicy },
...schedulerPolicy,
...(lockHealthPreflight === undefined ? {} : { lockHealthPreflight }),
@@ -122,7 +125,7 @@ if [[ "$1" != "run" ]]; then
fi
case "$2" in
lock:check:daily)
echo '{"excluded":[]}'
${lockCheckOutput}
;;
lock:health)
shift 2
@@ -611,3 +614,110 @@ test('does not use backend retry flow for exit code 3 lock conflicts', { concurr
assert.equal(result.code, 0, `stdout: ${result.stdout}\nstderr: ${result.stderr}`);
assert.doesNotMatch(result.stdout, /"event":"scheduler.lock.retry"/);
});
test('retries handoff command for retryable network failures and succeeds', { concurrency: false }, async () => {
const fixture = await setupFixture({
memoryPolicy: { mode: 'optional' },
handoffCommand: `count_file="$PWD/.handoff-count"
count=0
if [[ -f "$count_file" ]]; then
count="$(cat "$count_file")"
fi
count=$((count + 1))
echo "$count" > "$count_file"
if [[ "$count" -lt 2 ]]; then
echo "stream disconnected before completion" >&2
exit 12
fi
echo HANDOFF_OK`,
schedulerPolicy: {
handoffPolicyByCadence: {
daily: {
maxAttempts: 3,
retryBackoffMs: 0,
},
},
},
});
const result = await runNode(fixture.scriptPath, ['--cadence', 'daily'], {
cwd: fixture.root,
env: fixture.env,
});
assert.equal(result.code, 0, `stdout: ${result.stdout}\nstderr: ${result.stderr}`);
const logs = await fs.readdir(path.join(fixture.root, 'task-logs', 'daily'));
assert.ok(logs.some((name) => name.endsWith('__completed.md')));
});
test('uses fallback platform for handoff when primary platform fails', { concurrency: false }, async () => {
const fixture = await setupFixture({
memoryPolicy: { mode: 'optional' },
handoffCommand: `if [[ "$AGENT_PLATFORM" == "linux" ]]; then
echo HANDOFF_FALLBACK_OK
exit 0
fi
echo "stream disconnected before completion" >&2
exit 13`,
schedulerPolicy: {
handoffPolicyByCadence: {
daily: {
maxAttempts: 1,
fallbackPlatform: 'linux',
},
},
},
});
const result = await runNode(fixture.scriptPath, ['--cadence', 'daily', '--platform', 'codex'], {
cwd: fixture.root,
env: fixture.env,
});
assert.equal(result.code, 0, `stdout: ${result.stdout}\nstderr: ${result.stderr}`);
const logs = await fs.readdir(path.join(fixture.root, 'task-logs', 'daily'));
assert.ok(logs.some((name) => name.endsWith('__completed.md')));
});
test('fails before lock acquisition when runner health preflight command fails', { concurrency: false }, async () => {
const fixture = await setupFixture({
memoryPolicy: { mode: 'optional' },
schedulerPolicy: {
runnerHealthCommandByCadence: {
daily: 'echo RUNNER_UNAVAILABLE >&2; exit 9',
},
},
});
const result = await runNode(fixture.scriptPath, ['--cadence', 'daily'], {
cwd: fixture.root,
env: fixture.env,
});
assert.equal(result.code, 9, `stdout: ${result.stdout}\nstderr: ${result.stderr}`);
const logs = await fs.readdir(path.join(fixture.root, 'task-logs', 'daily'));
const failedLog = logs.find((name) => name.endsWith('__failed.md'));
assert.ok(failedLog);
const failedBody = await fs.readFile(path.join(fixture.root, 'task-logs', 'daily', failedLog), 'utf8');
assert.match(failedBody, /reason: Runner health preflight failed/);
assert.match(failedBody, /failure_class: 'runner_unavailable'/);
assert.match(failedBody, /failure_category: 'execution_error'/);
assert.match(failedBody, /Prompt not executed\./);
});
test('uses exclusion payload from mixed JSON lock:check output', { concurrency: false }, async () => {
const fixture = await setupFixture({
memoryPolicy: { mode: 'optional' },
roster: ['agent-a', 'agent-b'],
lockCheckOutput: `echo '{"event":"relay_health_snapshot"}'
echo '{"excluded":["agent-a"]}'`,
});
const result = await runNode(fixture.scriptPath, ['--cadence', 'daily'], {
cwd: fixture.root,
env: fixture.env,
});
assert.equal(result.code, 0, `stdout: ${result.stdout}\nstderr: ${result.stderr}`);
assert.match(result.stdout, /Agent:\s+agent-b/);
});
+47
View File
@@ -0,0 +1,47 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { spawn } from 'node:child_process';
const SCRIPT = path.resolve('scripts/test-audit/run-flaky-check.mjs');
function runFlakyCheck(args) {
return new Promise((resolve) => {
const childEnv = { ...process.env };
delete childEnv.NODE_TEST_CONTEXT;
delete childEnv.NODE_UNIQUE_ID;
const child = spawn(process.execPath, [SCRIPT, ...args], {
cwd: process.cwd(),
stdio: ['ignore', 'pipe', 'pipe'],
env: childEnv,
});
let stdout = '';
let stderr = '';
child.stdout.on('data', (chunk) => { stdout += chunk.toString(); });
child.stderr.on('data', (chunk) => { stderr += chunk.toString(); });
child.on('close', (code) => resolve({ code: code ?? 1, stdout, stderr }));
});
}
test('run-flaky-check reports pass/fail counts from TAP output files', async () => {
const outputDir = await fs.mkdtemp(path.join(os.tmpdir(), 'flaky-check-report-'));
const passFixture = 'test/fixtures/flaky-check/pass.fixture.test.mjs';
const failFixture = 'test/fixtures/flaky-check/fail.fixture.test.mjs';
const result = await runFlakyCheck(['--output-dir', outputDir, passFixture, failFixture]);
assert.equal(result.code, 0, result.stderr || result.stdout);
const matrix = JSON.parse(await fs.readFile(path.join(outputDir, 'flakiness-matrix.json'), 'utf8'));
// The tool keys results by test name, not filename
assert.deepEqual(matrix['pass fixture stays green'], { pass: 5, fail: 0 });
assert.deepEqual(matrix['fail fixture stays red'], { pass: 0, fail: 5 });
const diagnostics = JSON.parse(await fs.readFile(path.join(outputDir, 'flakiness-runs.json'), 'utf8'));
assert.equal(diagnostics.length, 5);
assert.ok(diagnostics.every((run) => run.observed >= 2));
});
+5
View File
@@ -2,7 +2,12 @@ import { describe, it, after } from 'node:test';
import assert from 'node:assert';
import fs from 'node:fs';
import path from 'node:path';
<<<<<<< HEAD
import { getIsoWeekStr, todayDateStr, nowUnix, ensureDir, detectPlatform, relayListLabel, mergeRelayList } from '../src/utils.mjs';
=======
import { getIsoWeekStr, todayDateStr, nowUnix, ensureDir, detectPlatform, relayListLabel } from '../src/utils.mjs';
import { mergeRelayList } from '../src/lock-utils.mjs';
>>>>>>> origin/main
describe('Date Utilities', () => {
describe('todayDateStr', () => {