From 32e992db93ebdfbe4ef3840825d1cdfbd76d9ac9 Mon Sep 17 00:00:00 2001 From: Ouyang Xingyuan Date: Thu, 16 Jul 2026 10:02:01 +0800 Subject: [PATCH] fix: scope merge notice suppression to bulk resolution Reason: - Bulk newest resolution should not display one success notice per file, while non-bulk resolution must retain its existing notice. Changes: - Pass an explicit notice flag only through the bulk resolution path. - Cover bulk suppression, non-bulk notices, and ten-file progress updates. --- .../coreFeatures/ModuleConflictResolver.ts | 11 +++-- .../ModuleConflictResolver.unit.spec.ts | 46 ++++++++++++++++--- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/modules/coreFeatures/ModuleConflictResolver.ts b/src/modules/coreFeatures/ModuleConflictResolver.ts index 62b55dd1..b081af22 100644 --- a/src/modules/coreFeatures/ModuleConflictResolver.ts +++ b/src/modules/coreFeatures/ModuleConflictResolver.ts @@ -30,7 +30,8 @@ export class ModuleConflictResolver extends AbstractModule { private async _resolveConflictByDeletingRev( path: FilePathWithPrefix, deleteRevision: string, - subTitle = "" + subTitle = "", + showNotice = true ): Promise { const title = `Resolving ${subTitle ? `[${subTitle}]` : ""}:`; if (!(await this.core.fileHandler.deleteRevisionFromDB(path, deleteRevision))) { @@ -58,7 +59,7 @@ export class ModuleConflictResolver extends AbstractModule { this._log(`Could not write the resolved content to the storage: ${path}`, LOG_LEVEL_NOTICE); return MISSING_OR_ERROR; } - const level = subTitle.indexOf("same") !== -1 || subTitle === "NEWEST" ? LOG_LEVEL_INFO : LOG_LEVEL_NOTICE; + const level = subTitle.indexOf("same") !== -1 || !showNotice ? LOG_LEVEL_INFO : LOG_LEVEL_NOTICE; this._log(`${path} has been merged automatically`, level); return AUTO_MERGED; } @@ -165,7 +166,7 @@ export class ModuleConflictResolver extends AbstractModule { }); } - private async _anyResolveConflictByNewest(filename: FilePathWithPrefix): Promise { + private async _anyResolveConflictByNewest(filename: FilePathWithPrefix, showNotice = true): Promise { const currentRev = await this.core.databaseFileAccess.fetchEntryMeta(filename, undefined, true); if (currentRev == false) { this._log(`Could not get current revision of ${filename}`); @@ -203,7 +204,7 @@ export class ModuleConflictResolver extends AbstractModule { this._log( `conflict: Deleting the older revision ${mTimeAndRev[i][1]} (${new Date(mTimeAndRev[i][0]).toLocaleString()}) of ${filename}` ); - await this.services.conflict.resolveByDeletingRevision(filename, mTimeAndRev[i][1], "NEWEST"); + await this._resolveConflictByDeletingRev(filename, mTimeAndRev[i][1], "NEWEST", showNotice); } return true; } @@ -221,7 +222,7 @@ export class ModuleConflictResolver extends AbstractModule { LOG_LEVEL_NOTICE, "resolveAllConflictedFilesByNewerOnes" ); - await this.services.conflict.resolveByNewest(file); + await this._anyResolveConflictByNewest(file, false); } this._log(`Done!`, LOG_LEVEL_NOTICE, "resolveAllConflictedFilesByNewerOnes"); } diff --git a/src/modules/coreFeatures/ModuleConflictResolver.unit.spec.ts b/src/modules/coreFeatures/ModuleConflictResolver.unit.spec.ts index db9a5550..b60f3c26 100644 --- a/src/modules/coreFeatures/ModuleConflictResolver.unit.spec.ts +++ b/src/modules/coreFeatures/ModuleConflictResolver.unit.spec.ts @@ -1,9 +1,14 @@ import { describe, expect, it, vi } from "vitest"; -import { DEFAULT_SETTINGS, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, type FilePathWithPrefix } from "@lib/common/types"; +import { + DEFAULT_SETTINGS, + LOG_LEVEL_INFO, + LOG_LEVEL_NOTICE, + type FilePathWithPrefix, + type MetaEntry, +} from "@lib/common/types"; import { ModuleConflictResolver } from "./ModuleConflictResolver"; function createModule(files: FilePathWithPrefix[] = []) { - const resolveByNewest = vi.fn(async () => true); const core = { _services: { API: { @@ -17,7 +22,7 @@ function createModule(files: FilePathWithPrefix[] = []) { saveSettingData: vi.fn(async () => undefined), }, conflict: { - resolveByNewest, + resolveByNewest: vi.fn(async () => true), }, }, settings: DEFAULT_SETTINGS, @@ -36,26 +41,55 @@ function createModule(files: FilePathWithPrefix[] = []) { const module = new ModuleConflictResolver(core); module._log = vi.fn(); - return { module, resolveByNewest }; + return { module }; } describe("ModuleConflictResolver bulk newest resolution", () => { - it("logs each successful newest resolution without displaying a notice", async () => { + it("retains the success notice for a non-bulk newest resolution", async () => { const { module } = createModule(); const path = "example.md" as FilePathWithPrefix; await (module as any)._resolveConflictByDeletingRev(path, "2-old", "NEWEST"); + expect(module._log).toHaveBeenLastCalledWith(`${path} has been merged automatically`, LOG_LEVEL_NOTICE); + }); + + it("logs a successful bulk newest resolution without displaying a notice", async () => { + const { module } = createModule(); + const path = "example.md" as FilePathWithPrefix; + module.core.databaseFileAccess.fetchEntryMeta = vi.fn( + async (_path: unknown, rev?: string): Promise => + ({ + _id: "doc-id", + _rev: rev ?? "2-current", + path, + ctime: 1, + mtime: rev ? 1 : 2, + size: 0, + children: [], + type: "plain", + eden: {}, + }) as unknown as MetaEntry + ); + module.core.databaseFileAccess.getConflictedRevs = vi + .fn() + .mockResolvedValueOnce(["1-old"]) + .mockResolvedValue([]); + + await (module as any)._anyResolveConflictByNewest(path, false); + expect(module._log).toHaveBeenLastCalledWith(`${path} has been merged automatically`, LOG_LEVEL_INFO); }); it("updates notice-level progress once every ten checked files", async () => { const files = Array.from({ length: 11 }, (_, index) => `note-${index}.md` as FilePathWithPrefix); - const { module, resolveByNewest } = createModule(files); + const { module } = createModule(files); + const resolveByNewest = vi.spyOn(module as any, "_anyResolveConflictByNewest").mockResolvedValue(true); await (module as any)._resolveAllConflictedFilesByNewerOnes(); expect(resolveByNewest).toHaveBeenCalledTimes(11); + expect(resolveByNewest).toHaveBeenCalledWith(files[0], false); expect(module._log).toHaveBeenCalledWith( "Check and Processing 10 / 11", LOG_LEVEL_NOTICE,