diff --git a/src/features/ConfigSync/CmdConfigSync.command.unit.spec.ts b/src/features/ConfigSync/CmdConfigSync.command.unit.spec.ts index 0237360c..5f524f8a 100644 --- a/src/features/ConfigSync/CmdConfigSync.command.unit.spec.ts +++ b/src/features/ConfigSync/CmdConfigSync.command.unit.spec.ts @@ -52,7 +52,7 @@ vi.mock("@/common/obsidianCommunityPlugins.ts", () => ({ })); import { cancelTask } from "@/common/utils.ts"; -import { ConfigSync } from "./CmdConfigSync"; +import { ConfigSync, PluginDataExDisplayV2, type IPluginDataExDisplay } from "./CmdConfigSync"; describe("ConfigSync commands", () => { it("shows the Customisation Sync command only whilst the feature is enabled", () => { @@ -111,3 +111,25 @@ describe("ConfigSync commands", () => { expect(periodicPluginSweepProcessor.disable).toHaveBeenCalledOnce(); }); }); + +describe("PluginDataExDisplayV2", () => { + const entryWithModifiedTimes = (mtimes: number[]) => + new PluginDataExDisplayV2({ + documentPath: "ix:device/plugin_main/example", + category: "PLUGIN_MAIN", + name: "example", + term: "device", + files: mtimes.map((mtime, index) => ({ filename: `file-${index}`, mtime, data: [] })), + } as unknown as IPluginDataExDisplay); + + it("averages millisecond modification times without truncating them to 32 bits", () => { + const mtime = Date.UTC(2026, 8, 15); + expect(entryWithModifiedTimes([mtime, mtime, mtime]).mtime).toBe(mtime); + }); + + it("keeps a newer copy newer than an older one", () => { + const newer = entryWithModifiedTimes([Date.UTC(2026, 8, 15)]); + const older = entryWithModifiedTimes([Date.UTC(2026, 8, 2)]); + expect(newer.mtime).toBeGreaterThan(older.mtime); + }); +}); diff --git a/src/features/ConfigSync/CmdConfigSync.ts b/src/features/ConfigSync/CmdConfigSync.ts index 4201385f..b560dba6 100644 --- a/src/features/ConfigSync/CmdConfigSync.ts +++ b/src/features/ConfigSync/CmdConfigSync.ts @@ -368,7 +368,8 @@ export class PluginDataExDisplayV2 { return this._version; } get mtime(): number { - return ~~this.files.reduce((a, b) => a + b.mtime, 0) / this.files.length; + // Millisecond timestamps exceed 32 bits, so `~~` would wrap them. + return Math.floor(this.files.reduce((a, b) => a + b.mtime, 0) / this.files.length); } } export type PluginDataEx = {