/* RFC-0574: Integration tests for star-topology sync with real git operations. Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only. RFC-0574: initial integration tests for sternsystem.sync star-topology. */ import { test, expect, beforeEach, afterEach } from "vitest"; import { execSync } from "node:child_process"; import { mkdtemp, rm, mkdir, writeFile } from "node:fs/promises"; import { existsSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { stringify as stringifyYaml } from "yaml"; import { runSternsystemSync } from "./sternsystem-sync.ts"; import type { KernelCommandInput, KernelRuntimeContext } from "@warpgogol/werkstatt-engine/kernel"; import { createDefaultIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io"; function git(cwd: string, args: string): string { return execSync(`git ${args}`, { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], }).trim(); } let testRoot: string; let workspaceRoot: string; let cacheDir: string; let bareDir: string; let externalDir: string; function makeInput(flags: Record): KernelCommandInput { return { flags: flags as Record, argv: [], }; } function makeContext(root: string): KernelRuntimeContext { return { io: createDefaultIO().io, workspaceRoot: root, logger: { section: () => {}, info: () => {}, warn: () => {}, error: () => {}, success: () => {}, }, dryRun: false, } as unknown as KernelRuntimeContext; } interface MirrorSpec { path: string; storageType: "non-bare" | "bare" | "bundle"; } async function setupSystemConfig(mirrors: MirrorSpec[]): Promise { const config = { schemaVersion: "system-config/v1", id: "test-bundle", cosmicStar: "Vega", mirrors, pinnedPlatform: "4.5.0", status: "registered", registeredAt: "2026-01-01T00:00:00Z", notes: "", }; await writeFile(join(cacheDir, "system-config.yaml"), stringifyYaml(config) + "\n", "utf8"); } beforeEach(async () => { testRoot = await mkdtemp(join(tmpdir(), "sync-integration-")); workspaceRoot = join(testRoot, "workspace"); cacheDir = join(testRoot, "systems-cache", "test-bundle"); bareDir = join(testRoot, "bare.git"); externalDir = join(testRoot, "external.git"); await mkdir(workspaceRoot, { recursive: true }); // Create bare repos git(testRoot, `init --bare -b main "${bareDir}"`); git(testRoot, `init --bare -b main "${externalDir}"`); // Create cache clone with initial content git(testRoot, `clone "${bareDir}" "${cacheDir}"`); git(cacheDir, 'config user.email "test@example.com"'); git(cacheDir, 'config user.name "Test"'); await mkdir(join(cacheDir, "src/content"), { recursive: true }); await writeFile(join(cacheDir, "src/content/system.md"), "# System\n"); await mkdir(join(cacheDir, "bordbuch"), { recursive: true }); await writeFile(join(cacheDir, "bordbuch/events.ndjson"), ""); git(cacheDir, "add -A"); git(cacheDir, 'commit -m "initial"'); git(cacheDir, "push origin main"); // Minimal workspace setup for validate await mkdir(join(workspaceRoot, "docs", "rfcs"), { recursive: true }); await writeFile(join(workspaceRoot, "docs", "rfcs", "RFC-0001-test.md"), "", "utf8"); await writeFile( join(workspaceRoot, "package.json"), JSON.stringify({ version: "4.5.0" }), "utf8", ); await writeFile( join(workspaceRoot, "uni.registry.yaml"), JSON.stringify({ entries: [{ id: "test", semanticId: "test", version: "1.0.0", intent: [] }] }), "utf8", ); await mkdir(join(workspaceRoot, "packages", "dummy"), { recursive: true }); await writeFile( join(workspaceRoot, "packages", "dummy", "index.ts"), "export const x = 1;\n", "utf8", ); }); afterEach(async () => { await rm(testRoot, { recursive: true, force: true }); }); test("sync pushes from cache clone to bare mirror", { retry: 2 }, async () => { await setupSystemConfig([ { path: cacheDir, storageType: "non-bare" }, { path: bareDir, storageType: "bare" }, ]); // Make a new commit in cache await writeFile(join(cacheDir, "src/content/system.md"), "# Updated\n"); git(cacheDir, "add -A"); git(cacheDir, 'commit -m "update"'); const result = await runSternsystemSync( makeInput({ id: "test-bundle", direction: "push" }), makeContext(workspaceRoot), ); expect(result.exitCode).toBe(0); // Verify bare repo received the commit const bareLog = git(bareDir, "log --oneline"); expect(bareLog).toContain("update"); }); test("sync pushes to multiple external mirrors", { retry: 2 }, async () => { await setupSystemConfig([ { path: cacheDir, storageType: "non-bare" }, { path: bareDir, storageType: "bare" }, { path: externalDir, storageType: "bare" }, ]); // Make a new commit in cache await writeFile(join(cacheDir, "src/content/system.md"), "# Multi-sync\n"); git(cacheDir, "add -A"); git(cacheDir, 'commit -m "multi-sync"'); const result = await runSternsystemSync( makeInput({ id: "test-bundle", direction: "push" }), makeContext(workspaceRoot), ); expect(result.exitCode).toBe(0); // Both bare and external should have the commit const bareLog = git(bareDir, "log --oneline"); const externalLog = git(externalDir, "log --oneline"); expect(bareLog).toContain("multi-sync"); expect(externalLog).toContain("multi-sync"); }); test("sync handles per-mirror failure non-fatally", { retry: 2 }, async () => { const nonExistentMirror = join(testRoot, "nonexistent.git"); await setupSystemConfig([ { path: cacheDir, storageType: "non-bare" }, { path: bareDir, storageType: "bare" }, { path: nonExistentMirror, storageType: "bare" }, ]); // Make a new commit in cache await writeFile(join(cacheDir, "src/content/system.md"), "# Failure test\n"); git(cacheDir, "add -A"); git(cacheDir, 'commit -m "failure-test"'); const result = await runSternsystemSync( makeInput({ id: "test-bundle", direction: "push" }), makeContext(workspaceRoot), ); // Should succeed overall (non-fatal mirror failure) expect(result.exitCode).toBe(0); // Bare repo should still receive the commit const bareLog = git(bareDir, "log --oneline"); expect(bareLog).toContain("failure-test"); }); test( "sync with external mirrors creates refs/mirror/${branch} matching bare repo HEAD", { retry: 2 }, async () => { await setupSystemConfig([ { path: cacheDir, storageType: "non-bare" }, { path: bareDir, storageType: "bare" }, { path: externalDir, storageType: "bare" }, ]); // Make a new commit in cache await writeFile(join(cacheDir, "src/content/system.md"), "# Mirror ref test\n"); git(cacheDir, "add -A"); git(cacheDir, 'commit -m "mirror-ref-test"'); const result = await runSternsystemSync( makeInput({ id: "test-bundle", direction: "push" }), makeContext(workspaceRoot), ); expect(result.exitCode).toBe(0); // refs/mirror/main must exist and match bare repo HEAD const bareHead = git(bareDir, "rev-parse main"); const mirrorRef = git(bareDir, "rev-parse refs/mirror/main"); expect(mirrorRef).toBe(bareHead); }, ); test("sync without external mirrors does not create refs/mirror/${branch}", async () => { await setupSystemConfig([ { path: cacheDir, storageType: "non-bare" }, { path: bareDir, storageType: "bare" }, ]); // Make a new commit in cache await writeFile(join(cacheDir, "src/content/system.md"), "# No mirror ref\n"); git(cacheDir, "add -A"); git(cacheDir, 'commit -m "no-mirror-ref"'); const result = await runSternsystemSync( makeInput({ id: "test-bundle", direction: "push" }), makeContext(workspaceRoot), ); expect(result.exitCode).toBe(0); // refs/mirror/main should NOT exist (no external mirrors) expect(() => git(bareDir, "rev-parse refs/mirror/main")).toThrow(); }); test("sync with single mirror (cache only) throws — no bare mirror", async () => { await setupSystemConfig([{ path: cacheDir, storageType: "non-bare" }]); await expect( runSternsystemSync( makeInput({ id: "test-bundle", direction: "push" }), makeContext(workspaceRoot), ), ).rejects.toThrow(/no bare mirror configured/); }); // RFC-0818: external mirror HEAD must match refs/mirror after sync test( "sync pushes bordbuch commit to external mirror — external HEAD matches refs/mirror", { retry: 2 }, async () => { await setupSystemConfig([ { path: cacheDir, storageType: "non-bare" }, { path: bareDir, storageType: "bare" }, { path: externalDir, storageType: "bare" }, ]); await writeFile(join(cacheDir, "src/content/system.md"), "# Bordbuch push test\n"); git(cacheDir, "add -A"); git(cacheDir, 'commit -m "bordbuch-push-test"'); const result = await runSternsystemSync( makeInput({ id: "test-bundle", direction: "push" }), makeContext(workspaceRoot), ); expect(result.exitCode).toBe(0); const bareHead = git(bareDir, "rev-parse main"); const mirrorRef = git(bareDir, "rev-parse refs/mirror/main"); const externalHead = git(externalDir, "rev-parse main"); expect(mirrorRef).toBe(bareHead); expect(externalHead).toBe(bareHead); const externalLog = git(externalDir, "log --oneline"); expect(externalLog).toContain("Bordbuch: mirror-sync test-bundle"); }, ); // RFC-0818: bundle mirror must include bordbuch commit test("sync creates bundle including bordbuch commit", { retry: 2 }, async () => { const bundleDestDir = join(testRoot, "bundle-dest"); await mkdir(bundleDestDir, { recursive: true }); await setupSystemConfig([ { path: cacheDir, storageType: "non-bare" }, { path: bareDir, storageType: "bare" }, { path: join(bundleDestDir, "test.bundle"), storageType: "bundle" }, ]); await writeFile(join(cacheDir, "src/content/system.md"), "# Bundle bordbuch test\n"); git(cacheDir, "add -A"); git(cacheDir, 'commit -m "bundle-bordbuch-test"'); const result = await runSternsystemSync( makeInput({ id: "test-bundle", direction: "push" }), makeContext(workspaceRoot), ); expect(result.exitCode).toBe(0); const bundlePath = join(bundleDestDir, "test.bundle"); expect(existsSync(bundlePath)).toBe(true); const cloneDir = join(testRoot, "bundle-clone"); git(testRoot, `clone "${bundlePath}" "${cloneDir}"`); const cloneLog = git(cloneDir, "log --oneline"); expect(cloneLog).toContain("Bordbuch: mirror-sync test-bundle"); }); // RFC-0818: regression test — residual false positive on external push failure test( "sync with failing external mirror — refs/mirror tracks bare HEAD despite push failure", { retry: 2 }, async () => { const nonExistentMirror = join(testRoot, "nonexistent.git"); await setupSystemConfig([ { path: cacheDir, storageType: "non-bare" }, { path: bareDir, storageType: "bare" }, { path: externalDir, storageType: "bare" }, { path: nonExistentMirror, storageType: "bare" }, ]); await writeFile(join(cacheDir, "src/content/system.md"), "# Push failure test\n"); git(cacheDir, "add -A"); git(cacheDir, 'commit -m "push-failure-test"'); const result = await runSternsystemSync( makeInput({ id: "test-bundle", direction: "push" }), makeContext(workspaceRoot), ); expect(result.exitCode).toBe(0); const bareHead = git(bareDir, "rev-parse main"); const mirrorRef = git(bareDir, "rev-parse refs/mirror/main"); const workingExternalHead = git(externalDir, "rev-parse main"); // refs/mirror matches bare HEAD (includes bordbuch commit) expect(mirrorRef).toBe(bareHead); // The working external mirror also received the bordbuch commit expect(workingExternalHead).toBe(bareHead); // Known residual: the failed external mirror (nonExistentMirror) has N, // but refs/mirror = N+1 — false positive only on push failure, not on every sync. }, ); // ADR-0073: --force-with-lease on external mirror push overwrites diverged mirrors test("sync overwrites diverged external mirror via --force-with-lease", { retry: 2 }, async () => { await setupSystemConfig([ { path: cacheDir, storageType: "non-bare" }, { path: bareDir, storageType: "bare" }, { path: externalDir, storageType: "bare" }, ]); // Initial sync to establish baseline await writeFile(join(cacheDir, "src/content/system.md"), "# Initial\n"); git(cacheDir, "add -A"); git(cacheDir, 'commit -m "initial-sync"'); await runSternsystemSync( makeInput({ id: "test-bundle", direction: "push" }), makeContext(workspaceRoot), ); // Diverge the external mirror — add a commit directly to it const externalClone = join(testRoot, "external-clone"); git(testRoot, `clone "${externalDir}" "${externalClone}"`); git(externalClone, 'config user.email "test@example.com"'); git(externalClone, 'config user.name "Test"'); await writeFile(join(externalClone, "DIVERGE.md"), "diverged\n"); git(externalClone, "add -A"); git(externalClone, 'commit -m "diverge-external"'); git(externalClone, "push origin main"); // Now make a new commit in cache and sync — --force-with-lease should overwrite await writeFile(join(cacheDir, "src/content/system.md"), "# After divergence\n"); git(cacheDir, "add -A"); git(cacheDir, 'commit -m "after-divergence"'); const result = await runSternsystemSync( makeInput({ id: "test-bundle", direction: "push" }), makeContext(workspaceRoot), ); // Sync succeeds (no non-fast-forward error) expect(result.exitCode).toBe(0); // External mirror HEAD now matches bare repo HEAD (diverged commit overwritten) const bareHead = git(bareDir, "rev-parse main"); const externalHead = git(externalDir, "rev-parse main"); expect(externalHead).toBe(bareHead); // The diverged commit is gone from external mirror const externalLog = git(externalDir, "log --oneline"); expect(externalLog).not.toContain("diverge-external"); }); // ADR-0086: dirty operational files are committed and pushed during sync test( "sync commits dirty operational files and pushes them to bare and external mirrors", { retry: 2 }, async () => { await setupSystemConfig([ { path: cacheDir, storageType: "non-bare" }, { path: bareDir, storageType: "bare" }, { path: externalDir, storageType: "bare" }, ]); // Simulate an uncommitted operational journal write (e.g. op-abandoned record) await mkdir(join(cacheDir, "operations"), { recursive: true }); await writeFile(join(cacheDir, "operations", "op-1.jsonl"), '{"op":"abandoned","id":"op-1"}\n'); const result = await runSternsystemSync( makeInput({ id: "test-bundle", direction: "push" }), makeContext(workspaceRoot), ); expect(result.exitCode).toBe(0); // Cache clone is clean after sync — the sweep committed the journal const status = git(cacheDir, "status --porcelain"); expect(status, "cache clone must be clean after sync sweep").toBe(""); // The auto-commit reached the bare repo const bareLog = git(bareDir, "log --oneline"); expect(bareLog).toContain("cache-clone: auto-commit"); // The journal file content reached the external mirror const externalFile = git(externalDir, "show main:operations/op-1.jsonl"); expect(externalFile).toContain('"op":"abandoned"'); }, ); // ADR-0086: a failed sweep must not block propagation of committed state test( "sync continues pushing when the operational sweep fails (index.lock)", { retry: 2 }, async () => { await setupSystemConfig([ { path: cacheDir, storageType: "non-bare" }, { path: bareDir, storageType: "bare" }, ]); // Committed content that must still reach the bare repo await writeFile(join(cacheDir, "src/content/system.md"), "# Committed\n"); git(cacheDir, "add -A"); git(cacheDir, 'commit -m "committed-content"'); // Dirty file + index.lock → git add -A inside the sweep throws await writeFile(join(cacheDir, "operations-dirty.jsonl"), "{}\n"); await writeFile(join(cacheDir, ".git", "index.lock"), ""); try { const result = await runSternsystemSync( makeInput({ id: "test-bundle", direction: "push" }), makeContext(workspaceRoot), ); expect(result.exitCode).toBe(0); const bareLog = git(bareDir, "log --oneline"); expect(bareLog).toContain("committed-content"); } finally { await rm(join(cacheDir, ".git", "index.lock"), { force: true }); } }, );