diff --git a/src/config/io.observe-recovery.test.ts b/src/config/io.observe-recovery.test.ts index e7180e86a03..4352788e387 100644 --- a/src/config/io.observe-recovery.test.ts +++ b/src/config/io.observe-recovery.test.ts @@ -316,6 +316,44 @@ describe("config observe recovery", () => { }); }); + it("sync recovery records copyFileSync failure instead of falsely claiming restore succeeded", async () => { + await withSuiteHome(async (home) => { + const { deps, configPath, auditPath, warn } = makeDeps(home); + await seedConfigBackup(configPath, recoverableTelegramConfig); + const clobbered = await writeClobberedUpdateChannel(configPath); + + const copyError = Object.assign(new Error("EACCES: permission denied"), { code: "EACCES" }); + const failingFs: ObserveRecoveryDeps["fs"] = { + ...deps.fs, + copyFileSync: () => { + throw copyError; + }, + }; + const recovered = maybeRecoverSuspiciousConfigReadSync({ + deps: { ...deps, fs: failingFs }, + configPath, + raw: clobbered.raw, + parsed: clobbered.parsed, + }); + + expect((recovered.parsed as { gateway?: { mode?: string } }).gateway?.mode).toBe("local"); + await expect(fsp.readFile(configPath, "utf-8")).resolves.toBe(clobbered.raw); + expect(warn).toHaveBeenCalledWith( + expect.stringContaining("Config auto-restore from backup failed:"), + ); + expect(warn).toHaveBeenCalledWith(expect.stringContaining("EACCES: permission denied")); + expect(warn).not.toHaveBeenCalledWith( + expect.stringContaining("Config auto-restored from backup:"), + ); + + const observe = await readLastObserveEvent(auditPath); + expect(observe?.restoredFromBackup).toBe(false); + expect(observe?.valid).toBe(false); + expect(observe?.restoreErrorCode).toBe("EACCES"); + expect(observe?.restoreErrorMessage).toBe("EACCES: permission denied"); + }); + }); + it("dedupes repeated suspicious hashes", async () => { await withSuiteHome(async (home) => { const { deps, configPath, auditPath } = makeDeps(home); diff --git a/src/config/io.observe-recovery.ts b/src/config/io.observe-recovery.ts index 82c3c87f358..dddc35c99b4 100644 --- a/src/config/io.observe-recovery.ts +++ b/src/config/io.observe-recovery.ts @@ -688,9 +688,9 @@ export async function maybeRecoverSuspiciousConfigRead(params: { ); } else { params.deps.logger.warn( - `Config auto-restore from backup failed: ${params.configPath} (${suspicious.join(", ")})${ - restoreErrorDetails.message ? `: ${restoreErrorDetails.message}` : "" - }`, + `Config auto-restore from backup failed: ${params.configPath} (${suspicious.join(", ")}${ + restoreErrorDetails.message ? `; ${restoreErrorDetails.message}` : "" + })`, ); } await appendConfigAuditRecord( @@ -795,9 +795,9 @@ export function maybeRecoverSuspiciousConfigReadSync(params: { ); } else { params.deps.logger.warn( - `Config auto-restore from backup failed: ${params.configPath} (${suspicious.join(", ")})${ - restoreErrorDetails.message ? `: ${restoreErrorDetails.message}` : "" - }`, + `Config auto-restore from backup failed: ${params.configPath} (${suspicious.join(", ")}${ + restoreErrorDetails.message ? `; ${restoreErrorDetails.message}` : "" + })`, ); } appendConfigAuditRecordSync(