fix: close finite remote database connections

This commit is contained in:
vorotamoroz
2026-08-14 10:29:29 +00:00
parent 16c7cc1b0f
commit de03534d2f
10 changed files with 163 additions and 45 deletions
@@ -547,7 +547,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
if (typeof db === "string") {
Logger($msg("obsidianLiveSyncSettingTab.logCheckPassphraseFailed", { db }), LOG_LEVEL_NOTICE);
return false;
} else {
}
try {
if (await checkSyncInfo(db.db)) {
// Logger($msg("obsidianLiveSyncSettingTab.logDatabaseConnected"), LOG_LEVEL_NOTICE);
return true;
@@ -555,6 +556,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
Logger($msg("obsidianLiveSyncSettingTab.logPassphraseNotCompatible"), LOG_LEVEL_NOTICE);
return false;
}
} finally {
await db.db.close();
}
};
isPassphraseValid = async () => {
@@ -0,0 +1,75 @@
import { describe, expect, it, vi } from "vitest";
import { DEFAULT_SETTINGS, REMOTE_COUCHDB } from "@vrtmrz/livesync-commonlib/compat/common/types";
const negotiationMocks = vi.hoisted(() => ({
checkSyncInfo: vi.fn(async () => true),
}));
vi.mock("@/deps.ts", () => ({
App: class {},
Component: class {},
PluginSettingTab: class {},
}));
vi.mock("@/main.ts", () => ({ default: class {} }));
vi.mock("@/common/events.ts", () => ({
EVENT_REQUEST_RELOAD_SETTING_TAB: "request-reload-setting-tab",
eventHub: { onEvent: vi.fn() },
}));
vi.mock("@vrtmrz/livesync-commonlib/compat/pouchdb/negotiation", () => negotiationMocks);
vi.mock("@vrtmrz/livesync-commonlib/compat/replication/couchdb/LiveSyncReplicator", () => ({
LiveSyncCouchDBReplicator: class {},
}));
vi.mock("./LiveSyncSetting.ts", () => ({ LiveSyncSetting: class {} }));
vi.mock("./SettingPane.ts", () => ({
enableOnly: vi.fn(() => vi.fn()),
setLevelClass: vi.fn(),
setStyle: vi.fn(),
visibleOnly: vi.fn(() => vi.fn()),
}));
vi.mock("./PaneChangeLog.ts", () => ({ paneChangeLog: vi.fn() }));
vi.mock("./PaneSetup.ts", () => ({ paneSetup: vi.fn() }));
vi.mock("./PaneGeneral.ts", () => ({ paneGeneral: vi.fn() }));
vi.mock("./PaneRemoteConfig.ts", () => ({ paneRemoteConfig: vi.fn() }));
vi.mock("./PaneSelector.ts", () => ({ paneSelector: vi.fn() }));
vi.mock("./PaneSyncSettings.ts", () => ({ paneSyncSettings: vi.fn() }));
vi.mock("./PaneCustomisationSync.ts", () => ({ paneCustomisationSync: vi.fn() }));
vi.mock("./PaneHatch.ts", () => ({ paneHatch: vi.fn() }));
vi.mock("./PaneAdvanced.ts", () => ({ paneAdvanced: vi.fn() }));
vi.mock("./PanePowerUsers.ts", () => ({ panePowerUsers: vi.fn() }));
vi.mock("./PanePatches.ts", () => ({ panePatches: vi.fn() }));
vi.mock("./PaneMaintenance.ts", () => ({ paneMaintenance: vi.fn() }));
import { LiveSyncCouchDBReplicator } from "@vrtmrz/livesync-commonlib/compat/replication/couchdb/LiveSyncReplicator";
import { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab";
describe("ObsidianLiveSyncSettingTab passphrase verification", () => {
it("closes the finite remote connection after checking synchronisation information", async () => {
const remoteDatabase = {
close: vi.fn(async () => undefined),
};
const replicator = Object.assign(new LiveSyncCouchDBReplicator({} as never), {
connectRemoteCouchDBWithSetting: vi.fn(async () => ({ db: remoteDatabase })),
});
const plugin = {
app: {},
core: {
services: {
API: { isMobile: vi.fn(() => false) },
replicator: { getNewReplicator: vi.fn(() => replicator) },
},
},
};
const tab = new ObsidianLiveSyncSettingTab({} as never, plugin as never);
Object.assign(tab, {
_editingSettings: {
...DEFAULT_SETTINGS,
remoteType: REMOTE_COUCHDB,
},
});
await expect(tab.checkWorkingPassphrase()).resolves.toBe(true);
expect(negotiationMocks.checkSyncInfo).toHaveBeenCalledWith(remoteDatabase);
expect(remoteDatabase.close).toHaveBeenCalledOnce();
});
});
@@ -8,7 +8,7 @@ export type CouchDBConnectionProbeResult = { ok: true } | { ok: false; reason: s
type CouchDBConnectionResult =
| string
| {
db: unknown;
db: { close(): Promise<void> };
info: unknown;
};
@@ -50,7 +50,11 @@ export async function probeCouchDBConnection(
if (typeof result === "string") {
return { ok: false, reason: result };
}
return { ok: true };
try {
return { ok: true };
} finally {
await result.db.close();
}
}
export function isValidCouchDBServerURL(value: string): boolean {
@@ -14,8 +14,9 @@ describe("CouchDB setup connection policy", () => {
] as const)(
"%s can %s without changing the Commonlib connection contract",
async (createIfMissing, _description) => {
const close = vi.fn(async () => undefined);
const connectRemoteCouchDBWithSetting = vi.fn(async () => ({
db: {},
db: { close },
info: { db_name: "notes" },
}));
const replicator = {
@@ -27,6 +28,7 @@ describe("CouchDB setup connection policy", () => {
await expect(probeCouchDBConnection(replicator, settings, createIfMissing)).resolves.toEqual({ ok: true });
expect(connectRemoteCouchDBWithSetting).toHaveBeenCalledWith(settings, false, createIfMissing, false);
expect(replicator.tryConnectRemote).not.toHaveBeenCalled();
expect(close).toHaveBeenCalledOnce();
}
);