import { describe, expect, it, vi } from "vitest"; import type { DocumentID } from "@vrtmrz/livesync-commonlib/compat/common/types"; import type { MetadataDocumentRepairRequest, MetadataDocumentRepairResult, } from "@vrtmrz/livesync-commonlib/compat/serviceFeatures/offlineScanner"; import { executeMetadataIdentityRepair } from "./metadataIdentityRepair"; const request: MetadataDocumentRepairRequest = { actualDocumentId: "f:stale" as DocumentID, expectedDocumentId: "f:expected" as DocumentID, sourceRevision: "4-source", }; function createDependencies() { const events: string[] = []; return { events, confirm: vi.fn(async () => true), repair: vi.fn(async (): Promise => { events.push("repair"); return { status: "completed" as const, ...request, targetCreated: true, }; }), requestOrdinaryScan: vi.fn(async () => { events.push("scan"); return true; }), }; } describe("executeMetadataIdentityRepair", () => { it("performs no mutation when the separate confirmation is cancelled", async () => { const dependencies = createDependencies(); dependencies.confirm.mockResolvedValue(false); await expect(executeMetadataIdentityRepair(request, dependencies)).resolves.toEqual({ status: "cancelled", }); expect(dependencies.repair).not.toHaveBeenCalled(); expect(dependencies.requestOrdinaryScan).not.toHaveBeenCalled(); }); it("requests an ordinary scan only after Commonlib completes the exact repair", async () => { const dependencies = createDependencies(); await expect(executeMetadataIdentityRepair(request, dependencies)).resolves.toMatchObject({ status: "repair-result", result: { status: "completed" }, scanCompleted: true, }); expect(dependencies.repair).toHaveBeenCalledWith(request); expect(dependencies.requestOrdinaryScan).toHaveBeenCalledOnce(); expect(dependencies.repair.mock.invocationCallOrder[0]).toBeLessThan( dependencies.requestOrdinaryScan.mock.invocationCallOrder[0] ); expect(dependencies.events).toEqual(["repair", "scan"]); }); it("keeps a completed repair distinct when the ordinary scan cannot start", async () => { const dependencies = createDependencies(); dependencies.requestOrdinaryScan.mockResolvedValue(false); await expect(executeMetadataIdentityRepair(request, dependencies)).resolves.toMatchObject({ status: "repair-result", result: { status: "completed" }, scanCompleted: false, }); expect(dependencies.requestOrdinaryScan).toHaveBeenCalledOnce(); expect(dependencies.events).toEqual(["repair"]); }); it("keeps a completed repair distinct when requesting the ordinary scan throws", async () => { const dependencies = createDependencies(); const error = new Error("scan unavailable"); dependencies.requestOrdinaryScan.mockRejectedValue(error); await expect(executeMetadataIdentityRepair(request, dependencies)).resolves.toMatchObject({ status: "repair-result", result: { status: "completed" }, scanCompleted: false, scanError: error, }); expect(dependencies.events).toEqual(["repair"]); }); it("does not scan after a stale, blocked, or failed repair result", async () => { for (const status of ["stale", "blocked", "failed"] as const) { const dependencies = createDependencies(); dependencies.repair.mockResolvedValue({ status, ...request, targetCreated: false, }); await expect(executeMetadataIdentityRepair(request, dependencies)).resolves.toMatchObject({ status: "repair-result", result: { status }, scanCompleted: false, }); expect(dependencies.requestOrdinaryScan).not.toHaveBeenCalled(); } }); });