mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-09-09 12:17:07 +00:00
249 lines
12 KiB
TypeScript
249 lines
12 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import { REMOTE_COUCHDB, REMOTE_MINIO } from "@vrtmrz/livesync-commonlib/compat/common/types";
|
|
import { createNewVaultSettings } from "@vrtmrz/livesync-commonlib/settings";
|
|
import {
|
|
CAPABILITY_SUPPORT_KINDS,
|
|
NO_INTERACTION,
|
|
REPLICATION_COMPLETED,
|
|
REPLICATION_PROGRESS_PRESENTATIONS,
|
|
REMOTE_RESOURCE_KINDS,
|
|
USER_INITIATED_REPLICATION_AUTHORITY,
|
|
} from "@vrtmrz/livesync-commonlib/replication";
|
|
|
|
const constructorMocks = vi.hoisted(() => ({
|
|
couchDB: vi.fn(),
|
|
couchDBOneShot: vi.fn(async (..._args: unknown[]) => REPLICATION_COMPLETED),
|
|
objectStorage: vi.fn(),
|
|
objectStorageOneShot: vi.fn(async (..._args: unknown[]) => REPLICATION_COMPLETED),
|
|
}));
|
|
|
|
vi.mock("@vrtmrz/livesync-commonlib/compat/replication/couchdb/LiveSyncReplicator", () => ({
|
|
LiveSyncCouchDBReplicator: class {
|
|
constructor(host: unknown) {
|
|
constructorMocks.couchDB(host);
|
|
}
|
|
openOneShotReplicationWithOutcome(...args: unknown[]) {
|
|
return constructorMocks.couchDBOneShot(...args);
|
|
}
|
|
},
|
|
}));
|
|
vi.mock("@vrtmrz/livesync-commonlib/compat/replication/journal/LiveSyncJournalReplicator", () => ({
|
|
LiveSyncJournalReplicator: class {
|
|
constructor(host: unknown) {
|
|
constructorMocks.objectStorage(host);
|
|
}
|
|
openOneShotReplicationWithOutcome(...args: unknown[]) {
|
|
return constructorMocks.objectStorageOneShot(...args);
|
|
}
|
|
},
|
|
}));
|
|
|
|
import { createCentralReplicatorProviderDefinitions } from "./replicatorProviders";
|
|
|
|
describe("central Replicator provider definitions", () => {
|
|
it("keeps the retained remote-resource catalogue bounded", () => {
|
|
expect
|
|
.soft(Object.values(REMOTE_RESOURCE_KINDS).sort())
|
|
.toEqual(["connection", "preferred-tweak", "security-seed", "synchronisation-information"].sort());
|
|
});
|
|
|
|
it("composes CouchDB and Object Storage policies outside LiveSyncBaseCore", async () => {
|
|
const host = {} as Parameters<typeof createCentralReplicatorProviderDefinitions>[0];
|
|
const definitions = createCentralReplicatorProviderDefinitions(host);
|
|
const couchDB = definitions.get(REMOTE_COUCHDB)!;
|
|
const objectStorage = definitions.get(REMOTE_MINIO)!;
|
|
|
|
expect([...definitions.keys()]).toEqual([REMOTE_COUCHDB, REMOTE_MINIO]);
|
|
expect("sameKindReconciliation" in couchDB).toBe(false);
|
|
expect("sameKindReconciliation" in objectStorage).toBe(false);
|
|
|
|
expect(
|
|
couchDB.isConfigured(
|
|
Object.assign(createNewVaultSettings(), {
|
|
remoteType: REMOTE_COUCHDB,
|
|
couchDB_URI: "https://couch.example.test",
|
|
couchDB_DBNAME: "vault",
|
|
})
|
|
)
|
|
).toBe(true);
|
|
expect(
|
|
objectStorage.isConfigured(
|
|
Object.assign(createNewVaultSettings(), {
|
|
remoteType: REMOTE_MINIO,
|
|
endpoint: "https://objects.example.test",
|
|
bucket: "vault",
|
|
})
|
|
)
|
|
).toBe(true);
|
|
|
|
await couchDB.create(createNewVaultSettings());
|
|
await objectStorage.create(createNewVaultSettings());
|
|
expect(constructorMocks.couchDB).toHaveBeenCalledWith(host);
|
|
expect(constructorMocks.objectStorage).toHaveBeenCalledWith(host);
|
|
});
|
|
|
|
it("rejects incomplete and wrong-kind settings before construction", () => {
|
|
const definitions = createCentralReplicatorProviderDefinitions({} as never);
|
|
const couchDB = definitions.get(REMOTE_COUCHDB)!;
|
|
const objectStorage = definitions.get(REMOTE_MINIO)!;
|
|
|
|
expect(couchDB.isConfigured(Object.assign(createNewVaultSettings(), { remoteType: REMOTE_MINIO }))).toBe(false);
|
|
expect(
|
|
objectStorage.isConfigured(Object.assign(createNewVaultSettings(), { remoteType: REMOTE_COUCHDB }))
|
|
).toBe(false);
|
|
});
|
|
|
|
it("declares the retained owned resources and cohesive optional administration", () => {
|
|
const definitions = createCentralReplicatorProviderDefinitions({} as never);
|
|
const couchResources = definitions.get(REMOTE_COUCHDB)?.remoteResources;
|
|
const objectResources = definitions.get(REMOTE_MINIO)?.remoteResources;
|
|
const couchAdministration = definitions.get(REMOTE_COUCHDB)?.centralRemoteAdministration;
|
|
const objectAdministration = definitions.get(REMOTE_MINIO)?.centralRemoteAdministration;
|
|
|
|
expect(Object.keys(couchResources ?? {}).sort()).toEqual(Object.values(REMOTE_RESOURCE_KINDS).sort());
|
|
expect(Object.keys(objectResources ?? {}).sort()).toEqual(Object.values(REMOTE_RESOURCE_KINDS).sort());
|
|
expect(couchResources?.[REMOTE_RESOURCE_KINDS.CONNECTION].kind).toBe(CAPABILITY_SUPPORT_KINDS.SUPPORTED);
|
|
expect(couchResources?.[REMOTE_RESOURCE_KINDS.PREFERRED_TWEAK].kind).toBe(CAPABILITY_SUPPORT_KINDS.SUPPORTED);
|
|
expect(couchResources?.[REMOTE_RESOURCE_KINDS.SECURITY_SEED].kind).toBe(CAPABILITY_SUPPORT_KINDS.SUPPORTED);
|
|
expect(couchResources?.[REMOTE_RESOURCE_KINDS.SYNCHRONISATION_INFORMATION].kind).toBe(
|
|
CAPABILITY_SUPPORT_KINDS.SUPPORTED
|
|
);
|
|
expect(objectResources?.[REMOTE_RESOURCE_KINDS.SECURITY_SEED].kind).toBe(CAPABILITY_SUPPORT_KINDS.SUPPORTED);
|
|
expect(objectResources?.[REMOTE_RESOURCE_KINDS.SYNCHRONISATION_INFORMATION].kind).toBe(
|
|
CAPABILITY_SUPPORT_KINDS.NOT_APPLICABLE
|
|
);
|
|
expect(couchAdministration?.kind).toBe(CAPABILITY_SUPPORT_KINDS.SUPPORTED);
|
|
expect(objectAdministration?.kind).toBe(CAPABILITY_SUPPORT_KINDS.SUPPORTED);
|
|
expect("activeRemoteReads" in definitions.get(REMOTE_COUCHDB)!).toBe(false);
|
|
expect("fullTransfers" in definitions.get(REMOTE_COUCHDB)!).toBe(false);
|
|
});
|
|
|
|
it("dispatches central finite work through provider-local attempt results", async () => {
|
|
const definitions = createCentralReplicatorProviderDefinitions({} as never);
|
|
const couchDB = definitions.get(REMOTE_COUCHDB)!;
|
|
const objectStorage = definitions.get(REMOTE_MINIO)!;
|
|
const setting = createNewVaultSettings();
|
|
const couchInstance = await couchDB.create(setting);
|
|
const objectInstance = await objectStorage.create(setting);
|
|
if (!couchInstance || !objectInstance) throw new Error("Provider construction failed");
|
|
if (couchDB.userInitiatedOneShot.kind !== CAPABILITY_SUPPORT_KINDS.SUPPORTED) {
|
|
throw new Error("CouchDB OneShot is unavailable");
|
|
}
|
|
if (objectStorage.unattendedOneShot.kind !== CAPABILITY_SUPPORT_KINDS.SUPPORTED) {
|
|
throw new Error("Object Storage OneShot is unavailable");
|
|
}
|
|
|
|
await expect(
|
|
couchDB.userInitiatedOneShot.run(couchInstance, setting, {
|
|
trigger: "manual",
|
|
progressPresentation: REPLICATION_PROGRESS_PRESENTATIONS.NOTICE,
|
|
interaction: USER_INITIATED_REPLICATION_AUTHORITY,
|
|
})
|
|
).resolves.toBe(REPLICATION_COMPLETED);
|
|
await expect(
|
|
objectStorage.unattendedOneShot.run(objectInstance, setting, {
|
|
trigger: "resume",
|
|
interaction: NO_INTERACTION,
|
|
})
|
|
).resolves.toBe(REPLICATION_COMPLETED);
|
|
|
|
expect(constructorMocks.couchDBOneShot).toHaveBeenCalledWith(setting, true);
|
|
expect(constructorMocks.objectStorageOneShot).toHaveBeenCalledWith(setting, false);
|
|
});
|
|
|
|
it.each([
|
|
["CouchDB", REMOTE_COUCHDB],
|
|
["Object Storage", REMOTE_MINIO],
|
|
] as const)("maps %s progress presentation independently of recovery authority", async (label, remoteType) => {
|
|
const definitions = createCentralReplicatorProviderDefinitions({} as never);
|
|
const definition = definitions.get(remoteType)!;
|
|
if (definition.userInitiatedOneShot.kind !== CAPABILITY_SUPPORT_KINDS.SUPPORTED) {
|
|
throw new Error(`${label} OneShot is unavailable`);
|
|
}
|
|
const setting = Object.assign(createNewVaultSettings(), { remoteType });
|
|
const openOneShotReplicationWithOutcome = vi.fn(async () => REPLICATION_COMPLETED);
|
|
const instance = {
|
|
initializeDatabaseForReplication: vi.fn(async () => true),
|
|
openReplication: vi.fn(async () => true),
|
|
terminateSync: vi.fn(),
|
|
closeReplication: vi.fn(),
|
|
openOneShotReplicationWithOutcome,
|
|
};
|
|
|
|
await definition.userInitiatedOneShot.run(instance, setting, {
|
|
trigger: "manual",
|
|
progressPresentation: REPLICATION_PROGRESS_PRESENTATIONS.QUIET,
|
|
interaction: USER_INITIATED_REPLICATION_AUTHORITY,
|
|
});
|
|
await definition.userInitiatedOneShot.run(instance, setting, {
|
|
trigger: "manual",
|
|
progressPresentation: REPLICATION_PROGRESS_PRESENTATIONS.NOTICE,
|
|
interaction: USER_INITIATED_REPLICATION_AUTHORITY,
|
|
});
|
|
|
|
expect(openOneShotReplicationWithOutcome).toHaveBeenNthCalledWith(1, setting, false);
|
|
expect(openOneShotReplicationWithOutcome).toHaveBeenNthCalledWith(2, setting, true);
|
|
});
|
|
|
|
it("dispatches central finite work through the declared operation rather than constructor identity", async () => {
|
|
const definitions = createCentralReplicatorProviderDefinitions({} as never);
|
|
const couchDB = definitions.get(REMOTE_COUCHDB)!;
|
|
const objectStorage = definitions.get(REMOTE_MINIO)!;
|
|
const setting = createNewVaultSettings();
|
|
const createStructuralOneShotReplicator = () => ({
|
|
initializeDatabaseForReplication: vi.fn(async () => true),
|
|
openReplication: vi.fn(async () => true),
|
|
terminateSync: vi.fn(),
|
|
closeReplication: vi.fn(),
|
|
openOneShotReplicationWithOutcome: vi.fn(async () => REPLICATION_COMPLETED),
|
|
});
|
|
const couchInstance = createStructuralOneShotReplicator();
|
|
const objectStorageInstance = createStructuralOneShotReplicator();
|
|
if (couchDB.userInitiatedOneShot.kind !== CAPABILITY_SUPPORT_KINDS.SUPPORTED) {
|
|
throw new Error("CouchDB OneShot is unavailable");
|
|
}
|
|
if (objectStorage.unattendedOneShot.kind !== CAPABILITY_SUPPORT_KINDS.SUPPORTED) {
|
|
throw new Error("Object Storage OneShot is unavailable");
|
|
}
|
|
|
|
const couchOutcome = await couchDB.userInitiatedOneShot.run(couchInstance, setting, {
|
|
trigger: "manual",
|
|
progressPresentation: REPLICATION_PROGRESS_PRESENTATIONS.NOTICE,
|
|
interaction: USER_INITIATED_REPLICATION_AUTHORITY,
|
|
});
|
|
const objectStorageOutcome = await objectStorage.unattendedOneShot.run(objectStorageInstance, setting, {
|
|
trigger: "resume",
|
|
interaction: NO_INTERACTION,
|
|
});
|
|
|
|
expect.soft(couchOutcome).toBe(REPLICATION_COMPLETED);
|
|
expect.soft(objectStorageOutcome).toBe(REPLICATION_COMPLETED);
|
|
expect(couchInstance.openOneShotReplicationWithOutcome).toHaveBeenCalledWith(setting, true);
|
|
expect(objectStorageInstance.openOneShotReplicationWithOutcome).toHaveBeenCalledWith(setting, false);
|
|
});
|
|
|
|
it("rejects a one-shot adapter whose Replicator does not declare the required operation", async () => {
|
|
const definitions = createCentralReplicatorProviderDefinitions({} as never);
|
|
const couchDB = definitions.get(REMOTE_COUCHDB)!;
|
|
const setting = createNewVaultSettings();
|
|
const incompleteInstance = {
|
|
initializeDatabaseForReplication: vi.fn(async () => true),
|
|
openReplication: vi.fn(async () => true),
|
|
terminateSync: vi.fn(),
|
|
closeReplication: vi.fn(),
|
|
};
|
|
if (couchDB.userInitiatedOneShot.kind !== CAPABILITY_SUPPORT_KINDS.SUPPORTED) {
|
|
throw new Error("CouchDB OneShot is unavailable");
|
|
}
|
|
|
|
const outcome = await couchDB.userInitiatedOneShot.run(incompleteInstance, setting, {
|
|
trigger: "manual",
|
|
progressPresentation: REPLICATION_PROGRESS_PRESENTATIONS.NOTICE,
|
|
interaction: USER_INITIATED_REPLICATION_AUTHORITY,
|
|
});
|
|
|
|
expect(outcome.status).toBe("failed");
|
|
expect(incompleteInstance.openReplication).not.toHaveBeenCalled();
|
|
});
|
|
});
|