fix(ci): align smoke stderr expectations

This commit is contained in:
Andrey Avtomonov 2026-05-12 15:31:41 +02:00
parent 009558be7e
commit 6a1fded5ce
2 changed files with 51 additions and 28 deletions

View file

@ -146,6 +146,10 @@ function parseJsonOutput<T>(stdout: string): T {
return JSON.parse(stdout) as T;
}
function expectProjectStderr(result: CliResult, projectDir: string): void {
expect(result).toMatchObject({ code: 0, stderr: `Project: ${projectDir}\n` });
}
async function runSetupNewProject(projectDir: string): Promise<CliResult> {
return await runBuiltCli([
'setup',
@ -178,7 +182,7 @@ describe('standalone built ktx CLI smoke', () => {
const sourceDir = join(tempDir, 'source');
const init = await runSetupNewProject(projectDir);
expect(init).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(init, projectDir);
expect(init.stdout).toContain(`Project: ${projectDir}`);
await writeWarehouseConfig(projectDir);
@ -204,14 +208,15 @@ describe('standalone built ktx CLI smoke', () => {
});
it('runs the default pre-seeded demo without credentials', async () => {
const projectDir = join(tempDir, 'demo-project');
const result = await runBuiltCli(
['setup', 'demo', '--project-dir', join(tempDir, 'demo-project'), '--plain', '--no-input'],
['setup', 'demo', '--project-dir', projectDir, '--plain', '--no-input'],
{
env: { ...process.env, ANTHROPIC_API_KEY: '' },
},
);
expect(result).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(result, projectDir);
expect(result.stdout).toContain('Mode: seeded');
expect(result.stdout).toContain('Source: packaged demo project');
expect(result.stdout).toContain('LLM calls: none');
@ -231,7 +236,7 @@ describe('standalone built ktx CLI smoke', () => {
const seeded = await runBuiltCli(['setup', 'demo', '--project-dir', projectDir, '--plain', '--no-input'], {
env: { ...process.env, ANTHROPIC_API_KEY: '' },
});
expect(seeded).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(seeded, projectDir);
expect(seeded.stdout).toContain('Mode: seeded');
const wikiSearch = await runBuiltCli([
@ -319,7 +324,7 @@ describe('standalone built ktx CLI smoke', () => {
expect(seeded.stdout).toContain('Knowledge pages:');
const inspect = await runBuiltCli(['setup', 'demo', 'inspect', '--project-dir', projectDir, '--no-input']);
expect(inspect).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(inspect, projectDir);
expect(inspect.stdout).toContain('Mode: seeded');
expect(inspect.stdout).toContain('Status: ready');
expect(inspect.stdout).toContain('Warehouse: 8 tables, 11,234 rows');
@ -348,7 +353,7 @@ describe('standalone built ktx CLI smoke', () => {
env: { ...process.env, ANTHROPIC_API_KEY: '' },
},
);
expect(seeded).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(seeded, projectDir);
expect(seeded.stdout).toContain('Mode: seeded');
const client = new Client({ name: 'ktx-seeded-demo-smoke-client', version: '0.0.0' });
@ -413,7 +418,7 @@ describe('standalone built ktx CLI smoke', () => {
expect(result.stdout).toContain('KTX setup doctor');
expect(result.stdout).toContain('Node 22+');
expect(result.stdout).toContain('Workspace-local CLI');
expect(result.stderr).toBe('');
expect(result.stderr).toBe(`Project: ${process.cwd()}\n`);
expect([0, 1]).toContain(result.code);
});
@ -433,7 +438,7 @@ describe('standalone built ktx CLI smoke', () => {
const projectDir = join(tempDir, 'reset-demo-project');
const init = await runBuiltCli(['setup', 'demo', 'init', '--project-dir', projectDir, '--no-input']);
expect(init).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(init, projectDir);
const withoutForce = await runBuiltCli(['setup', 'demo', 'reset', '--project-dir', projectDir, '--no-input']);
expect(withoutForce.code).toBe(1);
@ -450,7 +455,7 @@ describe('standalone built ktx CLI smoke', () => {
'--force',
'--no-input',
]);
expect(withForce).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(withForce, projectDir);
expect(withForce.stdout).toContain(`Demo project reset: ${projectDir}`);
});
@ -458,7 +463,7 @@ describe('standalone built ktx CLI smoke', () => {
const projectDir = join(tempDir, 'corrupt-demo-project');
const init = await runBuiltCli(['setup', 'demo', 'init', '--project-dir', projectDir, '--no-input']);
expect(init).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(init, projectDir);
await rm(join(projectDir, 'demo.db'), { force: true });
const replay = await runBuiltCli(['setup', 'demo', '--mode', 'replay', '--project-dir', projectDir, '--no-input']);
@ -471,14 +476,14 @@ describe('standalone built ktx CLI smoke', () => {
const projectDir = join(tempDir, 'doctor-demo-project');
const init = await runBuiltCli(['setup', 'demo', 'init', '--project-dir', projectDir, '--no-input']);
expect(init).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(init, projectDir);
const result = await runBuiltCli(['setup', 'demo', 'doctor', '--project-dir', projectDir, '--no-input']);
expect(result.stdout).toContain('KTX demo doctor');
expect(result.stdout).toContain('Demo dataset');
expect(result.stdout).toContain('Demo replay');
expect(result.stdout).toContain('Demo LLM provider');
expect(result.stderr).toBe('');
expect(result.stderr).toBe(`Project: ${projectDir}\n`);
expect([0, 1]).toContain(result.code);
});
@ -504,20 +509,20 @@ describe('standalone built ktx CLI smoke', () => {
it('runs structural and enriched scans through the built binary with manifest artifacts', async () => {
const projectDir = join(tempDir, 'scan-project');
const init = await runSetupNewProject(projectDir);
expect(init).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(init, projectDir);
const dbPath = join(projectDir, 'warehouse.db');
createSqliteWarehouse(dbPath);
await writeSqliteScanConfig(projectDir, dbPath);
const connectionTest = await runBuiltCli(['connection', 'test', 'warehouse', '--project-dir', projectDir]);
expect(connectionTest).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(connectionTest, projectDir);
expect(connectionTest.stdout).toContain('Connection test passed: warehouse');
expect(connectionTest.stdout).toContain('Driver: sqlite');
expect(connectionTest.stdout).toContain('Tables: 2');
const structural = await runBuiltCli(['dev', 'scan', 'warehouse', '--project-dir', projectDir]);
expect(structural).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(structural, projectDir);
expect(structural.stdout).toContain('Status: done');
expect(structural.stdout).toContain('Mode: structural');
const structuralRunId = getRunId(structural.stdout);
@ -560,7 +565,7 @@ describe('standalone built ktx CLI smoke', () => {
'--mode',
'enriched',
]);
expect(providerlessEnriched).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(providerlessEnriched, projectDir);
expect(providerlessEnriched.stdout).toContain('Mode: enriched');
expect(providerlessEnriched.stdout).toContain('Relationships');
expect(providerlessEnriched.stdout).toContain('Accepted: 1');
@ -614,7 +619,7 @@ describe('standalone built ktx CLI smoke', () => {
await writeSqliteScanConfig(projectDir, dbPath, true);
const enriched = await runBuiltCli(['dev', 'scan', 'warehouse', '--project-dir', projectDir, '--mode', 'enriched']);
expect(enriched).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(enriched, projectDir);
expect(enriched.stdout).toContain('Mode: enriched');
const enrichedRunId = getRunId(enriched.stdout);
@ -706,7 +711,7 @@ describe('standalone built ktx CLI smoke', () => {
it('adds a redacted Notion connection through the built binary', async () => {
const projectDir = join(tempDir, 'notion-project');
const init = await runSetupNewProject(projectDir);
expect(init).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(init, projectDir);
const add = await runBuiltCli([
'connection',
@ -723,7 +728,7 @@ describe('standalone built ktx CLI smoke', () => {
'5',
]);
expect(add).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(add, projectDir);
expect(add.stdout).toContain('Connection: notion-main');
expect(add.stdout).toContain('Driver: notion');
@ -746,7 +751,7 @@ describe('standalone built ktx CLI smoke', () => {
const projectDir = join(tempDir, 'project');
const init = await runSetupNewProject(projectDir);
expect(init).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(init, projectDir);
await writeWarehouseConfig(projectDir);
const client = new Client({ name: 'ktx-smoke-client', version: '0.0.0' });
@ -812,7 +817,7 @@ describe('standalone built ktx CLI smoke', () => {
it('serves scan execution and artifact inspection tools over stdio from the built binary', async () => {
const projectDir = join(tempDir, 'scan-mcp-project');
const init = await runSetupNewProject(projectDir);
expect(init).toMatchObject({ code: 0, stderr: '' });
expectProjectStderr(init, projectDir);
const dbPath = join(projectDir, 'warehouse.db');
createSqliteWarehouse(dbPath);

View file

@ -520,6 +520,15 @@ function requireSuccess(label, result) {
assert.equal(result.stderr, '', label + ' wrote unexpected stderr');
}
function requireProjectStderr(label, result, projectDir) {
assert.equal(
result.code,
0,
label + ' failed with code ' + result.code + '\\nstdout:\\n' + result.stdout + '\\nstderr:\\n' + result.stderr,
);
assert.equal(result.stderr, 'Project: ' + projectDir + '\\n', label + ' wrote unexpected stderr');
}
function requireSuccessWithStderr(label, result, stderrPattern) {
assert.equal(
result.code,
@ -631,7 +640,7 @@ try {
'--skip-sources',
'--skip-agents',
]);
requireSuccess('ktx setup', init);
requireProjectStderr('ktx setup', init, projectDir);
requireOutput('ktx setup', init, /Project: /);
const emptyProjectDir = join(root, 'empty-project');
@ -650,7 +659,7 @@ try {
'--skip-sources',
'--skip-agents',
]);
requireSuccess('ktx setup empty project', emptyInit);
requireProjectStderr('ktx setup empty project', emptyInit, emptyProjectDir);
const emptySearch = await run('pnpm', [
'exec',
'ktx',
@ -905,7 +914,7 @@ try {
'--project-dir',
projectDir,
]);
requireSuccess('ktx scan structural', structuralScan);
requireProjectStderr('ktx scan structural', structuralScan, projectDir);
requireOutput('ktx scan structural', structuralScan, /Status: done/);
requireOutput('ktx scan structural', structuralScan, /Mode: structural/);
requireOutput('ktx scan structural', structuralScan, /Needs attention\\s+None/);
@ -916,7 +925,7 @@ try {
projectDir,
structuralScanRunId,
]);
requireSuccess('ktx scan status', scanStatus);
requireProjectStderr('ktx scan status', scanStatus, projectDir);
requireOutput('ktx scan status', scanStatus, new RegExp('Run: ' + structuralScanRunId));
requireOutput('ktx scan status', scanStatus, /Status: done/);
requireOutput('ktx scan status', scanStatus, /Mode: structural/);
@ -943,7 +952,7 @@ try {
'--mode',
'enriched',
]);
requireSuccess('ktx scan enriched', enrichedScan);
requireProjectStderr('ktx scan enriched', enrichedScan, projectDir);
requireOutput('ktx scan enriched', enrichedScan, /Status: done/);
requireOutput('ktx scan enriched', enrichedScan, /Mode: enriched/);
const enrichedScanRunId = getRunId(enrichedScan.stdout);
@ -1037,6 +1046,15 @@ function requireStdout(label, result, pattern) {
assert.match(result.stdout, pattern, label + ' stdout did not match ' + pattern);
}
function requireProjectStderr(label, result, projectDir) {
assert.equal(
result.code,
0,
label + ' failed with code ' + result.code + '\\nstdout:\\n' + result.stdout + '\\nstderr:\\n' + result.stderr,
);
assert.equal(result.stderr, 'Project: ' + projectDir + '\\n', label + ' wrote unexpected stderr');
}
const root = await mkdtemp(join(tmpdir(), 'ktx-packed-demo-smoke-'));
try {
const projectDir = join(root, 'demo-project');
@ -1059,7 +1077,7 @@ try {
requireStdout('ktx setup demo seeded', seeded, /ktx serve --mcp stdio/);
assert.doesNotMatch(seeded.stdout, new RegExp(['--mode', 'deterministic'].join(' ')));
assert.doesNotMatch(seeded.stdout, /KTX memory flow/);
assert.equal(seeded.stderr, '', 'ktx setup demo seeded wrote unexpected stderr');
requireProjectStderr('ktx setup demo seeded', seeded, projectDir);
const demoWikiSearch = await run('pnpm', [
'exec',
@ -1108,7 +1126,7 @@ try {
assert.ok([0, 1].includes(doctor.code), 'ktx dev doctor setup exit code must be 0 or 1');
requireStdout('ktx dev doctor setup', doctor, /KTX setup doctor/);
requireStdout('ktx dev doctor setup', doctor, /Node 22\\+/);
assert.equal(doctor.stderr, '', 'ktx dev doctor setup wrote unexpected stderr');
assert.equal(doctor.stderr, 'Project: ' + process.cwd() + '\\n', 'ktx dev doctor setup wrote unexpected stderr');
} finally {
await rm(root, { recursive: true, force: true });
}