/*
Unit tests for resolve mirrors covering inferMirrorProtocol; isGitAccessible; resolveMirrorPath. It also guards the documented public contract and regression-sensitive edge cases.
- 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 path resolution helpers.
*/
import { test, expect, describe } from "vitest";
import {
resolveMirrors,
resolveMirrorPath,
inferMirrorProtocol,
isGitAccessible,
} from "./registry-io.ts";
import type { SystemConfig } from "@warpgogol/werkstatt-engine/schemas";
import { join } from "node:path";
function makeEntry(
mirrors: Array<{ path: string; storageType: "non-bare" | "bare" | "bundle" }>,
): SystemConfig {
return {
schemaVersion: "system-config/v1",
id: "test-bundle",
cosmicStar: "Vega",
mirrors,
pinnedPlatform: "4.5.0",
status: "active",
registeredAt: "2026-01-01T00:00:00Z",
notes: "",
};
}
const WORKSPACE = "/fake/workspace";
describe("inferMirrorProtocol", () => {
test("detects ssh protocol from git@ prefix", () => {
expect(inferMirrorProtocol("git@github.com:foo/bar.git")).toBe("ssh");
});
test("detects ssh protocol from ssh:// prefix", () => {
expect(inferMirrorProtocol("ssh://github.com/foo/bar.git")).toBe("ssh");
});
test("detects https protocol", () => {
expect(inferMirrorProtocol("https://github.com/foo/bar.git")).toBe("https");
});
test("detects http protocol as https", () => {
expect(inferMirrorProtocol("http://github.com/foo/bar.git")).toBe("https");
});
test("detects ftp protocol", () => {
expect(inferMirrorProtocol("ftp://server/path")).toBe("ftp");
});
test("detects sftp protocol as ftp", () => {
expect(inferMirrorProtocol("sftp://server/path")).toBe("ftp");
});
test("detects s3 protocol", () => {
expect(inferMirrorProtocol("s3://bucket/path")).toBe("s3");
});
test("detects rsync protocol", () => {
expect(inferMirrorProtocol("rsync://server/path")).toBe("rsync");
});
test("defaults to file protocol for local paths", () => {
expect(inferMirrorProtocol("../systems-git/test-bundle")).toBe("file");
expect(inferMirrorProtocol("./systems/test-bundle")).toBe("file");
expect(inferMirrorProtocol("/absolute/path")).toBe("file");
});
});
describe("isGitAccessible", () => {
test("returns true for git protocols (file, ssh, https)", () => {
expect(isGitAccessible("../systems-git/test-bundle")).toBe(true);
expect(isGitAccessible("git@github.com:foo/bar.git")).toBe(true);
expect(isGitAccessible("https://github.com/foo/bar.git")).toBe(true);
});
test("returns false for non-git protocols (ftp, s3, rsync)", () => {
expect(isGitAccessible("ftp://server/path")).toBe(false);
expect(isGitAccessible("s3://bucket/path")).toBe(false);
expect(isGitAccessible("rsync://server/path")).toBe(false);
});
});
describe("resolveMirrorPath", () => {
test("resolves relative paths against workspaceRoot", () => {
expect(resolveMirrorPath(WORKSPACE, "../systems-git/test-bundle")).toBe(
join(WORKSPACE, "..", "systems-git", "test-bundle"),
);
expect(resolveMirrorPath(WORKSPACE, "./systems/test-bundle")).toBe(
join(WORKSPACE, "systems", "test-bundle"),
);
});
test("resolves absolute paths as-is", () => {
expect(resolveMirrorPath(WORKSPACE, "/absolute/path")).toBe("/absolute/path");
});
test("strips file:// prefix and resolves relative", () => {
expect(resolveMirrorPath(WORKSPACE, "file://../systems-git/test-bundle")).toBe(
join(WORKSPACE, "..", "systems-git", "test-bundle"),
);
});
test("returns remote URLs unchanged", () => {
expect(resolveMirrorPath(WORKSPACE, "git@github.com:foo/bar.git")).toBe(
"git@github.com:foo/bar.git",
);
expect(resolveMirrorPath(WORKSPACE, "https://github.com/foo/bar.git")).toBe(
"https://github.com/foo/bar.git",
);
});
});
describe("resolveMirrors", () => {
test("single non-bare mirror — cache only, no git or backup mirrors", () => {
const entry = makeEntry([{ path: "./systems/test-bundle", storageType: "non-bare" }]);
const result = resolveMirrors(WORKSPACE, entry);
expect(result.cachePath).toBe(join(WORKSPACE, "systems", "test-bundle"));
expect(result.gitMirrors).toHaveLength(0);
expect(result.backupMirrors).toHaveLength(0);
});
test("non-bare + bare + external — cache, two git mirrors, no backup", () => {
const entry = makeEntry([
{ path: "./systems/test-bundle", storageType: "non-bare" },
{ path: "../systems-git/test-bundle", storageType: "bare" },
{ path: "git@github.com:foo/test.git", storageType: "bare" },
]);
const result = resolveMirrors(WORKSPACE, entry);
expect(result.cachePath).toBe(join(WORKSPACE, "systems", "test-bundle"));
expect(result.gitMirrors).toHaveLength(2);
expect(result.gitMirrors[0].path).toBe("../systems-git/test-bundle");
expect(result.gitMirrors[1].path).toBe("git@github.com:foo/test.git");
expect(result.backupMirrors).toHaveLength(0);
});
test("non-bare + bare + external + bundle — cache, two git, one backup", () => {
const entry = makeEntry([
{ path: "./systems/test-bundle", storageType: "non-bare" },
{ path: "../systems-git/test-bundle", storageType: "bare" },
{ path: "git@github.com:foo/test.git", storageType: "bare" },
{ path: "s3://bucket/backups/test-bundle.bundle", storageType: "bundle" },
]);
const result = resolveMirrors(WORKSPACE, entry);
expect(result.cachePath).toBe(join(WORKSPACE, "systems", "test-bundle"));
expect(result.gitMirrors).toHaveLength(2);
expect(result.backupMirrors).toHaveLength(1);
expect(result.backupMirrors[0].path).toBe("s3://bucket/backups/test-bundle.bundle");
});
test("non-bare + bundle only — cache, no git, one backup", () => {
const entry = makeEntry([
{ path: "./systems/test-bundle", storageType: "non-bare" },
{ path: "ftp://server/backups/test-bundle.bundle", storageType: "bundle" },
]);
const result = resolveMirrors(WORKSPACE, entry);
expect(result.gitMirrors).toHaveLength(0);
expect(result.backupMirrors).toHaveLength(1);
});
test("non-bare + rsync — rsync is not git-accessible, goes to backup", () => {
const entry = makeEntry([
{ path: "./systems/test-bundle", storageType: "non-bare" },
{ path: "rsync://server/path", storageType: "bare" },
]);
const result = resolveMirrors(WORKSPACE, entry);
expect(result.gitMirrors).toHaveLength(0);
expect(result.backupMirrors).toHaveLength(0); // rsync+bare is not bundle, not git → neither
});
test("cache path resolved from mirrors[0] with file:// prefix", () => {
const entry = makeEntry([
{ path: "file://../systems-cache/test-bundle", storageType: "non-bare" },
]);
const result = resolveMirrors(WORKSPACE, entry);
expect(result.cachePath).toBe(join(WORKSPACE, "..", "systems-cache", "test-bundle"));
});
});