/*
Unit tests for bordbuch commit helper covering its 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-0750: initial bordbuch-commit-helper unit tests.
*/
import { test, expect, beforeEach, afterEach, vi } from "vitest";
import fs from "node:fs/promises";
import { mkdirSync } from "node:fs";
import path from "node:path";
import os from "node:os";
// Mock the bordbuch-io and registry-io modules
vi.mock("./bordbuch-io.ts", () => ({
appendBordbuchEntry: vi.fn(),
commitAndPushBordbuch: vi.fn(),
}));
vi.mock("../sternsystem/registry-io.ts", () => ({
resolveCacheClonePath: vi.fn(),
}));
import { appendAndCommitBordbuch, appendBatchAndCommitBordbuch } from "./bordbuch-commit-helper.ts";
import { appendBordbuchEntry, commitAndPushBordbuch } from "./bordbuch-io.ts";
import { resolveCacheClonePath } from "../sternsystem/registry-io.ts";
import type { BordbuchEntry } from "@warpgogol/werkstatt-engine/schemas";
const mockAppendBordbuchEntry = vi.mocked(appendBordbuchEntry);
const mockCommitAndPushBordbuch = vi.mocked(commitAndPushBordbuch);
const mockResolveCachePath = vi.mocked(resolveCacheClonePath);
let tmpDir: string;
const systemId = "test-system";
beforeEach(() => {
tmpDir = path.join(
os.tmpdir(),
`bordbuch-helper-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
);
mkdirSync(tmpDir, { recursive: true });
vi.clearAllMocks();
mockResolveCachePath.mockResolvedValue(tmpDir);
});
afterEach(async () => {
await fs.rm(tmpDir, { recursive: true, force: true });
});
test("appendAndCommitBordbuch appends entry and commits with default message", async () => {
const mockEntry = {
id: "evt-001",
kind: "mission-open",
systemId,
summary: "test",
} as BordbuchEntry;
const mockCommitResult = { commitSha: "abc123", pushed: true, error: null };
mockAppendBordbuchEntry.mockResolvedValue(mockEntry);
mockCommitAndPushBordbuch.mockResolvedValue(mockCommitResult);
const result = await appendAndCommitBordbuch(
tmpDir,
systemId,
"mission-open",
"test summary",
"agent",
{ writerRole: "mission" },
);
expect(result.entry).toEqual(mockEntry);
expect(result.commitResult).toEqual(mockCommitResult);
expect(mockAppendBordbuchEntry).toHaveBeenCalledWith(
tmpDir,
systemId,
"mission-open",
"test summary",
"agent",
{ writerRole: "mission" },
);
expect(mockCommitAndPushBordbuch).toHaveBeenCalledWith(
tmpDir,
"Bordbuch: mission-open test-system",
);
});
test("appendAndCommitBordbuch uses custom commit message when provided", async () => {
const mockEntry = {
id: "evt-002",
kind: "mission-close",
systemId,
summary: "test",
} as BordbuchEntry;
const mockCommitResult = { commitSha: "def456", pushed: true, error: null };
mockAppendBordbuchEntry.mockResolvedValue(mockEntry);
mockCommitAndPushBordbuch.mockResolvedValue(mockCommitResult);
const result = await appendAndCommitBordbuch(
tmpDir,
systemId,
"mission-close",
"test close",
"agent",
undefined,
"Custom commit message",
);
expect(result.entry).toEqual(mockEntry);
expect(mockCommitAndPushBordbuch).toHaveBeenCalledWith(tmpDir, "Custom commit message");
});
test("appendAndCommitBordbuch does not throw on commit failure", async () => {
const mockEntry = {
id: "evt-003",
kind: "mission-abort",
systemId,
summary: "test",
} as BordbuchEntry;
const mockCommitResult = { commitSha: null, pushed: false, error: "git error" };
mockAppendBordbuchEntry.mockResolvedValue(mockEntry);
mockCommitAndPushBordbuch.mockResolvedValue(mockCommitResult);
const result = await appendAndCommitBordbuch(
tmpDir,
systemId,
"mission-abort",
"test abort",
"agent",
);
expect(result.commitResult.commitSha).toBe(null);
expect(result.commitResult.pushed).toBe(false);
});
test("appendBatchAndCommitBordbuch appends all entries and commits once", async () => {
const mockEntry1 = {
id: "evt-004",
kind: "nachweis-consent",
systemId,
summary: "consent",
} as BordbuchEntry;
const mockEntry2 = {
id: "evt-005",
kind: "nachweis-record",
systemId,
summary: "record",
} as BordbuchEntry;
const mockCommitResult = { commitSha: "ghi789", pushed: true, error: null };
mockAppendBordbuchEntry.mockResolvedValueOnce(mockEntry1).mockResolvedValueOnce(mockEntry2);
mockCommitAndPushBordbuch.mockResolvedValue(mockCommitResult);
const result = await appendBatchAndCommitBordbuch(
tmpDir,
systemId,
[
{
kind: "nachweis-consent",
summary: "consent revoked",
actor: "agent",
options: { writerRole: "nachweis", metadata: { action: "withdraw" } },
},
{
kind: "nachweis-record",
summary: "record withdrawn",
actor: "agent",
options: { writerRole: "nachweis", metadata: { action: "withdraw" } },
},
],
"Bordbuch: nachweis-withdraw test-system test-slug",
);
expect(result.entries).toHaveLength(2);
expect(result.entries[0]).toEqual(mockEntry1);
expect(result.entries[1]).toEqual(mockEntry2);
expect(result.commitResult).toEqual(mockCommitResult);
expect(mockAppendBordbuchEntry).toHaveBeenCalledTimes(2);
expect(mockCommitAndPushBordbuch).toHaveBeenCalledTimes(1);
expect(mockCommitAndPushBordbuch).toHaveBeenCalledWith(
tmpDir,
"Bordbuch: nachweis-withdraw test-system test-slug",
);
});
test("appendBatchAndCommitBordbuch with empty entries still commits", async () => {
const mockCommitResult = { commitSha: "empty-commit", pushed: true, error: null };
mockCommitAndPushBordbuch.mockResolvedValue(mockCommitResult);
const result = await appendBatchAndCommitBordbuch(
tmpDir,
systemId,
[],
"Bordbuch: empty-batch test-system",
);
expect(result.entries).toHaveLength(0);
expect(mockAppendBordbuchEntry).not.toHaveBeenCalled();
expect(mockCommitAndPushBordbuch).toHaveBeenCalledTimes(1);
});