import { test, expect } from "vitest"; import { chmodSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { dirname, join } from "node:path"; import { execFileSync } from "node:child_process"; import { createHash } from "node:crypto"; import { WorkspaceRuntimeConfigLeaseFactory } from "../src/workspaces/runtime-config-lease.js"; import { parseWorkspaceYaml, serializeWorkspaceYaml } from "../src/workspaces/schema.js"; const workspace = "abc"; const descriptor = `workspace: schema_version: 3 id: ${workspace} name: Lease language: en dwh: engine: postgres database: analytics schema: mart supported_transports: [postgres_direct] semantic_index: vector_store: engine: qdrant collection: abc dimensions: 1024 distance: cosine embedding: provider: ollama_internal model: qwen3-embedding:0.6b dimensions: 1024 llm_policy: allowed: [zai/glm-5.2] `; function fixture() { const root = mkdtempSync(join(tmpdir(), "runtime-config-lease-")); const canonicalDescriptor = serializeWorkspaceYaml(parseWorkspaceYaml(descriptor)); const snapshots = join(root, "snapshots"); const repo = join(root, "repo"); mkdirSync(join(repo, "workspaces"), { recursive: true }); execFileSync("git", ["init", "--initial-branch=main"], { cwd: repo }); execFileSync("git", ["config", "user.name", "Fixture"], { cwd: repo }); execFileSync("git", ["config", "user.email", "fixture@example.invalid"], { cwd: repo }); writeFileSync(join(repo, "workspaces", `${workspace}.yaml`), canonicalDescriptor); execFileSync("git", ["add", "."], { cwd: repo }); execFileSync("git", ["commit", "-m", "fixture"], { cwd: repo }); const actualCommit = execFileSync("git", ["rev-parse", "HEAD"], { cwd: repo, encoding: "utf8" }).trim(); const blob = execFileSync("git", ["rev-parse", `HEAD:workspaces/${workspace}.yaml`], { cwd: repo, encoding: "utf8" }).trim(); const snapshotsDir = join(snapshots, actualCommit); const snapshotPath = join(snapshotsDir, `${workspace}.yaml`); const dataRoot = join(root, "data"); const harness = join(root, "harness"); mkdirSync(snapshotsDir, { recursive: true, mode: 0o700 }); chmodSync(snapshots, 0o700); const publicFiles = { [`${workspace}.yaml`]: canonicalDescriptor, [`${workspace}.env.example`]: "# fixture\n", [`${workspace}.md`]: "# Lease\n", }; for (const [name, contents] of Object.entries(publicFiles)) writeFileSync(join(snapshotsDir, name), contents, { mode: 0o400 }); writeFileSync(join(snapshotsDir, "snapshot.json"), JSON.stringify({ head: actualCommit, revisions: [{ id: workspace, commit: actualCommit, blob, snapshotPath }], files: Object.fromEntries(Object.entries(publicFiles).map(([name, contents]) => [name, createHash("sha256").update(contents).digest("hex")])), }), { mode: 0o400 }); mkdirSync(harness); const secret = join(root, "password"); writeFileSync(secret, "secret", { mode: 0o600 }); const configPath = join(harness, "config.yaml"); writeFileSync(configPath, "profile: workstation\n"); const factory = new WorkspaceRuntimeConfigLeaseFactory({ dataRoot, runtimeSnapshotRoot: snapshots, harnessDir: harness, configPath, env: { THT_WS_ABC_DWH_TRANSPORT: "postgres_direct", THT_WS_ABC_DWH_HOST: "dwh", THT_WS_ABC_DWH_PORT: "5432", THT_WS_ABC_DWH_USER: "reader", THT_WS_ABC_DWH_PASSWORD_FILE: secret, }, secretRoots: [root], semanticRuntime: { internalQdrantUrl: "http://qdrant:6333", internalEmbeddingUrl: "http://embedding:11434", internalEmbeddingModel: "qwen3-embedding:0.6b", internalEmbeddingDimensions: 1024, }, }); return { root, snapshotPath, factory, canonicalDescriptor }; } test("session and maintenance share deterministic bytes and path", () => { const f = fixture(); try { const session = f.factory.acquireSession(f.snapshotPath); const maintenance = f.factory.acquireMaintenance({ snapshotPath: f.snapshotPath }); expect(session.path).toBe(maintenance.path); expect(readFileSync(session.path, "utf8")).toBe(readFileSync(maintenance.path, "utf8")); expect(lstatSync(session.path).mode & 0o777).toBe(0o400); expect(existsSync(maintenance.manifestPath)).toBe(true); const manifest = JSON.parse(readFileSync(maintenance.manifestPath, "utf8")); expect(manifest).toMatchObject({ version: 1, descriptor_dev: expect.any(String), descriptor_ino: expect.any(String) }); expect(existsSync(join(dirname(dirname(maintenance.path)), "runtime-config.lock"))).toBe(false); session.release(); maintenance.release(); expect(existsSync(session.path)).toBe(true); } finally { rmSync(f.root, { recursive: true, force: true }); } }); test("same revision changed bytes are refused", () => { const f = fixture(); try { const first = f.factory.acquireSession(f.snapshotPath); chmodSync(first.path, 0o600); writeFileSync(first.path, "changed", { mode: 0o600 }); chmodSync(first.path, 0o400); expect(() => f.factory.acquireSession(f.snapshotPath)).toThrow(/changed|mismatch|trusted/i); first.release(); } finally { rmSync(f.root, { recursive: true, force: true }); } }); test("snapshot descriptor must equal the Git canonical descriptor", () => { const f = fixture(); try { const mutated = f.canonicalDescriptor.replace("database: analytics", "database: evil").replace("name: Lease", "name: Lease analytics"); chmodSync(f.snapshotPath, 0o600); writeFileSync(f.snapshotPath, mutated, { mode: 0o600 }); chmodSync(f.snapshotPath, 0o400); const manifestPath = join(dirname(f.snapshotPath), "snapshot.json"); const manifest = JSON.parse(readFileSync(manifestPath, "utf8")); manifest.files[`${workspace}.yaml`] = createHash("sha256").update(mutated).digest("hex"); chmodSync(manifestPath, 0o600); writeFileSync(manifestPath, JSON.stringify(manifest), { mode: 0o600 }); chmodSync(manifestPath, 0o400); expect(() => f.factory.acquireSession(f.snapshotPath)).toThrow(/Git descriptor|integrity|identity/i); } finally { rmSync(f.root, { recursive: true, force: true }); } }); test("same-byte replacement of the registry descriptor is refused", () => { const f = fixture(); try { const first = f.factory.acquireSession(f.snapshotPath); const replacement = `${f.snapshotPath}.replacement`; writeFileSync(replacement, readFileSync(f.snapshotPath), { mode: 0o400 }); chmodSync(f.snapshotPath, 0o600); rmSync(f.snapshotPath); writeFileSync(f.snapshotPath, readFileSync(replacement), { mode: 0o400 }); expect(() => f.factory.acquireSession(f.snapshotPath)).toThrow(/identity|changed|mismatch|trusted/i); first.release(); } finally { rmSync(f.root, { recursive: true, force: true }); } });