From 5c7af20ad23757fde1b36ccf4e41ebfe10bd0890 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 31 Jul 2026 15:17:32 +0000 Subject: [PATCH] fix(cli): verify adaptive journal remotes --- src/apps/cli/commands/runCommand.ts | 16 ++++++++- src/apps/cli/commands/runCommand.unit.spec.ts | 35 ++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/apps/cli/commands/runCommand.ts b/src/apps/cli/commands/runCommand.ts index c1961500..d6f65c42 100644 --- a/src/apps/cli/commands/runCommand.ts +++ b/src/apps/cli/commands/runCommand.ts @@ -25,6 +25,8 @@ import { compatGlobal } from "@vrtmrz/livesync-commonlib/compat/common/coreEnvFu import { fsPromises as fs, path } from "@vrtmrz/livesync-commonlib/node"; import type { LiveSyncCouchDBReplicator } from "@vrtmrz/livesync-commonlib/compat/replication/couchdb/LiveSyncReplicator"; import type { LiveSyncJournalReplicator } from "@vrtmrz/livesync-commonlib/compat/replication/journal/LiveSyncJournalReplicator"; +import type { JournalSyncCore } from "@vrtmrz/livesync-commonlib/compat/replication/journal/JournalSyncCore"; +import { journalProtocolConfigurationForSettings } from "@vrtmrz/livesync-commonlib/journal-storage"; import { writeStderrLine, writeStdoutLine } from "@/apps/cli/cliOutput"; function redactConnectionString(uri: string): string { @@ -60,7 +62,19 @@ async function verifyRemoteState( } milestone = await dbRet.db.get(MILESTONE_DOCID); } else if (settings.remoteType === REMOTE_MINIO) { - milestone = await (replicator as LiveSyncJournalReplicator).client.downloadJson("_00000000-milestone.json"); + const journalReplicator = replicator as LiveSyncJournalReplicator; + if (journalProtocolConfigurationForSettings(settings).journalFormat === "adaptive-v1") { + try { + await journalReplicator.client.ensureCheckpointCachesAreFresh(); + standardIo.writeStderr("[Verification] Adaptive Journal repository is available.\n"); + return true; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + standardIo.writeStderr(`[Verification] Failed to verify Adaptive Journal repository: ${message}\n`); + return false; + } + } + milestone = await (journalReplicator.client as JournalSyncCore).downloadJson("_00000000-milestone.json"); } if (milestone) { diff --git a/src/apps/cli/commands/runCommand.unit.spec.ts b/src/apps/cli/commands/runCommand.unit.spec.ts index f3301575..4cfaf611 100644 --- a/src/apps/cli/commands/runCommand.unit.spec.ts +++ b/src/apps/cli/commands/runCommand.unit.spec.ts @@ -2,7 +2,12 @@ import { fsPromises as fs, os, path } from "@vrtmrz/livesync-commonlib/node"; import * as processSetting from "@vrtmrz/livesync-commonlib/compat/API/processSetting"; import { ConnectionStringParser } from "@vrtmrz/livesync-commonlib/compat/common/ConnectionString"; import { configURIBase } from "@vrtmrz/livesync-commonlib/compat/common/models/shared.const"; -import { DEFAULT_SETTINGS, REMOTE_COUCHDB, REMOTE_MINIO, REMOTE_P2P } from "@vrtmrz/livesync-commonlib/compat/common/types"; +import { + DEFAULT_SETTINGS, + REMOTE_COUCHDB, + REMOTE_MINIO, + REMOTE_P2P, +} from "@vrtmrz/livesync-commonlib/compat/common/types"; import { describe, expect, it, vi, beforeEach, afterEach } from "vitest"; import { runCommand } from "./runCommand"; import type { CLIOptions } from "./types"; @@ -717,6 +722,34 @@ describe("runCommand abnormal cases", () => { expect(core.services.control.applySettings).not.toHaveBeenCalled(); }); + it("verifies an Adaptive Journal repository without reading the legacy milestone", async () => { + const core = createCoreMock(); + const settings = core.services.setting.currentSettings(); + settings.remoteType = REMOTE_MINIO; + settings.journalFormat = "adaptive-v1"; + settings.packReadPolicy = "whole-pack"; + + const ensureCheckpointCachesAreFresh = vi.fn(async () => {}); + core.services.replicator.getActiveReplicator.mockReturnValue({ + nodeid: "test-node-id", + initializeDatabaseForReplication: vi.fn(async () => {}), + client: { + ensureCheckpointCachesAreFresh, + }, + }); + + const result = await runCommand(makeOptions("mark-resolved", []), { + ...context, + core, + }); + + expect(result).toBe(true); + expect(ensureCheckpointCachesAreFresh).toHaveBeenCalledTimes(1); + expect(core.services.context.standardIo.writeStderr).toHaveBeenCalledWith( + "[Verification] Adaptive Journal repository is available.\n" + ); + }); + it("mark-resolved with remote-id temporarily activates it and runs markResolved", async () => { const core = createCoreMock(); const settings = core.services.setting.currentSettings();