mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-13 08:15:14 +02:00
Merge remote-tracking branch 'origin/main' into ktx-agent-command-history
# Conflicts: # packages/cli/src/agent-search-readiness.test.ts # packages/cli/src/agent-search-readiness.ts # packages/cli/src/agent.test.ts # packages/cli/src/project-dir.test.ts # packages/cli/src/setup-context.test.ts # packages/cli/src/standalone-smoke.test.ts # scripts/package-artifacts.mjs
This commit is contained in:
commit
a8bf0470ec
56 changed files with 411 additions and 3910 deletions
|
|
@ -186,10 +186,10 @@ describe('standalone example docs', () => {
|
|||
assert.match(quickstart, publicPackagePattern('npm install -g {package}'));
|
||||
assert.match(quickstart, /ktx dev runtime install --feature local-embeddings --yes/);
|
||||
assert.match(quickstart, /ktx dev runtime start --feature local-embeddings/);
|
||||
assert.match(quickstart, /Install `uv`, run `ktx dev runtime doctor`/);
|
||||
assert.match(quickstart, /Install `uv`, run `ktx dev runtime status`/);
|
||||
assert.match(packageArtifacts, /requires `uv` on `PATH`/);
|
||||
assert.match(packageArtifacts, /ktx dev runtime status/);
|
||||
assert.match(packageArtifacts, /ktx dev runtime doctor/);
|
||||
assert.match(packageArtifacts, /ktx dev runtime status/);
|
||||
assert.match(packageArtifacts, /ktx dev runtime prune --dry-run/);
|
||||
assert.match(packageArtifacts, /ktx dev runtime prune --yes/);
|
||||
assert.match(
|
||||
|
|
@ -223,7 +223,7 @@ describe('standalone example docs', () => {
|
|||
assert.doesNotMatch(readme, /installs the Python artifacts directly/);
|
||||
assert.match(readme, /requires `uv` on `PATH`/);
|
||||
assert.match(readme, /ktx dev runtime status/);
|
||||
assert.match(readme, /ktx dev runtime doctor/);
|
||||
assert.match(readme, /ktx dev runtime status/);
|
||||
assert.match(readme, /ktx dev runtime prune --dry-run/);
|
||||
assert.match(readme, /ktx dev runtime prune --yes/);
|
||||
assert.doesNotMatch(readme, /@ktx\/context/);
|
||||
|
|
@ -236,14 +236,15 @@ describe('standalone example docs', () => {
|
|||
const buildingContext = await readText('docs-site/content/docs/guides/building-context.mdx');
|
||||
const scanReference = await readText('docs-site/content/docs/cli-reference/ktx-scan.mdx');
|
||||
|
||||
assert.match(buildingContext, /ktx dev scan <connection-id>/);
|
||||
assert.match(buildingContext, /ktx dev scan status <run-id>/);
|
||||
assert.match(buildingContext, /ktx dev scan report <run-id>/);
|
||||
assert.match(scanReference, /ktx dev scan <connectionId> \[options\]/);
|
||||
assert.match(buildingContext, /ktx scan <connection-id>/);
|
||||
assert.match(buildingContext, /ktx status/);
|
||||
assert.doesNotMatch(buildingContext, /ktx scan status <run-id>/);
|
||||
assert.doesNotMatch(buildingContext, /ktx scan report <run-id>/);
|
||||
assert.match(scanReference, /ktx scan <connectionId> \[options\]/);
|
||||
assert.match(rootReadme, /raw-sources\//);
|
||||
assert.match(rootReadme, /live-database\//);
|
||||
assert.doesNotMatch(rootReadme, /Run a local ingest smoke test/);
|
||||
assert.doesNotMatch(rootReadme, /ktx dev ingest run --project-dir/);
|
||||
assert.doesNotMatch(rootReadme, /ktx ingest run --project-dir/);
|
||||
assert.doesNotMatch(rootReadme, /ktx ingest status --project-dir/);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -107,7 +107,6 @@ export function buildLiveDatabaseIngestArgs(projectDir, databaseIntrospectionUrl
|
|||
return [
|
||||
'exec',
|
||||
'ktx',
|
||||
'dev',
|
||||
'ingest',
|
||||
'run',
|
||||
'--project-dir',
|
||||
|
|
@ -325,12 +324,12 @@ async function main() {
|
|||
env: managedRuntimeEnv(cleanInstallDir),
|
||||
timeout: 120_000,
|
||||
});
|
||||
requireSuccess('ktx dev ingest run live-database', ingestRun);
|
||||
requireOutput('ktx dev ingest run live-database', ingestRun, /Status: done/);
|
||||
requireOutput('ktx dev ingest run live-database', ingestRun, /Adapter: live-database/);
|
||||
requireOutput('ktx dev ingest run live-database', ingestRun, /Diff: \+4\/~0\/-0\/=0/);
|
||||
requireOutput('ktx dev ingest run live-database', ingestRun, /Raw files: 4/);
|
||||
requireOutput('ktx dev ingest run live-database', ingestRun, /Work units: 2/);
|
||||
requireSuccess('ktx ingest run live-database', ingestRun);
|
||||
requireOutput('ktx ingest run live-database', ingestRun, /Status: done/);
|
||||
requireOutput('ktx ingest run live-database', ingestRun, /Adapter: live-database/);
|
||||
requireOutput('ktx ingest run live-database', ingestRun, /Diff: \+4\/~0\/-0\/=0/);
|
||||
requireOutput('ktx ingest run live-database', ingestRun, /Raw files: 4/);
|
||||
requireOutput('ktx ingest run live-database', ingestRun, /Work units: 2/);
|
||||
|
||||
const runId = getRunId(ingestRun.stdout);
|
||||
const ingestStatus = await run('pnpm', buildLiveDatabaseStatusArgs(projectDir, runId), {
|
||||
|
|
|
|||
|
|
@ -102,7 +102,6 @@ describe('installed live-database artifact smoke helpers', () => {
|
|||
assert.deepEqual(buildLiveDatabaseIngestArgs('/tmp/project', 'http://127.0.0.1:8765'), [
|
||||
'exec',
|
||||
'ktx',
|
||||
'dev',
|
||||
'ingest',
|
||||
'run',
|
||||
'--project-dir',
|
||||
|
|
|
|||
|
|
@ -789,12 +789,11 @@ try {
|
|||
requireOutput('ktx sl query sqlite execute', sqliteSlQuery, /"rows": \\[\\s*\\[\\s*3\\s*\\]\\s*\\]/);
|
||||
process.stdout.write('ktx sl query sqlite execute verified\\n');
|
||||
|
||||
const runtimeDoctor = await run('pnpm', ['exec', 'ktx', 'dev', 'runtime', 'doctor']);
|
||||
requireSuccess('ktx dev runtime doctor', runtimeDoctor);
|
||||
requireOutput('ktx dev runtime doctor', runtimeDoctor, /PASS uv/);
|
||||
requireOutput('ktx dev runtime doctor', runtimeDoctor, /PASS Bundled Python wheel/);
|
||||
requireOutput('ktx dev runtime doctor', runtimeDoctor, /PASS Managed Python runtime/);
|
||||
process.stdout.write('ktx dev runtime doctor verified\\n');
|
||||
const runtimeDoctor = await run('pnpm', ['exec', 'ktx', 'dev', 'runtime', 'status']);
|
||||
requireSuccess('ktx dev runtime status', runtimeDoctor);
|
||||
requireOutput('ktx dev runtime status', runtimeDoctor, /KTX Python runtime/);
|
||||
requireOutput('ktx dev runtime status', runtimeDoctor, /status: ready/);
|
||||
process.stdout.write('ktx dev runtime status verified\\n');
|
||||
|
||||
const runtimeStart = await run('pnpm', ['exec', 'ktx', 'dev', 'runtime', 'start']);
|
||||
requireSuccess('ktx dev runtime start', runtimeStart);
|
||||
|
|
@ -835,7 +834,7 @@ try {
|
|||
await assert.rejects(() => access(staleRuntimeDir));
|
||||
process.stdout.write('ktx dev runtime prune verified\\n');
|
||||
|
||||
const structuralScan = await run('pnpm', ['exec', 'ktx', 'dev', 'scan', 'warehouse',
|
||||
const structuralScan = await run('pnpm', ['exec', 'ktx', 'scan', 'warehouse',
|
||||
'--project-dir',
|
||||
projectDir,
|
||||
]);
|
||||
|
|
@ -844,34 +843,10 @@ try {
|
|||
requireOutput('ktx scan structural', structuralScan, /Mode: structural/);
|
||||
requireOutput('ktx scan structural', structuralScan, /Needs attention\\s+None/);
|
||||
const structuralScanRunId = getRunId(structuralScan.stdout);
|
||||
|
||||
const scanStatus = await run('pnpm', ['exec', 'ktx', 'dev', 'scan', 'status',
|
||||
'--project-dir',
|
||||
projectDir,
|
||||
structuralScanRunId,
|
||||
]);
|
||||
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/);
|
||||
|
||||
const scanReport = await run('pnpm', ['exec', 'ktx', 'dev', 'scan', 'report',
|
||||
'--project-dir',
|
||||
projectDir,
|
||||
'--json',
|
||||
structuralScanRunId,
|
||||
]);
|
||||
requireSuccess('ktx scan report', scanReport);
|
||||
const scanReportJson = JSON.parse(scanReport.stdout);
|
||||
assert.equal(scanReportJson.mode, 'structural');
|
||||
assert.equal(scanReportJson.connectionId, 'warehouse');
|
||||
assert.equal(scanReportJson.manifestShardsWritten, 1);
|
||||
assert.deepEqual(scanReportJson.artifactPaths.enrichmentArtifacts, []);
|
||||
assert.deepEqual(scanReportJson.artifactPaths.manifestShards, ['semantic-layer/warehouse/_schema/public.yaml']);
|
||||
await access(join(projectDir, 'semantic-layer', 'warehouse', '_schema', 'public.yaml'));
|
||||
process.stdout.write('ktx scan structural verified: ' + structuralScanRunId + '\\n');
|
||||
|
||||
const enrichedScan = await run('pnpm', ['exec', 'ktx', 'dev', 'scan', 'warehouse',
|
||||
const enrichedScan = await run('pnpm', ['exec', 'ktx', 'scan', 'warehouse',
|
||||
'--project-dir',
|
||||
projectDir,
|
||||
'--mode',
|
||||
|
|
@ -880,24 +855,14 @@ try {
|
|||
requireProjectStderr('ktx scan enriched', enrichedScan, projectDir);
|
||||
requireOutput('ktx scan enriched', enrichedScan, /Status: done/);
|
||||
requireOutput('ktx scan enriched', enrichedScan, /Mode: enriched/);
|
||||
requireOutput('ktx scan enriched', enrichedScan, /Enrichment artifacts:/);
|
||||
const enrichedScanRunId = getRunId(enrichedScan.stdout);
|
||||
const enrichedScanReport = await run('pnpm', ['exec', 'ktx', 'dev', 'scan', 'report',
|
||||
'--project-dir',
|
||||
projectDir,
|
||||
'--json',
|
||||
enrichedScanRunId,
|
||||
]);
|
||||
requireSuccess('ktx scan enriched report', enrichedScanReport);
|
||||
const enrichedScanReportJson = JSON.parse(enrichedScanReport.stdout);
|
||||
assert.equal(enrichedScanReportJson.mode, 'enriched');
|
||||
assert.ok(enrichedScanReportJson.artifactPaths.enrichmentArtifacts.length > 0);
|
||||
assert.deepEqual(enrichedScanReportJson.artifactPaths.manifestShards, ['semantic-layer/warehouse/_schema/public.yaml']);
|
||||
process.stdout.write('ktx scan enriched verified: ' + enrichedScanRunId + '\\n');
|
||||
|
||||
await mkdir(join(sourceDir, 'orders'), { recursive: true });
|
||||
await writeFile(join(sourceDir, 'orders', 'orders.json'), '{"name":"orders"}\\n', 'utf-8');
|
||||
|
||||
const ingestRun = await run('pnpm', ['exec', 'ktx', 'dev', 'ingest', 'run',
|
||||
const ingestRun = await run('pnpm', ['exec', 'ktx', 'ingest', 'run',
|
||||
'--project-dir',
|
||||
projectDir,
|
||||
'--connection-id',
|
||||
|
|
@ -907,14 +872,14 @@ try {
|
|||
'--source-dir',
|
||||
sourceDir,
|
||||
]);
|
||||
assert.equal(ingestRun.code, 1, 'ktx dev ingest run without an LLM provider must fail');
|
||||
assert.equal(ingestRun.code, 1, 'ktx ingest run without an LLM provider must fail');
|
||||
assert.match(
|
||||
ingestRun.stderr,
|
||||
/ktx dev ingest run requires llm\\.provider\\.backend: anthropic, vertex, or gateway, or an injected agentRunner/,
|
||||
/ktx ingest run requires llm\\.provider\\.backend: anthropic, vertex, or gateway, or an injected agentRunner/,
|
||||
);
|
||||
|
||||
await access(join(projectDir, '.ktx', 'db.sqlite'));
|
||||
process.stdout.write('ktx dev ingest provider guard verified\\n');
|
||||
process.stdout.write('ktx ingest provider guard verified\\n');
|
||||
} finally {
|
||||
if (daemonStarted) {
|
||||
await run('pnpm', ['exec', 'ktx', 'dev', 'runtime', 'stop']);
|
||||
|
|
|
|||
|
|
@ -484,8 +484,8 @@ describe('verification snippets', () => {
|
|||
assert.match(source, /ktx dev runtime status ready/);
|
||||
assert.match(source, /runtimeStatusAfter\.kind, 'ready'/);
|
||||
assert.match(source, /runtimeStatusAfter\.manifest\.features/);
|
||||
assert.match(source, /ktx dev runtime doctor/);
|
||||
assert.match(source, /PASS Managed Python runtime/);
|
||||
assert.match(source, /ktx dev runtime status/);
|
||||
assert.match(source, /status: ready/);
|
||||
assert.match(source, /ktx dev runtime start/);
|
||||
assert.match(source, /ktx dev runtime start reuse/);
|
||||
assert.match(source, /Using existing KTX Python daemon/);
|
||||
|
|
@ -497,20 +497,18 @@ describe('verification snippets', () => {
|
|||
assert.match(source, /ktx dev runtime prune confirmed/);
|
||||
assert.match(source, /Removed stale KTX Python runtimes/);
|
||||
assert.match(source, /assert\.rejects\(\(\) => access\(staleRuntimeDir\)\)/);
|
||||
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'ktx',\s*'dev',\s*'scan',\s*'warehouse'/);
|
||||
assert.match(source, /run\('pnpm', \[\s*'exec',\s*'ktx',\s*'scan',\s*'warehouse'/);
|
||||
assert.match(source, /'--mode',\s*'enriched'/);
|
||||
assert.doesNotMatch(source, /'--enrich'/);
|
||||
assert.match(source, /ktx scan structural verified/);
|
||||
assert.match(source, /ktx scan enriched verified/);
|
||||
assert.match(source, /scanReportJson\.artifactPaths\.manifestShards/);
|
||||
assert.match(source, /scanReportJson\.artifactPaths\.enrichmentArtifacts/);
|
||||
assert.match(source, /enrichment:/);
|
||||
assert.match(source, /mode: deterministic/);
|
||||
assert.match(source, /run\('pnpm', \['exec', 'ktx', 'dev', 'ingest', 'run'/);
|
||||
assert.match(source, /run\('pnpm', \['exec', 'ktx', 'ingest', 'run'/);
|
||||
assert.match(source, /access\(join\(projectDir, '\.ktx', 'db\.sqlite'\)\)/);
|
||||
assert.match(source, /SQLite knowledge index/);
|
||||
assert.match(source, /ktx dev ingest run requires llm\\.provider\\.backend: anthropic, vertex, or gateway/);
|
||||
assert.match(source, /ktx dev ingest provider guard verified/);
|
||||
assert.match(source, /ktx ingest run requires llm\\.provider\\.backend: anthropic, vertex, or gateway/);
|
||||
assert.match(source, /ktx ingest provider guard verified/);
|
||||
});
|
||||
|
||||
describe('npmCliSmokeSource', () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import { mkdir as fsMkdir, writeFile as fsWriteFile } from 'node:fs/promises';
|
||||
import { mkdir as fsMkdir, readFile as fsReadFile, writeFile as fsWriteFile } from 'node:fs/promises';
|
||||
import { execFile as childExecFile } from 'node:child_process';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
|
|
@ -90,11 +90,7 @@ function parseArgs(argv) {
|
|||
}
|
||||
|
||||
export function buildOrbitScanArgv(input) {
|
||||
return ['dev', 'scan', input.connectionId, '--enrich', '--project-dir', input.projectDir];
|
||||
}
|
||||
|
||||
export function buildOrbitReportArgv(input) {
|
||||
return ['dev', 'scan', 'report', '--json', '--project-dir', input.projectDir, input.runId];
|
||||
return ['scan', input.connectionId, '--mode', 'relationships', '--project-dir', input.projectDir];
|
||||
}
|
||||
|
||||
export function extractRunId(stdout) {
|
||||
|
|
@ -102,6 +98,11 @@ export function extractRunId(stdout) {
|
|||
return match?.[1] ?? null;
|
||||
}
|
||||
|
||||
export function extractReportPath(stdout) {
|
||||
const match = stdout.match(/^\s*Report:\s*(\S+)/m);
|
||||
return match?.[1] ?? null;
|
||||
}
|
||||
|
||||
function listLines(values) {
|
||||
if (!values || values.length === 0) {
|
||||
return ['- none'];
|
||||
|
|
@ -204,11 +205,9 @@ export function formatOrbitVerificationMarkdown(result) {
|
|||
|
||||
if (result.status === 'success') {
|
||||
lines.push(
|
||||
'## JSON Report Command',
|
||||
'## Scan Report Artifact',
|
||||
'',
|
||||
'```bash',
|
||||
result.reportCommand,
|
||||
'```',
|
||||
`- ${result.reportPath}`,
|
||||
'',
|
||||
...formatSuccess(result),
|
||||
);
|
||||
|
|
@ -250,6 +249,7 @@ export async function runOrbitVerification(options = {}) {
|
|||
const now = options.now ?? (() => new Date());
|
||||
const mkdir = options.mkdir ?? fsMkdir;
|
||||
const writeFile = options.writeFile ?? fsWriteFile;
|
||||
const readFile = options.readFile ?? fsReadFile;
|
||||
const date = dateOnly(now());
|
||||
const env = options.env ?? orbitVerificationEnv(projectDir);
|
||||
const runWithEnv = (argv, runnerOptions) => runner(argv, { ...runnerOptions, env });
|
||||
|
|
@ -285,33 +285,32 @@ export async function runOrbitVerification(options = {}) {
|
|||
scanStderr: scan.stderr,
|
||||
};
|
||||
} else {
|
||||
const reportArgv = buildOrbitReportArgv({ projectDir, runId });
|
||||
const reportOutput = await runBufferedWorkspaceKtx(runWithEnv, reportArgv, rootDir, execFile);
|
||||
if (reportOutput.exitCode !== 0) {
|
||||
const scanReportPath = extractReportPath(scan.stdout);
|
||||
if (!scanReportPath) {
|
||||
result = {
|
||||
status: 'blocked',
|
||||
date,
|
||||
connectionId,
|
||||
projectDir,
|
||||
scanCommand: shellCommand(scanArgv),
|
||||
scanExitCode: reportOutput.exitCode,
|
||||
blocker: firstNonEmptyLine(reportOutput.stderr, reportOutput.stdout),
|
||||
scanStdout: `${scan.stdout}\n${reportOutput.stdout}`.trim(),
|
||||
scanStderr: `${scan.stderr}\n${reportOutput.stderr}`.trim(),
|
||||
scanExitCode: scan.exitCode,
|
||||
blocker: 'KTX scan completed without printing a report artifact path',
|
||||
scanStdout: scan.stdout,
|
||||
scanStderr: scan.stderr,
|
||||
};
|
||||
} else {
|
||||
const fullScanReportPath = resolve(projectDir, scanReportPath);
|
||||
result = {
|
||||
status: 'success',
|
||||
date,
|
||||
connectionId,
|
||||
projectDir,
|
||||
scanCommand: shellCommand(scanArgv),
|
||||
reportCommand: shellCommand(reportArgv),
|
||||
reportPath: fullScanReportPath,
|
||||
scanExitCode: scan.exitCode,
|
||||
reportExitCode: reportOutput.exitCode,
|
||||
scanStdout: scan.stdout,
|
||||
scanStderr: scan.stderr,
|
||||
report: JSON.parse(reportOutput.stdout),
|
||||
report: JSON.parse(await readFile(fullScanReportPath, 'utf8')),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import { readFile } from 'node:fs/promises';
|
|||
import { dirname } from 'node:path';
|
||||
import { describe, it } from 'node:test';
|
||||
import {
|
||||
buildOrbitReportArgv,
|
||||
buildOrbitScanArgv,
|
||||
defaultOrbitVerificationProjectDir,
|
||||
extractReportPath,
|
||||
extractRunId,
|
||||
formatOrbitVerificationMarkdown,
|
||||
runOrbitVerification,
|
||||
|
|
@ -59,24 +59,15 @@ describe('relationship Orbit verification helper', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('builds the current KTX launcher arguments for scan and JSON report commands', () => {
|
||||
it('builds the current KTX launcher arguments for scan commands', () => {
|
||||
assert.deepEqual(buildOrbitScanArgv({ connectionId: 'orbit', projectDir: '/tmp/orbit-project' }), [
|
||||
'dev',
|
||||
'scan',
|
||||
'orbit',
|
||||
'--enrich',
|
||||
'--mode',
|
||||
'relationships',
|
||||
'--project-dir',
|
||||
'/tmp/orbit-project',
|
||||
]);
|
||||
assert.deepEqual(buildOrbitReportArgv({ projectDir: '/tmp/orbit-project', runId: 'scan-orbit-1' }), [
|
||||
'dev',
|
||||
'scan',
|
||||
'report',
|
||||
'--json',
|
||||
'--project-dir',
|
||||
'/tmp/orbit-project',
|
||||
'scan-orbit-1',
|
||||
]);
|
||||
});
|
||||
|
||||
it('uses the checked-in Orbit verification project by default', async () => {
|
||||
|
|
@ -95,22 +86,17 @@ describe('relationship Orbit verification helper', () => {
|
|||
runWorkspaceKtx: async (argv, options) => {
|
||||
calls.push(argv);
|
||||
envs.push(options.env);
|
||||
if (argv[2] === 'report') {
|
||||
options.stdout.write(successReportJson());
|
||||
return 0;
|
||||
}
|
||||
options.stdout.write('KTX scan completed\nRun: scan-orbit-1\nConnection: orbit\n');
|
||||
options.stdout.write('KTX scan completed\nRun: scan-orbit-1\nConnection: orbit\n Report: reports/scan-report.json\n');
|
||||
return 0;
|
||||
},
|
||||
readFile: async () => successReportJson(),
|
||||
});
|
||||
|
||||
assert.equal(result.status, 'success');
|
||||
assert.deepEqual(calls, [
|
||||
['dev', 'scan', 'orbit', '--enrich', '--project-dir', defaultProjectDir],
|
||||
['dev', 'scan', 'report', '--json', '--project-dir', defaultProjectDir, 'scan-orbit-1'],
|
||||
['scan', 'orbit', '--mode', 'relationships', '--project-dir', defaultProjectDir],
|
||||
]);
|
||||
assert.equal(envs[0].GIT_CEILING_DIRECTORIES, dirname(defaultProjectDir));
|
||||
assert.equal(envs[1].GIT_CEILING_DIRECTORIES, dirname(defaultProjectDir));
|
||||
assert.equal(writes.length, 1);
|
||||
assert.match(writes[0].content, new RegExp(defaultProjectDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
|
||||
});
|
||||
|
|
@ -129,19 +115,15 @@ describe('relationship Orbit verification helper', () => {
|
|||
writeFile: async () => {},
|
||||
runWorkspaceKtx: async (argv, options) => {
|
||||
calls.push(argv);
|
||||
if (argv[2] === 'report') {
|
||||
options.stdout.write(successReportJson());
|
||||
return 0;
|
||||
}
|
||||
options.stdout.write('KTX scan completed\nRun: scan-orbit-1\nConnection: orbit\n');
|
||||
options.stdout.write('KTX scan completed\nRun: scan-orbit-1\nConnection: orbit\n Report: reports/scan-report.json\n');
|
||||
return 0;
|
||||
},
|
||||
readFile: async () => successReportJson(),
|
||||
});
|
||||
|
||||
assert.equal(result.projectDir, '/tmp/orbit-project-from-env');
|
||||
assert.deepEqual(calls, [
|
||||
['dev', 'scan', 'orbit', '--enrich', '--project-dir', '/tmp/orbit-project-from-env'],
|
||||
['dev', 'scan', 'report', '--json', '--project-dir', '/tmp/orbit-project-from-env', 'scan-orbit-1'],
|
||||
['scan', 'orbit', '--mode', 'relationships', '--project-dir', '/tmp/orbit-project-from-env'],
|
||||
]);
|
||||
} finally {
|
||||
if (previousProjectDir === undefined) {
|
||||
|
|
@ -155,6 +137,7 @@ describe('relationship Orbit verification helper', () => {
|
|||
it('extracts the run id from human scan output', () => {
|
||||
assert.equal(extractRunId(`KTX scan completed\nStatus: done\nRun: scan-orbit-1\nConnection: orbit\n`), 'scan-orbit-1');
|
||||
assert.equal(extractRunId('KTX scan completed without a run line\n'), null);
|
||||
assert.equal(extractReportPath('Artifacts\n Report: reports/scan-report.json\n'), 'reports/scan-report.json');
|
||||
});
|
||||
|
||||
it('formats successful Orbit verification evidence from the JSON report', () => {
|
||||
|
|
@ -163,10 +146,9 @@ describe('relationship Orbit verification helper', () => {
|
|||
date: '2026-05-07',
|
||||
connectionId: 'orbit',
|
||||
projectDir: '/tmp/orbit-project',
|
||||
scanCommand: 'pnpm run ktx -- dev scan orbit --enrich --project-dir /tmp/orbit-project',
|
||||
reportCommand: 'pnpm run ktx -- dev scan report --json --project-dir /tmp/orbit-project scan-orbit-1',
|
||||
scanCommand: 'pnpm run ktx -- scan orbit --mode relationships --project-dir /tmp/orbit-project',
|
||||
reportPath: '/tmp/orbit-project/reports/scan-report.json',
|
||||
scanExitCode: 0,
|
||||
reportExitCode: 0,
|
||||
scanStdout: 'KTX scan completed\nRun: scan-orbit-1\n',
|
||||
scanStderr: '',
|
||||
report: JSON.parse(successReportJson()),
|
||||
|
|
@ -189,7 +171,7 @@ describe('relationship Orbit verification helper', () => {
|
|||
date: '2026-05-07',
|
||||
connectionId: 'orbit',
|
||||
projectDir: '/tmp/orbit-project',
|
||||
scanCommand: 'pnpm run ktx -- dev scan orbit --enrich --project-dir /tmp/orbit-project',
|
||||
scanCommand: 'pnpm run ktx -- scan orbit --mode relationships --project-dir /tmp/orbit-project',
|
||||
scanExitCode: 1,
|
||||
blocker: 'Connection "orbit" was not found',
|
||||
scanStdout: '',
|
||||
|
|
@ -202,7 +184,7 @@ describe('relationship Orbit verification helper', () => {
|
|||
assert.doesNotMatch(markdown, /scan\.enrichment\.mode is required/);
|
||||
});
|
||||
|
||||
it('runs scan then JSON report and writes success Markdown', async () => {
|
||||
it('runs scan then reads the report artifact and writes success Markdown', async () => {
|
||||
const calls = [];
|
||||
const writes = [];
|
||||
const result = await runOrbitVerification({
|
||||
|
|
@ -216,19 +198,15 @@ describe('relationship Orbit verification helper', () => {
|
|||
},
|
||||
runWorkspaceKtx: async (argv, options) => {
|
||||
calls.push(argv);
|
||||
if (argv[2] === 'report') {
|
||||
options.stdout.write(successReportJson());
|
||||
return 0;
|
||||
}
|
||||
options.stdout.write('KTX scan completed\nRun: scan-orbit-1\nConnection: orbit\n');
|
||||
options.stdout.write('KTX scan completed\nRun: scan-orbit-1\nConnection: orbit\n Report: reports/scan-report.json\n');
|
||||
return 0;
|
||||
},
|
||||
readFile: async () => successReportJson(),
|
||||
});
|
||||
|
||||
assert.equal(result.status, 'success');
|
||||
assert.deepEqual(calls, [
|
||||
['dev', 'scan', 'orbit', '--enrich', '--project-dir', '/tmp/orbit-project'],
|
||||
['dev', 'scan', 'report', '--json', '--project-dir', '/tmp/orbit-project', 'scan-orbit-1'],
|
||||
['scan', 'orbit', '--mode', 'relationships', '--project-dir', '/tmp/orbit-project'],
|
||||
]);
|
||||
assert.equal(writes.length, 1);
|
||||
assert.equal(writes[0].path, '/tmp/orbit-report.md');
|
||||
|
|
|
|||
|
|
@ -83,10 +83,6 @@ async function isBuildStale(rootDir, binPath, fs) {
|
|||
return false;
|
||||
}
|
||||
|
||||
function isShellCompletionRequest(argv) {
|
||||
return argv[0] === '__complete' || (argv[0] === 'dev' && argv[1] === '__complete');
|
||||
}
|
||||
|
||||
async function runBuffered(execFile, stdout, stderr, command, args, options) {
|
||||
try {
|
||||
const result = await execFile(command, args, { cwd: options.cwd, env: options.env, maxBuffer: 1024 * 1024 * 16 });
|
||||
|
|
@ -150,8 +146,7 @@ export async function runWorkspaceKtx(argv, options = {}) {
|
|||
const commandEnv = options.env;
|
||||
|
||||
const binExists = await fileExists(binPath, access);
|
||||
const skipStaleBuildCheck = binExists && isShellCompletionRequest(cliArgv);
|
||||
const needsBuild = !binExists || (!skipStaleBuildCheck && (await isBuildStale(rootDir, binPath, fs)));
|
||||
const needsBuild = !binExists || (await isBuildStale(rootDir, binPath, fs));
|
||||
if (needsBuild) {
|
||||
stderr.write(
|
||||
binExists
|
||||
|
|
|
|||
|
|
@ -105,53 +105,6 @@ test('runWorkspaceKtx drops a leading npm argument separator', async () => {
|
|||
]);
|
||||
});
|
||||
|
||||
test('runWorkspaceKtx skips stale-build checks for shell completion when dist exists', async () => {
|
||||
const calls = [];
|
||||
let statCalls = 0;
|
||||
|
||||
const exitCode = await runWorkspaceKtx(['dev', '__complete', '--shell', 'zsh', '--position', '2', '--', 'ktx', ''], {
|
||||
rootDir: '/workspace/ktx',
|
||||
access: async () => undefined,
|
||||
stat: async (path) => {
|
||||
statCalls += 1;
|
||||
return {
|
||||
mtimeMs: path.endsWith('/packages/cli/dist/bin.js') ? 2000 : 3000,
|
||||
isDirectory: () => path.endsWith('/src') || path.endsWith('/packages'),
|
||||
};
|
||||
},
|
||||
readdir: async () => {
|
||||
throw new Error('completion should not scan source directories');
|
||||
},
|
||||
execFile: async (command, args, options) => {
|
||||
calls.push({ command, args, cwd: options.cwd });
|
||||
return { stdout: 'connect:Add, list, test, and map data sources\n', stderr: '' };
|
||||
},
|
||||
stdout: { write: () => undefined },
|
||||
stderr: { write: () => undefined },
|
||||
});
|
||||
|
||||
assert.equal(exitCode, 0);
|
||||
assert.equal(statCalls, 0);
|
||||
assert.deepEqual(calls, [
|
||||
{
|
||||
command: process.execPath,
|
||||
args: [
|
||||
'/workspace/ktx/packages/cli/dist/bin.js',
|
||||
'dev',
|
||||
'__complete',
|
||||
'--shell',
|
||||
'zsh',
|
||||
'--position',
|
||||
'2',
|
||||
'--',
|
||||
'ktx',
|
||||
'',
|
||||
],
|
||||
cwd: '/workspace/ktx',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('runWorkspaceKtx builds the workspace CLI before running it when dist is missing', async () => {
|
||||
const calls = [];
|
||||
const logs = [];
|
||||
|
|
@ -199,7 +152,7 @@ test('runWorkspaceKtx rebuilds before running when workspace sources are newer t
|
|||
const logs = [];
|
||||
let sourceMtimeMs = 3000;
|
||||
|
||||
const exitCode = await runWorkspaceKtx(['dev', 'scan', 'orbit', '--enrich'], {
|
||||
const exitCode = await runWorkspaceKtx(['scan', 'orbit', '--mode', 'relationships'], {
|
||||
rootDir: '/workspace/ktx',
|
||||
access: async () => undefined,
|
||||
stat: async (path) => ({
|
||||
|
|
@ -232,7 +185,7 @@ test('runWorkspaceKtx rebuilds before running when workspace sources are newer t
|
|||
calls.map((call) => [call.command, call.args]),
|
||||
[
|
||||
['pnpm', ['run', 'build']],
|
||||
[process.execPath, ['/workspace/ktx/packages/cli/dist/bin.js', 'dev', 'scan', 'orbit', '--enrich']],
|
||||
[process.execPath, ['/workspace/ktx/packages/cli/dist/bin.js', 'scan', 'orbit', '--mode', 'relationships']],
|
||||
],
|
||||
);
|
||||
assert.deepEqual(logs, [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue