import { test } from "node:test"; import assert from "node:assert/strict"; import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join, dirname, resolve } from "node:path"; import { spawnSync } from "node:child_process"; import { validateProvider, validateAccount, validateSettingsProfile, validateSeatSelection, validateHarnessManifest } from "../src/records.mjs"; const pkgRoot = resolve(import.meta.dirname, ".."); const cli = join(pkgRoot, "src", "cli", "main.mjs"); const validFixture = join(pkgRoot, "tests", "fixtures", "valid"); function makeRoot() { const root = mkdtempSync(join(tmpdir(), "mosaic-registry-test-")); return root; } function writeTree(root, files) { for (const [name, content] of Object.entries(files)) { const path = join(root, name); mkdirSync(dirname(path), { recursive: true }); writeFileSync(path, typeof content === "string" ? content : JSON.stringify(content)); } } function runCli(root, command = "validate") { return spawnSync(process.execPath, [cli, command, "--registry-root", root], { encoding: "utf8", timeout: 15000 }); } const validTree = () => ({ "auth/providers/openai-codex.json": { providerVersion: "1", id: "openai-codex", kind: "native", harnesses: { pi: { providerId: "openai-codex" } }, credentialTypes: ["oauth", "api_key"] }, "auth/accounts/openai-codex/homelab/account.json": { accountVersion: "1", id: "homelab", name: "Homelab", provider: "openai-codex", type: "oauth", createdAt: "2026-09-10T00:00:00Z" }, "auth/settings/research.json": { settingsVersion: "1", id: "research", allowedAccounts: ["openai-codex/homelab"], providers: ["openai-codex"], defaultAccounts: { "openai-codex": "openai-codex/homelab" } }, "harnesses/pi.json": { harnessVersion: "1", id: "pi", executable: "pi", adapter: "pi", compatibleRange: ">=0.85.1 <0.86.0", executionMode: "container", materializers: ["auth-json"] }, }); test("valid fixture tree validates and lists without secrets", () => { const r = runCli(validFixture); assert.equal(r.status, 0, r.stderr); assert.match(r.stdout, /^valid\n$/); const l = runCli(validFixture, "list"); assert.equal(l.status, 0, l.stderr); const listed = JSON.parse(l.stdout); assert.equal(listed.providers.length, 2); assert.equal(listed.accounts.length, 1); assert.equal(listed.profiles.length, 1); assert.ok(!JSON.stringify(listed).includes("credential")); assert.ok(!JSON.stringify(listed).includes("token")); }); test("unknown-field refuses", () => { const errors = validateProvider({ providerVersion: "1", id: "p", kind: "native", harnesses: {}, credentialTypes: ["none"], extra: 1 }); assert.equal(errors[0].code, "unknown-field"); }); test("invalid-id refuses uppercase and traversal shapes", () => { assert.ok(validateProvider({ providerVersion: "1", id: "BadID", kind: "native", harnesses: {}, credentialTypes: ["none"] }).some((e) => e.code === "invalid-id")); assert.ok(validateAccount({ accountVersion: "1", id: "../escape", name: "x", provider: "p", type: "oauth", createdAt: "2026-09-10T00:00:00Z" }).some((e) => e.code === "invalid-id")); }); test("plain-http baseUrl requires allowInsecureTransport", () => { const base = { providerVersion: "1", id: "o", kind: "custom-endpoint", harnesses: { pi: { api: "openai-completions", baseUrl: "http://h:11434/v1", models: ["m"] } }, credentialTypes: ["none"] }; assert.ok(validateProvider(base).some((e) => e.code === "insecure-transport-not-allowed")); assert.equal(validateProvider({ ...base, allowInsecureTransport: true }).length, 0); const https = JSON.parse(JSON.stringify(base)); https.harnesses.pi.baseUrl = "https://h/v1"; assert.equal(validateProvider(https).length, 0); }); test("native provider rejects allowInsecureTransport", () => { assert.ok(validateProvider({ providerVersion: "1", id: "p", kind: "native", harnesses: {}, credentialTypes: ["none"], allowInsecureTransport: true }).some((e) => e.code === "insecure-transport-unsupported-for-native")); }); test("unsupported credential type and kind refuse", () => { assert.ok(validateAccount({ accountVersion: "1", id: "a", name: "x", provider: "p", type: "basic", createdAt: "2026-09-10T00:00:00Z" }).some((e) => e.code === "unsupported-credential-type")); assert.ok(validateProvider({ providerVersion: "1", id: "p", kind: "weird", harnesses: {}, credentialTypes: ["none"] }).some((e) => e.code === "unsupported-kind")); }); test("account provider-path mismatch refuses", () => { assert.ok(validateAccount({ accountVersion: "1", id: "a", name: "x", provider: "other", type: "oauth", createdAt: "2026-09-10T00:00:00Z" }, "right").some((e) => e.code === "provider-path-mismatch")); }); test("profile account refs must be provider/account shaped", () => { const errors = validateSettingsProfile({ settingsVersion: "1", id: "s", allowedAccounts: ["justone"], providers: [], defaultAccounts: {} }); assert.ok(errors.some((e) => e.code === "invalid-account-ref-list")); }); test("seat selection accepts fork pin field, validates account refs", () => { assert.equal(validateSeatSelection({ selectionVersion: "1", profile: "research", accounts: { "openai-codex": "openai-codex/homelab" }, updatedAt: "2026-09-10T00:00:00Z", pinnedFromSession: "abc123" }).length, 0); assert.ok(validateSeatSelection({ selectionVersion: "1", profile: "research", accounts: { "openai-codex": "nope" } }).some((e) => e.code === "invalid-account-ref")); }); test("harness manifest id must equal executable (gate 1)", () => { assert.ok(validateHarnessManifest({ harnessVersion: "1", id: "pi", executable: "claude", adapter: "x", compatibleRange: ">=1", executionMode: "container", materializers: ["m"] }).some((e) => e.code === "id-executable-mismatch")); }); test("CLI validate: duplicate provider id across files refuses", () => { const root = makeRoot(); try { const tree = validTree(); tree["auth/providers/dup.json"] = { ...tree["auth/providers/openai-codex.json"] }; writeTree(root, tree); const r = runCli(root); assert.equal(r.status, 1); assert.match(r.stderr, /duplicate-id|id-path-mismatch/); } finally { rmSync(root, { recursive: true, force: true }); } }); test("CLI validate: missing referenced provider/account refuse", () => { const root = makeRoot(); try { const tree = validTree(); delete tree["auth/providers/openai-codex.json"]; writeTree(root, tree); const r = runCli(root); assert.equal(r.status, 1); assert.match(r.stderr, /missing-provider/); } finally { rmSync(root, { recursive: true, force: true }); } }); test("CLI validate: broken JSON refuses without secret echo", () => { const root = makeRoot(); try { const tree = validTree(); tree["auth/providers/openai-codex.json"] = "{ not json"; writeTree(root, tree); const r = runCli(root); assert.equal(r.status, 1); assert.match(r.stderr, /invalid-json/); } finally { rmSync(root, { recursive: true, force: true }); } }); test("CLI usage errors exit 2", () => { assert.equal(runCli(validFixture, "bogus").status, 2); assert.equal(spawnSync(process.execPath, [cli, "validate"], { encoding: "utf8" }).status, 2); }); test("credential.json sibling presence does not break validation and is never read", () => { const root = makeRoot(); try { const tree = validTree(); tree["auth/accounts/openai-codex/homelab/credential.json"] = { secretMarker: "FIXTURE-NOT-A-SECRET" }; writeTree(root, tree); const r = runCli(root); assert.equal(r.status, 0, r.stderr); const l = runCli(root, "list"); assert.ok(!l.stdout.includes("FIXTURE-NOT-A-SECRET")); } finally { rmSync(root, { recursive: true, force: true }); } });