diff --git a/src/lib/server/db/dashboard-store.test.ts b/src/lib/server/db/dashboard-store.test.ts index 7de467f..71942f0 100644 --- a/src/lib/server/db/dashboard-store.test.ts +++ b/src/lib/server/db/dashboard-store.test.ts @@ -2,7 +2,7 @@ import { existsSync, rmSync } from "node:fs"; import { mkdtemp } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { afterEach, describe, expect, test } from "vitest"; +import { afterEach, describe, expect, test, vi } from "vitest"; import { genericDashboardFixture } from "$lib/model/fixtures/generic"; import type { DashboardDocument } from "$lib/model"; import { @@ -15,6 +15,7 @@ const stores: DashboardStore[] = []; const tempRoots: string[] = []; afterEach(() => { + vi.useRealTimers(); stores.splice(0).forEach((store) => store.close()); tempRoots.splice(0).forEach((path) => rmSync(path, { force: true, recursive: true })); }); @@ -130,6 +131,40 @@ describe("dashboard persistence store", () => { "seed", ]); }); + + test("assigns monotonic revision timestamps for stable history ordering", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date("2026-06-18T12:00:00.000Z")); + const { store } = await createTestStore(); + const seed = store.seedDashboardIfEmpty(genericDashboardFixture, { + actor: "test-seed", + }); + const updated = cloneDashboard({ + ...genericDashboardFixture, + metadata: { + ...genericDashboardFixture.metadata, + title: "Changed Dashboard", + }, + }); + + const commit = store.commitDashboard(updated, { actor: "agent" }); + const rollback = store.rollbackToRevision(seed.id, { actor: "operator" }); + + expect([ + seed.createdAt.toISOString(), + commit.createdAt.toISOString(), + rollback.createdAt.toISOString(), + ]).toEqual([ + "2026-06-18T12:00:00.000Z", + "2026-06-18T12:00:00.001Z", + "2026-06-18T12:00:00.002Z", + ]); + expect(store.listRevisions().map((item) => item.id)).toEqual([ + rollback.id, + commit.id, + seed.id, + ]); + }); }); async function createTestStore() { diff --git a/src/lib/server/db/dashboard-store.ts b/src/lib/server/db/dashboard-store.ts index 957b2f6..92c7a73 100644 --- a/src/lib/server/db/dashboard-store.ts +++ b/src/lib/server/db/dashboard-store.ts @@ -185,7 +185,7 @@ class SqliteDashboardStore implements DashboardStore { throw new DashboardPersistenceValidationError(migration.failure); } - const now = new Date(); + const now = this.nextRevisionTimestamp(); const revision: DashboardRevision = { id: randomUUID(), dashboardId: this.dashboardId, @@ -230,6 +230,13 @@ class SqliteDashboardStore implements DashboardStore { return revision; } + + private nextRevisionTimestamp(): Date { + const active = this.getActiveDashboard(); + const activeUpdatedAtMs = active?.updatedAt.getTime() || 0; + + return new Date(Math.max(Date.now(), activeUpdatedAtMs + 1)); + } } type DashboardRevisionRow = typeof dashboardRevisions.$inferSelect;