mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-09-24 03:27:06 +00:00
test: recover review harness controller contract
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { NEW_VAULT_SETTINGS, type SettingsMigrationState } from "@vrtmrz/livesync-commonlib/settings";
|
||||
import type { CompatibilityPause } from "@/common/databaseCompatibility.ts";
|
||||
import { ReviewHarnessController, type ReviewHarnessRuntime } from "./reviewHarnessController";
|
||||
|
||||
function migration(): SettingsMigrationState {
|
||||
return {
|
||||
sourceVersion: 9,
|
||||
targetVersion: 10,
|
||||
isNewVault: false,
|
||||
isFromFutureSchema: false,
|
||||
changed: true,
|
||||
requiresSyncReview: true,
|
||||
reviewReasons: [
|
||||
{
|
||||
code: "legacy-update-review-pending",
|
||||
fromVersion: 9,
|
||||
toVersion: 10,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function compatibilityPause(): CompatibilityPause {
|
||||
return {
|
||||
resumable: true,
|
||||
reasons: [
|
||||
{
|
||||
source: "settings-schema",
|
||||
sourceVersion: 9,
|
||||
currentVersion: 10,
|
||||
isFromFutureSchema: false,
|
||||
resumable: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function createRuntime(): ReviewHarnessRuntime & {
|
||||
compatibilityReviewInitialised: boolean;
|
||||
compatibilityPause: CompatibilityPause | undefined;
|
||||
continuation: string | null;
|
||||
events: string[];
|
||||
} {
|
||||
const services = {};
|
||||
const replicator = { env: { services } };
|
||||
const runtime: ReviewHarnessRuntime & {
|
||||
compatibilityReviewInitialised: boolean;
|
||||
compatibilityPause: CompatibilityPause | undefined;
|
||||
continuation: string | null;
|
||||
events: string[];
|
||||
} = {
|
||||
compatibilityReviewInitialised: true,
|
||||
compatibilityPause: compatibilityPause(),
|
||||
continuation: null,
|
||||
events: [],
|
||||
now: () => new Date("2026-07-18T12:00:00.000Z"),
|
||||
getSettings: () => ({
|
||||
liveSync: true,
|
||||
syncOnSave: false,
|
||||
syncOnEditorSave: true,
|
||||
syncOnStart: false,
|
||||
syncOnFileOpen: true,
|
||||
syncAfterMerge: false,
|
||||
periodicReplication: true,
|
||||
}),
|
||||
getNewVaultSettings: () => NEW_VAULT_SETTINGS,
|
||||
getSettingsMigrationState: () => migration(),
|
||||
isCompatibilityReviewInitialised() {
|
||||
return this.compatibilityReviewInitialised;
|
||||
},
|
||||
getCompatibilityPause() {
|
||||
return this.compatibilityPause;
|
||||
},
|
||||
openCompatibilityReview: vi.fn(async () => {
|
||||
runtime.events.push("open-compatibility-review");
|
||||
runtime.compatibilityPause = undefined;
|
||||
}),
|
||||
getP2PComposition: () => ({ first: replicator, second: replicator, expectedServices: services }),
|
||||
runVaultRoundTrip: vi.fn(async () => ({
|
||||
status: "passed" as const,
|
||||
detail: "The owned fixture tree was exercised and removed.",
|
||||
observations: [],
|
||||
})),
|
||||
readContinuation() {
|
||||
return this.continuation;
|
||||
},
|
||||
writeContinuation(value) {
|
||||
this.events.push("write");
|
||||
this.continuation = value;
|
||||
},
|
||||
deleteContinuation() {
|
||||
this.events.push("delete");
|
||||
this.continuation = null;
|
||||
},
|
||||
restart: vi.fn(),
|
||||
reportError: vi.fn(),
|
||||
copyText: vi.fn(async () => undefined),
|
||||
getEnvironment: () => ({
|
||||
pluginVersion: "1.0.0-beta.1",
|
||||
obsidianVersion: "1.13.1",
|
||||
platform: "desktop",
|
||||
userAgent: "test",
|
||||
viewport: "1280x720",
|
||||
}),
|
||||
};
|
||||
runtime.restart = vi.fn(() => runtime.events.push("restart"));
|
||||
return runtime;
|
||||
}
|
||||
|
||||
describe("ReviewHarnessController", () => {
|
||||
it("runs the automatic settings and P2P composition checks", async () => {
|
||||
const runtime = createRuntime();
|
||||
const controller = new ReviewHarnessController(runtime);
|
||||
|
||||
await controller.runAutomaticScenarios();
|
||||
|
||||
expect(controller.snapshot().results["settings-lifecycle"].status).toBe("passed");
|
||||
expect(controller.snapshot().results["p2p-composition"].status).toBe("passed");
|
||||
expect(controller.snapshot().results["vault-round-trip"].status).toBe("idle");
|
||||
expect(controller.snapshot().results["compatibility-review"].status).toBe("idle");
|
||||
});
|
||||
|
||||
it("runs the Vault fixture scenario only when it is selected explicitly", async () => {
|
||||
const runtime = createRuntime();
|
||||
const controller = new ReviewHarnessController(runtime);
|
||||
|
||||
await controller.runAutomaticScenarios();
|
||||
expect(runtime.runVaultRoundTrip).not.toHaveBeenCalled();
|
||||
|
||||
await controller.runScenario("vault-round-trip");
|
||||
expect(runtime.runVaultRoundTrip).toHaveBeenCalledOnce();
|
||||
expect(controller.snapshot().results["vault-round-trip"].status).toBe("passed");
|
||||
});
|
||||
|
||||
it("does not copy unexpected runtime error details into the report", async () => {
|
||||
const runtime = createRuntime();
|
||||
runtime.runVaultRoundTrip = vi.fn(async () => {
|
||||
throw new Error("Failed below /Users/reviewer/private-vault/secret-note.md");
|
||||
});
|
||||
const controller = new ReviewHarnessController(runtime);
|
||||
|
||||
await controller.runScenario("vault-round-trip");
|
||||
|
||||
const result = controller.snapshot().results["vault-round-trip"];
|
||||
const report = controller.createReport();
|
||||
expect(result).toMatchObject({
|
||||
status: "failed",
|
||||
detail: "The scenario failed unexpectedly. Review the in-app logs for diagnostic details.",
|
||||
});
|
||||
expect(report).not.toContain("private-vault");
|
||||
expect(report).not.toContain("secret-note.md");
|
||||
expect(runtime.reportError).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("deletes a one-shot continuation before exposing the resumed guided step", () => {
|
||||
const runtime = createRuntime();
|
||||
runtime.continuation = JSON.stringify({
|
||||
formatVersion: 1,
|
||||
requestId: "compatibility-review-2026-07-18T11:59:00.000Z",
|
||||
scenarioId: "compatibility-review",
|
||||
stage: "awaiting-restart",
|
||||
requestedAt: "2026-07-18T11:59:00.000Z",
|
||||
});
|
||||
const controller = new ReviewHarnessController(runtime);
|
||||
|
||||
controller.consumeContinuation();
|
||||
|
||||
expect(runtime.events).toEqual(["delete"]);
|
||||
expect(controller.snapshot().results["compatibility-review"]).toMatchObject({
|
||||
status: "waiting-for-user",
|
||||
});
|
||||
expect(controller.snapshot().resumedRequestId).toBe(
|
||||
"compatibility-review-2026-07-18T11:59:00.000Z"
|
||||
);
|
||||
});
|
||||
|
||||
it("does not copy rejected continuation values into the report", () => {
|
||||
const runtime = createRuntime();
|
||||
runtime.continuation = JSON.stringify({
|
||||
formatVersion: 1,
|
||||
requestId: "review-1",
|
||||
scenarioId: "/Users/reviewer/private-vault/secret-note.md",
|
||||
stage: "awaiting-restart",
|
||||
requestedAt: "2026-07-18T11:59:00.000Z",
|
||||
});
|
||||
const controller = new ReviewHarnessController(runtime);
|
||||
|
||||
controller.consumeContinuation();
|
||||
|
||||
expect(controller.snapshot().continuationError).toContain("private-vault");
|
||||
expect(controller.snapshot().results["compatibility-review"].detail).toBe(
|
||||
"The stored continuation was invalid and was removed."
|
||||
);
|
||||
expect(controller.createReport()).not.toContain("private-vault");
|
||||
expect(controller.createReport()).not.toContain("secret-note.md");
|
||||
});
|
||||
|
||||
it("does not accept an injected request ID which could enter the report transcript", () => {
|
||||
const runtime = createRuntime();
|
||||
runtime.continuation = JSON.stringify({
|
||||
formatVersion: 1,
|
||||
requestId: "/Users/reviewer/private-vault/secret-note.md",
|
||||
scenarioId: "compatibility-review",
|
||||
stage: "awaiting-restart",
|
||||
requestedAt: "2026-07-18T11:59:00.000Z",
|
||||
});
|
||||
const controller = new ReviewHarnessController(runtime);
|
||||
|
||||
controller.consumeContinuation();
|
||||
|
||||
expect(controller.snapshot().results["compatibility-review"].status).toBe("failed");
|
||||
expect(controller.createReport()).not.toContain("private-vault");
|
||||
expect(controller.createReport()).not.toContain("secret-note.md");
|
||||
});
|
||||
|
||||
it("persists the fixed continuation before requesting restart", () => {
|
||||
const runtime = createRuntime();
|
||||
const controller = new ReviewHarnessController(runtime);
|
||||
|
||||
controller.prepareCompatibilityReviewRestart();
|
||||
|
||||
expect(runtime.events).toEqual(["write", "restart"]);
|
||||
expect(JSON.parse(runtime.continuation!)).toMatchObject({
|
||||
formatVersion: 1,
|
||||
scenarioId: "compatibility-review",
|
||||
stage: "awaiting-restart",
|
||||
});
|
||||
});
|
||||
|
||||
it("passes only after the actual compatibility review clears its pause", async () => {
|
||||
const runtime = createRuntime();
|
||||
const controller = new ReviewHarnessController(runtime);
|
||||
await controller.runScenario("compatibility-review");
|
||||
|
||||
await controller.openCompatibilityReview();
|
||||
|
||||
expect(runtime.openCompatibilityReview).toHaveBeenCalledOnce();
|
||||
expect(controller.snapshot().results["compatibility-review"]).toMatchObject({
|
||||
status: "passed",
|
||||
detail: "The device-local compatibility pause was reviewed and cleared.",
|
||||
});
|
||||
});
|
||||
|
||||
it("remains waiting when the actual compatibility review stays paused", async () => {
|
||||
const runtime = createRuntime();
|
||||
runtime.openCompatibilityReview = vi.fn(async () => undefined);
|
||||
const controller = new ReviewHarnessController(runtime);
|
||||
await controller.runScenario("compatibility-review");
|
||||
|
||||
await controller.openCompatibilityReview();
|
||||
|
||||
expect(controller.snapshot().results["compatibility-review"].status).toBe("waiting-for-user");
|
||||
});
|
||||
|
||||
it("copies a Markdown report after compatibility evidence is recorded", async () => {
|
||||
const runtime = createRuntime();
|
||||
const controller = new ReviewHarnessController(runtime);
|
||||
await controller.runScenario("compatibility-review");
|
||||
await controller.openCompatibilityReview();
|
||||
|
||||
await controller.copyReport();
|
||||
|
||||
expect(runtime.copyText).toHaveBeenCalledOnce();
|
||||
expect(vi.mocked(runtime.copyText).mock.calls[0][0]).toContain("Compatibility review boundary");
|
||||
expect(controller.snapshot().results["compatibility-review"].status).toBe("passed");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user