mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-07-23 04:52:58 +00:00
217 lines
7.7 KiB
TypeScript
217 lines
7.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { NEW_VAULT_SETTINGS, type SettingsMigrationState } from "@vrtmrz/livesync-commonlib/settings";
|
|
import type { CompatibilityPause } from "@/common/databaseCompatibility.ts";
|
|
import {
|
|
REVIEW_HARNESS_SCENARIO_IDS,
|
|
formatReviewHarnessReport,
|
|
inspectCompatibilityReview,
|
|
inspectSettingsLifecycle,
|
|
parsePendingReviewRun,
|
|
} from "./reviewHarnessContract";
|
|
|
|
const preservedSyncSettings = {
|
|
liveSync: true,
|
|
syncOnSave: false,
|
|
syncOnEditorSave: true,
|
|
syncOnStart: false,
|
|
syncOnFileOpen: true,
|
|
syncAfterMerge: false,
|
|
periodicReplication: true,
|
|
};
|
|
|
|
function migration(overrides: Partial<SettingsMigrationState> = {}): SettingsMigrationState {
|
|
return {
|
|
sourceVersion: 9,
|
|
targetVersion: 10,
|
|
isNewVault: false,
|
|
isFromFutureSchema: false,
|
|
changed: true,
|
|
requiresSyncReview: true,
|
|
reviewReasons: [
|
|
{
|
|
code: "legacy-update-review-pending",
|
|
fromVersion: 9,
|
|
toVersion: 10,
|
|
},
|
|
],
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function compatibilityPause(): CompatibilityPause {
|
|
return {
|
|
resumable: true,
|
|
reasons: [
|
|
{
|
|
source: "settings-schema",
|
|
sourceVersion: 9,
|
|
currentVersion: 10,
|
|
isFromFutureSchema: false,
|
|
resumable: true,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
describe("Review Harness contract", () => {
|
|
it("accepts only a fixed one-shot restart continuation", () => {
|
|
const pendingRun = {
|
|
formatVersion: 1,
|
|
requestId: "compatibility-review-2026-07-18T12:00:00.000Z",
|
|
scenarioId: "compatibility-review",
|
|
stage: "awaiting-restart",
|
|
requestedAt: "2026-07-18T12:00:00.000Z",
|
|
};
|
|
|
|
expect(parsePendingReviewRun(JSON.stringify(pendingRun))).toEqual({ pendingRun });
|
|
expect(
|
|
parsePendingReviewRun(
|
|
JSON.stringify({ ...pendingRun, scenarioId: "arbitrary-command", command: "delete-vault" })
|
|
)
|
|
).toEqual({ error: "Unknown Review Harness scenario: arbitrary-command" });
|
|
expect(parsePendingReviewRun("not-json")).toEqual({ error: "Review Harness continuation is not valid JSON" });
|
|
expect(REVIEW_HARNESS_SCENARIO_IDS).toEqual([
|
|
"settings-lifecycle",
|
|
"compatibility-review",
|
|
"p2p-composition",
|
|
"vault-round-trip",
|
|
]);
|
|
});
|
|
|
|
it("checks that an existing Vault keeps the seven synchronisation choices typed and intact", () => {
|
|
const result = inspectSettingsLifecycle({
|
|
migration: migration(),
|
|
settings: preservedSyncSettings,
|
|
newVaultSettings: NEW_VAULT_SETTINGS,
|
|
});
|
|
|
|
expect(result.status).toBe("passed");
|
|
expect(result.detail).toContain("existing Vault");
|
|
expect(result.observations).toContain("liveSync=true");
|
|
expect(result.observations).toContain("periodicReplication=true");
|
|
});
|
|
|
|
it("checks recommended values only for a genuinely new Vault", () => {
|
|
const result = inspectSettingsLifecycle({
|
|
migration: migration({
|
|
sourceVersion: 10,
|
|
targetVersion: 10,
|
|
isNewVault: true,
|
|
changed: false,
|
|
requiresSyncReview: false,
|
|
reviewReasons: [],
|
|
}),
|
|
settings: {
|
|
...preservedSyncSettings,
|
|
syncMaxSizeInMB: NEW_VAULT_SETTINGS.syncMaxSizeInMB,
|
|
chunkSplitterVersion: NEW_VAULT_SETTINGS.chunkSplitterVersion,
|
|
doNotUseFixedRevisionForChunks: NEW_VAULT_SETTINGS.doNotUseFixedRevisionForChunks,
|
|
usePluginSyncV2: NEW_VAULT_SETTINGS.usePluginSyncV2,
|
|
handleFilenameCaseSensitive: NEW_VAULT_SETTINGS.handleFilenameCaseSensitive,
|
|
E2EEAlgorithm: NEW_VAULT_SETTINGS.E2EEAlgorithm,
|
|
},
|
|
newVaultSettings: NEW_VAULT_SETTINGS,
|
|
});
|
|
|
|
expect(result.status).toBe("passed");
|
|
expect(result.detail).toContain("new Vault recommendations");
|
|
});
|
|
|
|
it("requires the compatibility review controller to initialise when settings migration requires review", () => {
|
|
expect(
|
|
inspectSettingsLifecycle({
|
|
migration: undefined,
|
|
settings: preservedSyncSettings,
|
|
newVaultSettings: NEW_VAULT_SETTINGS,
|
|
}).status
|
|
).toBe("failed");
|
|
|
|
expect(
|
|
inspectCompatibilityReview({
|
|
migration: migration(),
|
|
reviewInitialised: false,
|
|
pendingPause: undefined,
|
|
})
|
|
).toEqual({
|
|
status: "failed",
|
|
detail: "The settings migration requires review, but the compatibility review was not initialised.",
|
|
observations: [],
|
|
});
|
|
});
|
|
|
|
it("distinguishes a completed compatibility review from an uninitialised controller", () => {
|
|
const result = inspectCompatibilityReview({
|
|
migration: migration(),
|
|
reviewInitialised: true,
|
|
pendingPause: undefined,
|
|
});
|
|
|
|
expect(result).toMatchObject({
|
|
status: "passed",
|
|
detail: "No compatibility review is pending on this device.",
|
|
});
|
|
expect(result.observations).toContain("compatibilityReviewInitialised=true");
|
|
});
|
|
|
|
it("reports only bounded compatibility evidence", () => {
|
|
const pendingPause: CompatibilityPause = {
|
|
resumable: true,
|
|
reasons: [
|
|
...compatibilityPause().reasons,
|
|
{
|
|
source: "legacy-review",
|
|
message: "/Users/reviewer/private-vault/secret-note.md",
|
|
resumable: true,
|
|
},
|
|
],
|
|
};
|
|
|
|
const result = inspectCompatibilityReview({
|
|
migration: migration(),
|
|
reviewInitialised: true,
|
|
pendingPause,
|
|
});
|
|
|
|
expect(result.status).toBe("passed");
|
|
expect(result.observations).toContain("reasonSources=settings-schema,legacy-review");
|
|
expect(result.observations).toContain("resumable=true");
|
|
expect(result.observations.join("\n")).not.toContain("private-vault");
|
|
expect(result.observations.join("\n")).not.toContain("secret-note.md");
|
|
});
|
|
|
|
it("formats a review report without accepting Vault or credential fields", () => {
|
|
const report = formatReviewHarnessReport({
|
|
generatedAt: "2026-07-18T12:30:00.000Z",
|
|
environment: {
|
|
pluginVersion: "1.0.0-beta.1",
|
|
obsidianVersion: "1.13.1",
|
|
platform: "ios",
|
|
userAgent: "Obsidian|mobile\nreview",
|
|
viewport: "390x844",
|
|
},
|
|
scenarios: [
|
|
{
|
|
id: "compatibility-review",
|
|
title: "Compatibility review boundary",
|
|
mode: "guided",
|
|
status: "passed",
|
|
detail: "Compatibility pause confirmed",
|
|
},
|
|
],
|
|
transcript: [
|
|
{
|
|
at: "2026-07-18T12:29:00.000Z",
|
|
event: "compatibility-review-completed",
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(report).toContain("## Self-hosted LiveSync Review Harness report");
|
|
expect(report).toContain("Obsidian\\|mobile<br>review");
|
|
expect(report).toContain("Compatibility review boundary (compatibility-review)");
|
|
expect(report).toContain("was not transmitted");
|
|
expect(report).not.toContain("Vault name");
|
|
expect(report).not.toContain("credentials");
|
|
});
|
|
});
|