mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-09-01 00:07:06 +00:00
Refresh capability ADRs and Object Storage E2E evidence
This commit is contained in:
@@ -158,7 +158,7 @@ LIVESYNC_CLI_COMMAND="docker run --rm --network host --user $(id -u):$(id -g) --
|
||||
|
||||
`test:e2e:obsidian:minio-upload` reuses the Object Storage variables from `.test.env` or the process environment. It expects a reachable S3-compatible service and starts with isolated Object Storage settings and the device-local compatibility acknowledgement already in place, keeping the scenario focused on upload rather than unconfigured start-up or setup. It confirms those settings through `obsidian-cli eval`, creates a note in real Obsidian, runs one-shot Journal Sync, and verifies through the AWS SDK that objects were written under a unique bucket prefix. Adapter tests separately observe an in-progress SDK command, while this real-runtime workflow verifies the resulting request counters advance and rebalance.
|
||||
|
||||
`test:e2e:obsidian:object-storage-setup-uri-workflow` uses the public Commonlib-backed tool to generate the initial Setup URI for a unique MinIO prefix, completes visible initialisation on the first device, and then asks that working real Obsidian device to create a new Setup URI through the registered command. A second real Obsidian device imports only the device-generated URI. The workflow verifies A-to-B and B-to-A notes, captures the documented onboarding choices, and removes the Object Storage prefix only after both sessions have stopped.
|
||||
`test:e2e:obsidian:object-storage-setup-uri-workflow` uses the public Commonlib-backed tool to generate the initial Setup URI for a unique MinIO prefix, completes visible initialisation on the first device, and then asks that working real Obsidian device to create a new Setup URI through the registered command. A second real Obsidian device imports only the device-generated URI. The workflow verifies the A-to-B note through explicit replication, then verifies that the B-to-A note arrives through `syncOnStart` after restarting the first device, without requesting manual replication. It captures the documented onboarding choices, and removes the Object Storage prefix only after both sessions have stopped.
|
||||
|
||||
`test:e2e:obsidian:p2p-setup-uri-workflow` runs two concurrent isolated real Obsidian sessions against the local Compose Nostr relay fixture. The first device imports a generated initial Setup URI and completes its signalling test with zero peers, creates a Setup URI for the second device through the registered command, and remains online while the second device imports it. The second device must select the expected online source before Fetch can rebuild its local database. The workflow accepts each connection request visibly on the receiving device, verifies the initial A-to-B fetch, checks that the menu for the three persistent per-peer actions remains within the viewport, reconnects both P2P sessions in join order, and verifies the B-to-A return journey. Every started session remains tracked until teardown completes.
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { discoverObsidianCli, requireObsidianBinary } from "../runner/environmen
|
||||
import {
|
||||
assertEqual,
|
||||
pushLocalChanges,
|
||||
type ConfiguredSettings,
|
||||
waitForLiveSyncCoreReady,
|
||||
waitForLocalDatabaseEntry,
|
||||
} from "../runner/liveSyncWorkflow.ts";
|
||||
@@ -57,6 +58,10 @@ type RunnerContext = {
|
||||
activeSessions: Set<ObsidianLiveSyncSession>;
|
||||
};
|
||||
|
||||
type StartupSchedulingState = ConfiguredSettings & {
|
||||
periodicReplication: boolean;
|
||||
};
|
||||
|
||||
function sessionEnvironment(port: number): NodeJS.ProcessEnv {
|
||||
return { ...process.env, E2E_OBSIDIAN_REMOTE_DEBUGGING_PORT: String(port) };
|
||||
}
|
||||
@@ -187,6 +192,41 @@ async function waitForObjectStorageData(config: ObjectStorageConfig, prefix: str
|
||||
throw new Error(`Timed out waiting for Object Storage data under ${prefix}.`);
|
||||
}
|
||||
|
||||
async function configureMigratedStartupScheduling(
|
||||
cliBinary: string,
|
||||
environment: NodeJS.ProcessEnv
|
||||
): Promise<StartupSchedulingState> {
|
||||
return await evalObsidianJson<StartupSchedulingState>(
|
||||
cliBinary,
|
||||
[
|
||||
"(async()=>{",
|
||||
"const plugin=app.plugins.plugins['obsidian-livesync'];",
|
||||
"const core=plugin.core;",
|
||||
// Persist only the migration-shaped scheduling flags and leave the
|
||||
// generated URI unchanged. Device A stops before B creates the
|
||||
// return note, so save-triggered reconciliation cannot satisfy the
|
||||
// later start-up assertion.
|
||||
"await core.services.setting.applyExternalSettings({liveSync:true,syncOnStart:true,periodicReplication:false},true);",
|
||||
"const current=core.services.setting.currentSettings();",
|
||||
"return JSON.stringify({",
|
||||
"isConfigured:current.isConfigured,",
|
||||
"liveSync:current.liveSync,",
|
||||
"syncOnStart:current.syncOnStart,",
|
||||
"syncOnSave:current.syncOnSave,",
|
||||
"periodicReplication:current.periodicReplication,",
|
||||
"remoteType:current.remoteType,",
|
||||
"couchDB_URI:current.couchDB_URI,",
|
||||
"couchDB_DBNAME:current.couchDB_DBNAME,",
|
||||
"endpoint:current.endpoint,",
|
||||
"bucket:current.bucket,",
|
||||
"bucketPrefix:current.bucketPrefix,",
|
||||
"});",
|
||||
"})()",
|
||||
].join(""),
|
||||
environment
|
||||
);
|
||||
}
|
||||
|
||||
async function captureNote(port: number, path: string, text: string, filename: string): Promise<string> {
|
||||
await withObsidianPage(port, async (page) => {
|
||||
await page.evaluate((notePath) => {
|
||||
@@ -254,6 +294,24 @@ async function main(): Promise<void> {
|
||||
throw new Error("The first device returned the bootstrap Setup URI instead of generating a new one.");
|
||||
}
|
||||
screenshots.push(...generated.screenshots);
|
||||
const startupState = await configureMigratedStartupScheduling(context.cliBinary, sessionA.cliEnv);
|
||||
assertEqual(startupState.liveSync, true, "The first device did not persist its Continuous setting.");
|
||||
assertEqual(startupState.syncOnStart, true, "The first device did not persist syncOnStart.");
|
||||
assertEqual(
|
||||
startupState.periodicReplication,
|
||||
false,
|
||||
"Periodic replication could mask the syncOnStart return journey."
|
||||
);
|
||||
assertEqual(
|
||||
startupState.endpoint,
|
||||
objectStorage.endpoint,
|
||||
"Enabling syncOnStart changed the Object Storage endpoint."
|
||||
);
|
||||
assertEqual(
|
||||
startupState.bucketPrefix,
|
||||
bucketPrefix,
|
||||
"Enabling syncOnStart changed the Object Storage bucket prefix."
|
||||
);
|
||||
await stopSession(context, sessionA);
|
||||
|
||||
const sessionB = await startSession(context, vaultB, portB);
|
||||
@@ -290,7 +348,9 @@ async function main(): Promise<void> {
|
||||
const returningSessionA = await startSession(context, vaultA, portA);
|
||||
await waitForLiveSyncCoreReady(context.cliBinary, returningSessionA.cliEnv);
|
||||
await resumeCompatibilityReviewIfShown(portA);
|
||||
await pushLocalChanges(context.cliBinary, returningSessionA.cliEnv);
|
||||
// Deliberately omit manual replication here. Object Storage reports
|
||||
// Continuous as not applicable, so startup scheduling must honour the
|
||||
// retained syncOnStart setting by running an unattended OneShot.
|
||||
await waitForPathContent(vaultA, noteFromSecond, secondContent);
|
||||
screenshots.push(
|
||||
await captureNote(
|
||||
|
||||
Reference in New Issue
Block a user