/*
RFC-0574: Unit tests for mirror topology validation rules in sternsystem.validate.
- Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only.
- RFC-0574: initial unit tests for mirror validation rules.
*/
import { test, expect, beforeEach, afterEach } from "vitest";
import { mkdtemp, rm, mkdir } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { runSternsystemValidate } from "./sternsystem-validate.ts";
import { makeInput, makeContext, writeSystemConfig, BASE_SETUP } from "./test-helpers.ts";
let testRoot: string;
let workspaceRoot: string;
beforeEach(async () => {
testRoot = await mkdtemp(join(tmpdir(), "mirror-validate-test-"));
workspaceRoot = join(testRoot, "workspace");
await mkdir(workspaceRoot, { recursive: true });
await BASE_SETUP(workspaceRoot);
});
afterEach(async () => {
await rm(testRoot, { recursive: true, force: true });
});
test("validate passes with single non-bare mirror (cache only)", async () => {
await writeSystemConfig(workspaceRoot, [
{ path: "./systems/test-bundle", storageType: "non-bare" },
]);
await mkdir(join(workspaceRoot, "systems", "test-bundle"), { recursive: true });
const result = await runSternsystemValidate(makeInput({}), makeContext(workspaceRoot));
expect(result.data!.violations).toHaveLength(0);
});
test("validate passes with 3 mirrors (cache + bare + external)", async () => {
await writeSystemConfig(workspaceRoot, [
{ path: "./systems/test-bundle", storageType: "non-bare" },
{ path: "../systems-git/test-bundle", storageType: "bare" },
{ path: "git@github.com:foo/test.git", storageType: "bare" },
]);
await mkdir(join(workspaceRoot, "systems", "test-bundle"), { recursive: true });
const result = await runSternsystemValidate(makeInput({}), makeContext(workspaceRoot));
const mirrorViolations = result.data!.violations.filter((v) => v.rule.startsWith("mirror"));
expect(mirrorViolations).toHaveLength(0);
});
test("validate detects embedded credentials in external mirror URL", async () => {
await writeSystemConfig(workspaceRoot, [
{ path: "./systems/test-bundle", storageType: "non-bare" },
{ path: "../systems-git/test-bundle", storageType: "bare" },
{ path: "https://user:pass@github.com/foo/test.git", storageType: "bare" },
]);
await mkdir(join(workspaceRoot, "systems", "test-bundle"), { recursive: true });
const result = await runSternsystemValidate(makeInput({}), makeContext(workspaceRoot));
const credViolations = result.data!.violations.filter((v) => v.rule === "mirror-credentials");
expect(credViolations).toHaveLength(1);
expect(credViolations[0].message).toContain("credentials");
});
test("validate does not flag credentials when no external mirror exists", async () => {
await writeSystemConfig(workspaceRoot, [
{ path: "./systems/test-bundle", storageType: "non-bare" },
{ path: "../systems-git/test-bundle", storageType: "bare" },
]);
await mkdir(join(workspaceRoot, "systems", "test-bundle"), { recursive: true });
const result = await runSternsystemValidate(makeInput({}), makeContext(workspaceRoot));
const credViolations = result.data!.violations.filter((v) => v.rule === "mirror-credentials");
expect(credViolations).toHaveLength(0);
});
test("validate resolves cache dir from mirrors[0].path", async () => {
await writeSystemConfig(workspaceRoot, [
{ path: "../systems-cache/test-bundle", storageType: "non-bare" },
]);
// Cache dir already created by writeSystemConfig
const result = await runSternsystemValidate(makeInput({}), makeContext(workspaceRoot));
const _cacheViolations = result.data!.violations.filter(
(v) => v.rule === "bundle-contract" || v.rule === "cache-missing",
);
expect(result.exitCode).toBe(0);
});
test("validate detects mirrors[0] with wrong storageType (bare instead of non-bare)", async () => {
await writeSystemConfig(workspaceRoot, [{ path: "./systems/test-bundle", storageType: "bare" }]);
await mkdir(join(workspaceRoot, "systems", "test-bundle"), { recursive: true });
const result = await runSternsystemValidate(makeInput({}), makeContext(workspaceRoot));
const cacheViolations = result.data!.violations.filter(
(v) => v.rule === "cache-must-be-non-bare",
);
expect(cacheViolations).toHaveLength(1);
expect(cacheViolations[0].message).toContain("non-bare");
});
test("validate detects bundle storageType with git-accessible protocol", async () => {
await writeSystemConfig(workspaceRoot, [
{ path: "./systems/test-bundle", storageType: "non-bare" },
{ path: "../systems-git/test-bundle", storageType: "bare" },
{ path: "git@github.com:foo/test.git", storageType: "bundle" },
]);
await mkdir(join(workspaceRoot, "systems", "test-bundle"), { recursive: true });
const result = await runSternsystemValidate(makeInput({}), makeContext(workspaceRoot));
const bundleViolations = result.data!.violations.filter(
(v) => v.rule === "bundle-no-git-protocol",
);
expect(bundleViolations).toHaveLength(1);
expect(bundleViolations[0].message).toContain("bundle");
expect(bundleViolations[0].message).toContain("git");
});