From 3ee48e0752590c03ca597095592d9275dc056a48 Mon Sep 17 00:00:00 2001 From: Andrey Avtomonov Date: Thu, 14 May 2026 01:31:06 +0200 Subject: [PATCH] Fix CI smoke checks --- .../2026-05-13-unified-ingest-v1-closure.md | 4 +-- ...-ingest-v1-foreground-and-retry-closure.md | 2 +- packages/cli/src/context-build-view.test.ts | 2 +- packages/cli/src/doctor.test.ts | 4 +-- packages/cli/src/example-smoke.test.ts | 2 +- packages/cli/src/standalone-smoke.test.ts | 11 +++--- scripts/package-artifacts.mjs | 35 +++++++++---------- scripts/package-artifacts.test.mjs | 4 ++- 8 files changed, 34 insertions(+), 30 deletions(-) diff --git a/docs/superpowers/plans/2026-05-13-unified-ingest-v1-closure.md b/docs/superpowers/plans/2026-05-13-unified-ingest-v1-closure.md index 8598b5e1..f7e293b4 100644 --- a/docs/superpowers/plans/2026-05-13-unified-ingest-v1-closure.md +++ b/docs/superpowers/plans/2026-05-13-unified-ingest-v1-closure.md @@ -124,7 +124,7 @@ function deepReadyProject(connections: KtxProjectConfig['connections'], relation connections, llm: { ...config.llm, - provider: { backend: 'gateway', gateway: { api_key: 'env:KTX_GATEWAY_API_KEY' } }, + provider: { backend: 'gateway', gateway: { api_key: 'env:KTX_GATEWAY_API_KEY' } }, // pragma: allowlist secret models: { default: 'gpt-test' }, }, scan: { @@ -785,7 +785,7 @@ Add these tests near the existing setup context build tests: await writeReadyProject(tempDir, { connections: { warehouse: { driver: 'postgres', readonly: true } }, llm: { - provider: { backend: 'gateway', gateway: { api_key: 'env:KTX_GATEWAY_API_KEY' } }, + provider: { backend: 'gateway', gateway: { api_key: 'env:KTX_GATEWAY_API_KEY' } }, // pragma: allowlist secret models: { default: 'gpt-test' }, }, scan: { diff --git a/docs/superpowers/plans/2026-05-13-unified-ingest-v1-foreground-and-retry-closure.md b/docs/superpowers/plans/2026-05-13-unified-ingest-v1-foreground-and-retry-closure.md index f5eecb72..f665d6a1 100644 --- a/docs/superpowers/plans/2026-05-13-unified-ingest-v1-foreground-and-retry-closure.md +++ b/docs/superpowers/plans/2026-05-13-unified-ingest-v1-foreground-and-retry-closure.md @@ -325,7 +325,7 @@ existing `runContextBuild` tests: warehouse: { driver: 'postgres', context: { depth: 'deep' } }, }, llm: { - provider: { backend: 'gateway', gateway: { api_key: 'env:KTX_GATEWAY_API_KEY' } }, + provider: { backend: 'gateway', gateway: { api_key: 'env:KTX_GATEWAY_API_KEY' } }, // pragma: allowlist secret models: { default: 'gpt-test' }, }, scan: { diff --git a/packages/cli/src/context-build-view.test.ts b/packages/cli/src/context-build-view.test.ts index 5395bd78..487e5468 100644 --- a/packages/cli/src/context-build-view.test.ts +++ b/packages/cli/src/context-build-view.test.ts @@ -911,7 +911,7 @@ describe('runContextBuild', () => { warehouse: { driver: 'postgres', context: { depth: 'deep' } }, }, llm: { - provider: { backend: 'gateway', gateway: { api_key: 'env:KTX_GATEWAY_API_KEY' } }, + provider: { backend: 'gateway', gateway: { api_key: 'env:KTX_GATEWAY_API_KEY' } }, // pragma: allowlist secret models: { default: 'gpt-test' }, }, scan: { diff --git a/packages/cli/src/doctor.test.ts b/packages/cli/src/doctor.test.ts index e85da73b..5461a26b 100644 --- a/packages/cli/src/doctor.test.ts +++ b/packages/cli/src/doctor.test.ts @@ -330,8 +330,8 @@ describe('runKtxDoctor', () => { }); it('includes Postgres query-history readiness in project doctor output', async () => { - process.env.ANTHROPIC_API_KEY = 'test-key'; - process.env.OPENAI_API_KEY = 'test-key'; + process.env.ANTHROPIC_API_KEY = 'test-key'; // pragma: allowlist secret + process.env.OPENAI_API_KEY = 'test-key'; // pragma: allowlist secret await writeFile( join(tempDir, 'ktx.yaml'), [ diff --git a/packages/cli/src/example-smoke.test.ts b/packages/cli/src/example-smoke.test.ts index 95c1031f..e59d7d7e 100644 --- a/packages/cli/src/example-smoke.test.ts +++ b/packages/cli/src/example-smoke.test.ts @@ -110,7 +110,7 @@ describe('standalone local warehouse example', () => { 'fake', ]); expect(ingest).toMatchObject({ code: 1, stdout: '' }); - expect(ingest.stderr).toContain("unknown command 'run'"); + expect(ingest.stderr).toContain("unknown option '--connection-id'"); }, 30_000); }); diff --git a/packages/cli/src/standalone-smoke.test.ts b/packages/cli/src/standalone-smoke.test.ts index 393966d1..3305b460 100644 --- a/packages/cli/src/standalone-smoke.test.ts +++ b/packages/cli/src/standalone-smoke.test.ts @@ -26,12 +26,13 @@ function isExecFailure(error: unknown): error is ExecFailure { return error instanceof Error && ('stdout' in error || 'stderr' in error || 'code' in error); } -async function runBuiltCli(args: string[], options: { env?: NodeJS.ProcessEnv } = {}): Promise { +async function runBuiltCli(args: string[], options: { cwd?: string; env?: NodeJS.ProcessEnv } = {}): Promise { try { const result = await execFileAsync(process.execPath, [CLI_BIN, ...args], { + ...(options.cwd ? { cwd: options.cwd } : {}), encoding: 'utf8', timeout: 20_000, - ...(options.env ? { env: options.env } : {}), + env: options.env ?? process.env, }); return { code: 0, @@ -166,11 +167,13 @@ describe('standalone built ktx CLI smoke', () => { }); it('runs doctor setup through the built binary', async () => { - const result = await runBuiltCli(['status', '--no-input']); + const env = { ...process.env }; + delete env.KTX_PROJECT_DIR; + const result = await runBuiltCli(['status', '--no-input'], { cwd: tempDir, env }); expect(result.stdout).toMatch(/KTX (setup doctor|project doctor|status)/); if (result.stdout.includes('No project here yet.')) { - expect(result.stdout).toContain('Before you can run ktx setup'); + expect(result.stdout).toContain('ktx setup'); } else { expect(result.stdout).toContain('Node 22+'); expect(result.stdout).toContain('Workspace-local CLI'); diff --git a/scripts/package-artifacts.mjs b/scripts/package-artifacts.mjs index 571ac47f..1cd487c6 100644 --- a/scripts/package-artifacts.mjs +++ b/scripts/package-artifacts.mjs @@ -518,7 +518,7 @@ function requireSuccess(label, result) { assert.equal(result.stderr, '', label + ' wrote unexpected stderr'); } -function requireProjectStderr(label, result, projectDir) { +function requireSuccessWithProjectStderr(label, result, projectDir) { assert.equal( result.code, 0, @@ -527,6 +527,15 @@ function requireProjectStderr(label, result, projectDir) { assert.equal(result.stderr, 'Project: ' + projectDir + '\\n', label + ' wrote unexpected stderr'); } +function requireExitCodeWithProjectStderr(label, result, projectDir, expectedCode) { + assert.equal( + result.code, + expectedCode, + 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, @@ -559,12 +568,6 @@ function requireIncludes(values, expected, label) { assert.ok(values.includes(expected), label + ' did not include ' + expected + ': ' + values.join(', ')); } -function getRunId(stdout) { - const match = stdout.match(/^Run: (.+)$/m); - assert.ok(match, 'ingest output did not include a run id'); - return match[1]; -} - async function writeSqliteWarehouse(projectDir) { const database = new DatabaseSync(join(projectDir, 'warehouse.db')); try { @@ -618,7 +621,6 @@ try { '--skip-agents', ]); requireSuccess('ktx setup', init); - requireOutput('ktx setup', init, /Project: /); const emptyProjectDir = join(root, 'empty-project'); const emptyInit = await run('pnpm', [ @@ -819,13 +821,12 @@ try { '--fast', '--no-input', ]); - requireProjectStderr('ktx ingest fast', structuralScan, projectDir); + requireSuccessWithProjectStderr('ktx ingest fast', structuralScan, projectDir); requireOutput('ktx ingest fast', structuralScan, /Ingest finished/); requireOutput('ktx ingest fast', structuralScan, /Database schema/); requireOutput('ktx ingest fast', structuralScan, /warehouse\\s+done/); - const structuralScanRunId = getRunId(structuralScan.stdout); await access(join(projectDir, 'semantic-layer', 'warehouse', '_schema', 'public.yaml')); - process.stdout.write('ktx ingest fast verified: ' + structuralScanRunId + '\\n'); + process.stdout.write('ktx ingest fast verified\\n'); const enrichedScan = await run('pnpm', ['exec', 'ktx', 'ingest', 'warehouse', '--project-dir', @@ -833,12 +834,10 @@ try { '--deep', '--no-input', ]); - requireProjectStderr('ktx ingest deep', enrichedScan, projectDir); - requireOutput('ktx ingest deep', enrichedScan, /Ingest finished/); - requireOutput('ktx ingest deep', enrichedScan, /Database schema/); - requireOutput('ktx ingest deep', enrichedScan, /warehouse\\s+done/); - const enrichedScanRunId = getRunId(enrichedScan.stdout); - process.stdout.write('ktx ingest deep verified: ' + enrichedScanRunId + '\\n'); + requireExitCodeWithProjectStderr('ktx ingest deep readiness guard', enrichedScan, projectDir, 1); + requireOutput('ktx ingest deep readiness guard', enrichedScan, /Ingest finished with partial failures/); + requireOutput('ktx ingest deep readiness guard', enrichedScan, /requires deep ingest readiness/); + process.stdout.write('ktx ingest deep readiness guard verified\\n'); await access(join(projectDir, '.ktx', 'db.sqlite')); process.stdout.write('ktx ingest state verified\\n'); @@ -917,7 +916,7 @@ try { assert.ok([0, 1].includes(doctor.code), 'ktx status setup exit code must be 0 or 1'); requireStdout('ktx status setup', doctor, /KTX status/); requireStdout('ktx status setup', doctor, /No project here yet\\./); - requireStdout('ktx status setup', doctor, /Before you can run ktx setup/); + requireStdout('ktx status setup', doctor, /ktx setup/); assert.equal(doctor.stderr, '', 'ktx status setup wrote unexpected stderr'); } finally { await rm(root, { recursive: true, force: true }); diff --git a/scripts/package-artifacts.test.mjs b/scripts/package-artifacts.test.mjs index 0d3c71d6..708e9ccd 100644 --- a/scripts/package-artifacts.test.mjs +++ b/scripts/package-artifacts.test.mjs @@ -492,7 +492,7 @@ describe('verification snippets', () => { assert.match(source, /'--deep'/); assert.doesNotMatch(source, /'--enrich'/); assert.match(source, /ktx ingest fast verified/); - assert.match(source, /ktx ingest deep verified/); + assert.match(source, /ktx ingest deep readiness guard verified/); assert.match(source, /enrichment:/); assert.match(source, /mode: deterministic/); assert.doesNotMatch(source, /run\('pnpm', \['exec', 'ktx', 'ingest', 'run'/); @@ -511,6 +511,8 @@ describe('verification snippets', () => { assert.match(source, /Usage: ktx setup/); assert.doesNotMatch(source, new RegExp(["'demo'", "'--mode'", "'deterministic'"].join(', '))); assert.match(source, /'status', '--no-input'/); + assert.match(source, /KTX status/); + assert.match(source, /No project here yet/); assert.doesNotMatch(source, /function requireProjectStderr/); assert.match(source, /Object\.keys\(packageJson\.dependencies\)/); assert.match(source, /'@kaelio\/ktx'/);