import assert from 'node:assert/strict'; import { createHash } from 'node:crypto'; import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { describe, it } from 'node:test'; import { PUBLIC_NPM_PACKAGE_VERSION } from './public-npm-release-metadata.mjs'; import { CLI_PYTHON_ASSET_MANIFEST, RUNTIME_WHEEL_DISTRIBUTION_NAME, RUNTIME_WHEEL_NORMALIZED_NAME, RUNTIME_WHEEL_PACKAGE_VERSION, artifactManifestPath, buildArtifactCommands, copyRuntimeWheelAssets, findPythonArtifacts, NPM_ARTIFACT_PACKAGES, npmCliSmokeSource, npmRuntimeSmokeSource, npmSmokePackageJson, npmSmokePnpmWorkspaceYaml, npmVerifySource, packageArtifactLayout, packageReleaseMetadata, verifyArtifactManifest, writeArtifactManifest, } from './package-artifacts.mjs'; async function writeJson(path, value) { await writeFile(path, `${JSON.stringify(value, null, 2)}\n`); } async function writeReleaseMetadataInputs(root) { await writeJson(join(root, 'release-policy.json'), { schemaVersion: 1, releaseMode: 'ci-artifact-only', npm: { publish: false, registry: null, access: 'public', tag: 'next', packages: ['@kaelio/ktx'], }, python: { publish: false, repository: null, packages: ['kaelio-ktx'], }, publishedPackageSmoke: { packageName: '@kaelio/ktx', version: PUBLIC_NPM_PACKAGE_VERSION, registry: null, }, runtimeInstaller: { uvStrategy: 'path-prerequisite', bootstrapUv: false, missingUvBehavior: 'focused-error', }, requiredBeforePublishing: ['Choose public release version.'], }); for (const packageInfo of NPM_ARTIFACT_PACKAGES) { await mkdir(join(root, packageInfo.packageRoot), { recursive: true }); await writeJson(join(root, packageInfo.packageRoot, 'package.json'), { name: packageInfo.name, version: PUBLIC_NPM_PACKAGE_VERSION, }); } } function runtimeWheelFilename(version = RUNTIME_WHEEL_PACKAGE_VERSION) { return `kaelio_ktx-${version}-py3-none-any.whl`; } async function writeUploadableArtifactFixtures(layout) { await mkdir(layout.npmDir, { recursive: true }); await mkdir(layout.pythonDir, { recursive: true }); const fileContents = new Map([ ...NPM_ARTIFACT_PACKAGES.map((packageInfo) => [ layout.npmTarballs[packageInfo.name], `${packageInfo.name}-tarball`, ]), [ join(layout.pythonDir, runtimeWheelFilename()), 'kaelio-ktx-runtime-wheel', ], ]); for (const [path, contents] of fileContents) { await writeFile(path, contents); } } describe('packageArtifactLayout', () => { it('uses stable artifact paths under ktx/dist/artifacts', () => { const layout = packageArtifactLayout('/repo/ktx', PUBLIC_NPM_PACKAGE_VERSION); assert.equal(layout.artifactDir, join('/repo/ktx', 'dist', 'artifacts')); assert.equal(layout.npmDir, join('/repo/ktx', 'dist', 'artifacts', 'npm')); assert.equal(layout.pythonDir, join('/repo/ktx', 'dist', 'artifacts', 'python')); assert.equal( layout.cliTarball, join('/repo/ktx', 'dist', 'artifacts', 'npm', `kaelio-ktx-${PUBLIC_NPM_PACKAGE_VERSION}.tgz`), ); assert.deepEqual(Object.keys(layout.npmTarballs), ['@kaelio/ktx']); }); }); describe('buildArtifactCommands', () => { it('builds the CLI package, then the runtime wheel, then packs the npm tarball directly', () => { const layout = packageArtifactLayout('/repo/ktx', PUBLIC_NPM_PACKAGE_VERSION); const commands = buildArtifactCommands(layout); const expectedBuildCommand = process.platform === 'win32' ? ['cmd.exe', ['/d', '/s', '/c', 'pnpm', '--filter', '@kaelio/ktx', 'run', 'build'], layout.rootDir] : ['pnpm', ['--filter', '@kaelio/ktx', 'run', 'build'], layout.rootDir]; const expectedPackCommand = process.platform === 'win32' ? ['cmd.exe', ['/d', '/s', '/c', 'pnpm', 'pack', '--out', layout.cliTarball], join('/repo/ktx', 'packages', 'cli')] : ['pnpm', ['pack', '--out', layout.cliTarball], join('/repo/ktx', 'packages', 'cli')]; assert.deepEqual( commands.map((command) => [command.command, command.args, command.cwd]), [ expectedBuildCommand, [process.execPath, ['scripts/build-python-runtime-wheel.mjs'], layout.rootDir], expectedPackCommand, ], ); }); }); describe('packageReleaseMetadata', () => { it('reads package identities and versions from package manifests', async () => { const root = await mkdtemp(join(tmpdir(), 'ktx-release-metadata-test-')); try { await writeReleaseMetadataInputs(root); assert.deepEqual(await packageReleaseMetadata(root), [ { ecosystem: 'npm', packageName: '@kaelio/ktx', packageRoot: 'packages/cli', packageVersion: PUBLIC_NPM_PACKAGE_VERSION, private: false, releaseMode: 'ci-artifact-only', }, { ecosystem: 'python', packageName: 'kaelio-ktx', packageRoot: 'python/runtime-wheel', packageVersion: RUNTIME_WHEEL_PACKAGE_VERSION, private: false, releaseMode: 'ci-artifact-only', }, ]); } finally { await rm(root, { recursive: true, force: true }); } }); }); describe('findPythonArtifacts', () => { it('finds the bundled runtime wheel only', async () => { const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-test-')); try { await writeFile(join(root, runtimeWheelFilename()), ''); assert.deepEqual(await findPythonArtifacts(root), { runtimeWheel: join(root, runtimeWheelFilename()), }); } finally { await rm(root, { recursive: true, force: true }); } }); it('throws when a required Python artifact is missing', async () => { const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-test-')); try { await assert.rejects(() => findPythonArtifacts(root), /Missing Python artifact: kaelio-ktx runtime wheel/); } finally { await rm(root, { recursive: true, force: true }); } }); }); describe('artifact manifest', () => { it('writes release metadata, source revision, checksums, and byte counts for every uploadable artifact', async () => { const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-manifest-test-')); const layout = packageArtifactLayout(root, PUBLIC_NPM_PACKAGE_VERSION); try { await writeReleaseMetadataInputs(root); await writeUploadableArtifactFixtures(layout); const manifest = await writeArtifactManifest(layout, new Date('2026-04-28T12:00:00.000Z'), { sourceRevision: 'abc123', }); assert.equal(artifactManifestPath(layout), join(root, 'dist', 'artifacts', 'manifest.json')); assert.equal(manifest.schemaVersion, 2); assert.equal(manifest.generatedAt, '2026-04-28T12:00:00.000Z'); assert.equal(manifest.sourceRevision, 'abc123'); assert.deepEqual( manifest.packages.filter((entry) => entry.ecosystem === 'npm'), [ { ecosystem: 'npm', packageName: '@kaelio/ktx', packageRoot: 'packages/cli', packageVersion: PUBLIC_NPM_PACKAGE_VERSION, private: false, releaseMode: 'ci-artifact-only', }, ], ); assert.deepEqual( manifest.packages.filter((entry) => entry.ecosystem === 'python'), [ { ecosystem: 'python', packageName: 'kaelio-ktx', packageRoot: 'python/runtime-wheel', packageVersion: RUNTIME_WHEEL_PACKAGE_VERSION, private: false, releaseMode: 'ci-artifact-only', }, ], ); assert.deepEqual( manifest.files .filter((file) => file.ecosystem === 'npm') .map((file) => ({ artifactKind: file.artifactKind, ecosystem: file.ecosystem, packageName: file.packageName, packageVersion: file.packageVersion, path: file.path, })) .sort((left, right) => left.packageName.localeCompare(right.packageName)), [ { artifactKind: 'tarball', ecosystem: 'npm', packageName: '@kaelio/ktx', packageVersion: PUBLIC_NPM_PACKAGE_VERSION, path: `npm/kaelio-ktx-${PUBLIC_NPM_PACKAGE_VERSION}.tgz`, }, ], ); assert.deepEqual( manifest.files .filter((file) => file.ecosystem === 'python') .map((file) => ({ artifactKind: file.artifactKind, ecosystem: file.ecosystem, packageName: file.packageName, packageVersion: file.packageVersion, path: file.path, })), [ { artifactKind: 'wheel', ecosystem: 'python', packageName: 'kaelio-ktx', packageVersion: RUNTIME_WHEEL_PACKAGE_VERSION, path: `python/${runtimeWheelFilename()}`, }, ], ); const npmEntry = manifest.files.find( (file) => file.path === `npm/kaelio-ktx-${PUBLIC_NPM_PACKAGE_VERSION}.tgz`, ); assert.ok(npmEntry); assert.equal(npmEntry.bytes, Buffer.byteLength('@kaelio/ktx-tarball')); assert.equal(npmEntry.sha256, createHash('sha256').update('@kaelio/ktx-tarball').digest('hex')); const writtenManifest = JSON.parse(await readFile(artifactManifestPath(layout), 'utf-8')); assert.deepEqual(writtenManifest, manifest); } finally { await rm(root, { recursive: true, force: true }); } }); }); describe('verifyArtifactManifest', () => { it('accepts a schema version 2 manifest that matches the artifact directory', async () => { const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-verify-manifest-test-')); const layout = packageArtifactLayout(root, PUBLIC_NPM_PACKAGE_VERSION); try { await writeReleaseMetadataInputs(root); await writeUploadableArtifactFixtures(layout); await writeArtifactManifest(layout, new Date('2026-04-28T12:00:00.000Z'), { sourceRevision: 'abc123', }); const manifest = await verifyArtifactManifest(layout, { expectedSourceRevision: 'abc123', }); assert.equal(manifest.schemaVersion, 2); assert.equal(manifest.sourceRevision, 'abc123'); assert.equal(manifest.files.length, NPM_ARTIFACT_PACKAGES.length + 1); } finally { await rm(root, { recursive: true, force: true }); } }); it('rejects a manifest when a file checksum has drifted', async () => { const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-checksum-drift-test-')); const layout = packageArtifactLayout(root, PUBLIC_NPM_PACKAGE_VERSION); try { await writeReleaseMetadataInputs(root); await writeUploadableArtifactFixtures(layout); await writeArtifactManifest(layout, new Date('2026-04-28T12:00:00.000Z'), { sourceRevision: 'abc123', }); await writeFile(layout.contextTarball, 'changed-context-tarball'); await assert.rejects( () => verifyArtifactManifest(layout), /Artifact manifest files do not match artifact contents/, ); } finally { await rm(root, { recursive: true, force: true }); } }); it('rejects a manifest with an unsafe artifact path', async () => { const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-path-test-')); const layout = packageArtifactLayout(root, PUBLIC_NPM_PACKAGE_VERSION); try { await writeReleaseMetadataInputs(root); await writeUploadableArtifactFixtures(layout); const manifest = await writeArtifactManifest(layout, new Date('2026-04-28T12:00:00.000Z'), { sourceRevision: 'abc123', }); manifest.files[0].path = '../outside.tgz'; await writeFile(artifactManifestPath(layout), `${JSON.stringify(manifest, null, 2)}\n`); await assert.rejects(() => verifyArtifactManifest(layout), /Unsafe artifact manifest path: \.\.\/outside\.tgz/); } finally { await rm(root, { recursive: true, force: true }); } }); it('rejects a manifest from the wrong source revision when one is required', async () => { const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-revision-test-')); const layout = packageArtifactLayout(root, PUBLIC_NPM_PACKAGE_VERSION); try { await writeReleaseMetadataInputs(root); await writeUploadableArtifactFixtures(layout); await writeArtifactManifest(layout, new Date('2026-04-28T12:00:00.000Z'), { sourceRevision: 'abc123', }); await assert.rejects( () => verifyArtifactManifest(layout, { expectedSourceRevision: 'def456', }), /Artifact manifest sourceRevision mismatch: expected def456, got abc123/, ); } finally { await rm(root, { recursive: true, force: true }); } }); }); describe('copyRuntimeWheelAssets', () => { it('copies the runtime wheel and checksum manifest into CLI assets', async () => { const root = await mkdtemp(join(tmpdir(), 'ktx-runtime-assets-test-')); const layout = packageArtifactLayout(root, PUBLIC_NPM_PACKAGE_VERSION); try { await mkdir(layout.pythonDir, { recursive: true }); await writeFile( join(layout.pythonDir, runtimeWheelFilename()), 'kaelio-ktx-runtime-wheel', ); const assets = await copyRuntimeWheelAssets(layout, { runtimeWheel: join(layout.pythonDir, runtimeWheelFilename()), }); assert.equal( assets.wheelPath, join(root, 'packages', 'cli', 'assets', 'python', runtimeWheelFilename()), ); assert.equal( assets.manifestPath, join(root, 'packages', 'cli', 'assets', 'python', CLI_PYTHON_ASSET_MANIFEST), ); const manifest = JSON.parse(await readFile(assets.manifestPath, 'utf8')); assert.deepEqual(manifest, { schemaVersion: 1, distributionName: RUNTIME_WHEEL_DISTRIBUTION_NAME, normalizedName: RUNTIME_WHEEL_NORMALIZED_NAME, version: RUNTIME_WHEEL_PACKAGE_VERSION, wheel: { file: runtimeWheelFilename(), sha256: createHash('sha256') .update('kaelio-ktx-runtime-wheel') .digest('hex'), bytes: Buffer.byteLength('kaelio-ktx-runtime-wheel'), }, }); } finally { await rm(root, { recursive: true, force: true }); } }); }); describe('verifyNpmArtifacts', () => { it('does not prepare an external Python environment for the npm smoke', async () => { const source = await readFile(new URL('./package-artifacts.mjs', import.meta.url), 'utf8'); const start = source.indexOf('async function verifyNpmArtifacts'); const end = source.indexOf('async function verifyNpmCliArtifacts'); assert.ok(start > 0, 'verifyNpmArtifacts function must exist'); assert.ok(end > start, 'verifyNpmCliArtifacts must follow verifyNpmArtifacts'); const body = source.slice(start, end); assert.doesNotMatch(body, /uv', \['venv', '\.venv'\]/); assert.doesNotMatch(body, /pythonArtifactInstallArgs/); assert.doesNotMatch(body, /npmSmokePythonEnv/); }); }); describe('standalone Python artifact cleanup', () => { it('does not build or verify standalone Python package artifacts', async () => { const source = await readFile(new URL('./package-artifacts.mjs', import.meta.url), 'utf8'); assert.doesNotMatch(source, /uv', \['build', '--package', 'ktx-sl'/); assert.doesNotMatch(source, /uv', \['build', '--package', 'ktx-daemon'/); assert.doesNotMatch(source, /async function verifyPythonArtifacts/); assert.doesNotMatch(source, /pythonArtifactInstallArgs/); assert.doesNotMatch(source, /pythonVerifySource/); assert.doesNotMatch(source, /ktx_sl-0\.1\.0/); assert.doesNotMatch(source, /ktx_daemon-0\.1\.0/); }); }); describe('verification snippets', () => { it('pins the smoke project to the public package artifact', () => { const layout = packageArtifactLayout('/repo/ktx', PUBLIC_NPM_PACKAGE_VERSION); const packageJson = npmSmokePackageJson(layout); assert.deepEqual(packageJson.dependencies, { '@kaelio/ktx': `file:${layout.cliTarball}`, }); assert.equal(packageJson.devDependencies, undefined); assert.equal( npmSmokePnpmWorkspaceYaml(), ['packages:', ' - "."', 'allowBuilds:', ' better-sqlite3: true', ''].join('\n'), ); }); it('exposes manifest verification as a package artifact command', async () => { const source = await readFile(new URL('./package-artifacts.mjs', import.meta.url), 'utf8'); const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8')); assert.match(source, /if \(command === 'verify-manifest'\)/); assert.match(source, /await verifyArtifactManifest\(layout\)/); assert.equal(packageJson.scripts['artifacts:verify-demo'], 'node scripts/package-artifacts.mjs verify-demo'); assert.equal(packageJson.scripts['artifacts:verify-manifest'], 'node scripts/package-artifacts.mjs verify-manifest'); }); it('asserts the public npm entry point that clean installs must expose', () => { const verifySource = npmVerifySource(); assert.match(verifySource, /const cli = await import\('@kaelio\/ktx'\);/); assert.match(verifySource, /getKtxCliPackageInfo/); assert.match(verifySource, /runKtxCli/); assert.doesNotMatch(verifySource, /@ktx\/context/); assert.doesNotMatch(verifySource, /@ktx\/llm/); assert.doesNotMatch(verifySource, /@ktx\/connector-/); }); it('runs installed CLI commands through the public package runtime', () => { const source = npmRuntimeSmokeSource(); assert.match(source, /function pnpmCommand\(args\)/); assert.match(source, /process\.platform === 'win32'/); assert.match(source, /command: 'cmd\.exe'/); assert.match(source, /args: \['\/d', '\/s', '\/c', 'pnpm', \.\.\.args\]/); assert.match(source, /import \{ setTimeout as delay \} from 'node:timers\/promises';/); assert.match(source, /async function rmWithRetry\(path\)/); assert.match(source, /await delay\(500\)/); assert.match(source, /await rmWithRetry\(root\)/); assert.match(source, /ktx public package version/); assert.match(source, /installedPackageVersionPattern/); assert.doesNotMatch(source, /@kaelio\\\/ktx 0\\\.1\\\.0/); assert.match(source, /pnpmCommand\(\[\s*'exec',\s*'ktx',\s*'sl',\s*'query'/); assert.doesNotMatch(source, /@ktx\/context/); assert.doesNotMatch(source, /@modelcontextprotocol/); assert.doesNotMatch(source, /startSemanticDaemon/); assert.doesNotMatch(source, /run\('pnpm',/); assert.match(source, /pnpmCommand\(\[\s*'exec',\s*'ktx',\s*'setup'/); assert.match(source, /wiki', 'global', 'revenue\.md'/); assert.match(source, /pnpmCommand\(\[\s*'exec',\s*'ktx',\s*'wiki',\s*'revenue'/); assert.match(source, /semantic-layer', 'warehouse', 'orders\.yaml'/); assert.match(source, /pnpmCommand\(\[\s*'exec',\s*'ktx',\s*'sl',\s*'orders'/); assert.match(source, /orders\.order_count/); assert.match(source, /node:sqlite/); assert.match(source, /driver: sqlite/); assert.match(source, /path: warehouse\.db/); assert.doesNotMatch(source, /live-database/); assert.match(source, /'--execute'/); assert.match(source, /"mode": "compile_only"/); assert.match(source, /"mode": "executed"/); assert.match(source, /ktx sl query sqlite execute/); assert.match(source, /import \{ DatabaseSync \} from 'node:sqlite'/); assert.doesNotMatch(source, /run\('python'/); assert.match(source, /KTX_RUNTIME_ROOT/); assert.match(source, /managed-runtime/); assert.match(source, /ktx admin runtime status missing/); assert.match(source, /runtimeStatusBefore\.kind, 'missing'/); assert.ok(source.includes(String.raw`Installing KTX Python runtime \(core\) with uv`)); assert.match(source, /KTX Python runtime ready:/); assert.match(source, /ktx admin runtime status ready/); assert.match(source, /runtimeStatusAfter\.kind, 'ready'/); assert.match(source, /runtimeStatusAfter\.manifest\.features/); assert.match(source, /ktx admin runtime status/); assert.match(source, /status: ready/); assert.match(source, /ktx admin runtime start/); assert.match(source, /ktx admin runtime start reuse/); assert.match(source, /Using existing KTX daemon/); assert.match(source, /ktx admin runtime stop/); assert.doesNotMatch(source, /ktx admin runtime prune/); assert.doesNotMatch(source, /staleRuntimeDir/); assert.match(source, /pnpmCommand\(\['exec', 'ktx', 'ingest', 'warehouse'/); assert.doesNotMatch(source, /'--fast'/); assert.doesNotMatch(source, /'--deep'/); assert.doesNotMatch(source, /'--enrich'/); assert.match(source, /ktx ingest enrichment guard verified/); assert.match(source, /enrichment is not configured/); assert.match(source, /requireStderr\('ktx ingest enrichment guard'/); assert.match(source, /enrichment:/); assert.match(source, /mode: deterministic/); assert.doesNotMatch(source, /run\('pnpm', \['exec', 'ktx', 'ingest', 'run'/); assert.match(source, /access\(join\(projectDir, '\.ktx', 'db\.sqlite'\)\)/); assert.match(source, /SQLite wiki index/); assert.doesNotMatch(source, /ktx ingest run requires llm\\.provider\\.backend: anthropic, vertex, or gateway/); assert.match(source, /ktx ingest state verified/); }); describe('npmCliSmokeSource', () => { it('exercises supported public package CLI commands', () => { const source = npmCliSmokeSource(); assert.match(source, /function pnpmCommand\(args\)/); assert.match(source, /process\.platform === 'win32'/); assert.doesNotMatch(source, /run\('pnpm',/); assert.match(source, /pnpmCommand\(\['exec', 'ktx', '--help'\]\)/); assert.match(source, /pnpmCommand\(\['exec', 'ktx', 'setup', '--help'\]\)/); assert.match(source, /Usage: ktx setup/); assert.doesNotMatch(source, new RegExp(["'demo'", "'--mode'", "'deterministic'"].join(', '))); assert.match(source, /'status', '--verbose', '--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'/); }); }); });