test(obsidian): integrate representative adaptive journeys

# Conflicts:
#	package.json
#	test/e2e-obsidian/README.md
#	test/e2e-obsidian/runner/setupUri.ts
#	test/e2e-obsidian/scripts/local-suite.ts
#	test/e2e-obsidian/scripts/run-focused.ts
This commit is contained in:
vorotamoroz
2026-08-02 08:10:48 +00:00
6 changed files with 296 additions and 4 deletions
+2
View File
@@ -73,6 +73,8 @@
"test:e2e:obsidian:minio-upload": "tsx test/e2e-obsidian/scripts/minio-upload.ts",
"test:e2e:obsidian:adaptive-webdav": "tsx test/e2e-obsidian/scripts/adaptive-webdav.ts",
"test:e2e:obsidian:adaptive-webdav:services": "tsx test/e2e-obsidian/scripts/adaptive-webdav.ts --manage-webdav",
"test:e2e:obsidian:adaptive-postgrest": "tsx test/e2e-obsidian/scripts/adaptive-postgrest.ts",
"test:e2e:obsidian:adaptive-postgrest:services": "tsx test/e2e-obsidian/scripts/adaptive-postgrest.ts --manage-postgrest",
"test:e2e:obsidian:object-storage-setup-uri-workflow": "tsx test/e2e-obsidian/scripts/object-storage-setup-uri-workflow.ts",
"test:e2e:obsidian:p2p-setup-uri-workflow": "tsx test/e2e-obsidian/scripts/p2p-setup-uri-workflow.ts",
"pretest:e2e:obsidian:p2p-connection-check": "npm run build && npm run build --workspace webpeer",
+8 -3
View File
@@ -77,9 +77,10 @@ npm run test:e2e:obsidian:focused -- settings-ui
npm run test:e2e:obsidian:focused -- two-vault-sync
npm run test:e2e:obsidian:focused -- security-seed-reconnect
npm run test:e2e:obsidian:focused -- adaptive-webdav --manage-webdav
npm run test:e2e:obsidian:focused -- adaptive-postgrest --manage-postgrest
```
The wrapper accepts only maintained real-Obsidian scenario names; run it with `--help` for the current list. It deliberately does not manage CouchDB, Object Storage, WebDAV, or the P2P signalling relay unless a focused scenario exposes its own explicit service-management argument. Start the required fixture first, pass `--manage-webdav` to the Adaptive WebDAV scenario, or use the complete service-managed suite.
The wrapper accepts only maintained real-Obsidian scenario names; run it with `--help` for the current list. It deliberately does not manage CouchDB, Object Storage, PostgREST, WebDAV, or the P2P signalling relay unless a focused scenario exposes its own explicit service-management argument. Start the required fixture first, pass the provider's service-management argument to the Adaptive WebDAV or PostgREST scenario, or use the complete service-managed suite.
The principal entry points are:
@@ -115,7 +116,7 @@ The mobile pass uses Obsidian's `app.emulateMobile(true)`, a 390 by 844 CSS-pixe
`test:e2e:obsidian:p2p-pane` starts one configured CouchDB-only session with no P2P profile and separate configured P2P sessions for desktop and mobile. It proves that the command remains registered while the retired command, automatic pane, and ribbon entry without a P2P configuration are absent. For the configured P2P profiles, it verifies that the desktop ribbon is available, the current status command reaches the pane without it opening at start-up, checks its connection control and horizontal layout, and captures unobstructed desktop and mobile screenshots. The mobile session uses a fresh Vault, profile, and Obsidian process, enters `app.emulateMobile(true)` through `lifecycle.beforePluginStart`, and requires the P2P view to belong to the right drawer rather than inheriting desktop workspace state. It deliberately uses no relay or peer: replacement of the active replicator is covered by focused unit tests, the Deno and Compose CLI P2P lifecycle suite covers the headless transport, and `p2p-setup-uri-workflow` owns the visible transfer path between two real Obsidian sessions.
`test:e2e:obsidian:local-suite` builds the plug-in and, unless `LIVESYNC_CLI_COMMAND` selects an external CLI, the local LiveSync CLI. It then runs discovery, smoke, the onboarding invitation, Svelte dialogue mounting, revision repair, settings UI, the remote-setup provider flow, the Review Harness, the P2P status pane, Vault reflection, CouchDB upload and manual setup, CLI-to-Obsidian synchronisation, Object Storage upload, the Adaptive WebDAV workflow, Object Storage and P2P Setup URI round trips, startup scan, provisioned CouchDB Setup URI, two-vault synchronisation, Hidden File Sync, Customisation Sync, and setting Markdown export in sequence. Start the local CouchDB, MinIO, Apache WebDAV, and P2P relay fixtures before running it, or use `test:e2e:obsidian:local-suite:services` to let the wrapper stop leftover fixtures, start fresh fixtures, and stop them again after the run.
`test:e2e:obsidian:local-suite` builds the plug-in and, unless `LIVESYNC_CLI_COMMAND` selects an external CLI, the local LiveSync CLI. It then runs discovery, smoke, the onboarding invitation, Svelte dialogue mounting, revision repair, settings UI, the remote-setup provider flow, the Review Harness, the P2P status pane, Vault reflection, CouchDB upload and manual setup, CLI-to-Obsidian synchronisation, Object Storage upload, the Adaptive WebDAV and PostgREST workflows, Object Storage and P2P Setup URI round trips, startup scan, provisioned CouchDB Setup URI, two-vault synchronisation, Hidden File Sync, Customisation Sync, and setting Markdown export in sequence. Start the local CouchDB, MinIO, Apache WebDAV, PostgreSQL/PostgREST, and P2P relay fixtures before running it, or use `test:e2e:obsidian:local-suite:services` to let the wrapper stop leftover fixtures, start fresh fixtures, and stop them again after the run.
`test:e2e:obsidian:couchdb-upload` reuses the CouchDB variables from `.test.env` or the process environment. It expects a reachable CouchDB service, creates a unique database, starts from configured plug-in data without the device-local compatibility marker, and verifies the copied-or-restored Vault explanation in the actual compatibility dialogue. It captures the summary and details, resumes explicitly, confirms that the marker was recorded, creates a note in real Obsidian, commits the note into the local database, runs one-shot synchronisation, and verifies that the remote database contains both the metadata document and its chunk documents.
@@ -149,6 +150,9 @@ 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:adaptive-webdav` uses visible manual onboarding to configure a unique collection on a reachable WebDAV endpoint. It requires the endpoint safety check before onboarding can continue, retains the WebDAV profile across a real Obsidian restart, generates a Setup URI on that working device, and completes Fast Fetch and a text and binary return journey through a second device. This representative object-store journey exercises Obsidian's internal request bridge and verifies user-observable Vault reflection. Commonlib integration tests own the Adaptive remote layout, capability semantics, and failure matrix. Run `test:e2e:obsidian:adaptive-webdav:services`, or pass `--manage-webdav` through the focused wrapper, to start and stop the shared local Apache fixture around this scenario.
`test:e2e:obsidian:adaptive-postgrest` applies the two-device real-Obsidian workflow to the Adaptive-only PostgREST provider. Visible manual onboarding must withhold continuation until the packaged RPC and binary-semantics check succeeds, and it must not expose Opaque or Pack retrieval choices. The first device retains its profile across a real Obsidian restart, generates a Setup URI for the second device, and completes Fast Fetch and a text and binary return journey. This representative native-store journey verifies Host composition and user-observable Vault reflection. Commonlib integration tests own the RPC failure matrix, SQL transaction semantics, repository binding, and remote layout. Rebuild resets all Adaptive data for the supplied Vault ID, so external settings must identify a disposable test Vault. Run `test:e2e:obsidian:adaptive-postgrest:services`, or pass `--manage-postgrest` through the focused wrapper, to start and stop the shared PostgreSQL and PostgREST fixture around this scenario.
`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: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.
@@ -201,6 +205,7 @@ Start the local fixtures first when they are not already running:
npm run test:docker-couchdb:start
npm run test:docker-s3:start
npm run test:docker-webdav:start
npm run test:docker-postgrest:start
npm run test:docker-p2p:start
npm run test:e2e:obsidian:local-suite
```
@@ -249,7 +254,7 @@ Useful environment variables:
- `E2E_OBSIDIAN_REMOTE_ACTIVITY_TIMEOUT_MS`: timeout for an observed remote activity to enter or leave its status boundary; default is 30 seconds.
- `E2E_OBSIDIAN_DIAGNOSTICS_DIR`: directory for screenshots and status snapshots, including the Security Seed reconnect stages; default is `/tmp/obsidian-livesync-e2e`.
- `E2E_OBSIDIAN_OBJECT_STORAGE_TIMEOUT_MS`: timeout for waiting until Object Storage contains uploaded E2E objects.
- `E2E_OBSIDIAN_WEBDAV_TIMEOUT_MS`: timeout for waiting until the Adaptive WebDAV collection contains the expected immutable objects; default is 30 seconds.
- `POSTGREST_ENDPOINT`, `POSTGREST_SCHEMA`, `POSTGREST_VAULT_ID`, `POSTGREST_VAULT_CREDENTIAL`, and `POSTGREST_API_KEY`: external PostgREST fixture settings. The API key must be publishable or otherwise client-safe. The managed fixture supplies disposable defaults.
- `E2E_OBSIDIAN_KEEP_COUCHDB=true`: keep the temporary CouchDB database for inspection.
- `E2E_OBSIDIAN_KEEP_OBJECT_STORAGE=true`: keep the temporary Object Storage prefix for inspection.
- `E2E_OBSIDIAN_KEEP_WEBDAV=true`: keep the temporary WebDAV collection for inspection while the endpoint remains available.
+2
View File
@@ -24,6 +24,7 @@ export type SetupState = {
p2pEnabled: boolean;
p2pRelays: string;
p2pRoomId: string;
postgrestActiveConnectionURI: string;
webDAVactiveConnectionURI: string;
};
@@ -352,6 +353,7 @@ export async function readSetupState(cliBinary: string, environment: NodeJS.Proc
"p2pEnabled:settings.P2P_Enabled===true,",
"p2pRelays:settings.P2P_relays||'',",
"p2pRoomId:settings.P2P_roomID||'',",
"postgrestActiveConnectionURI:settings.postgrestActiveConnectionURI||'',",
"webDAVactiveConnectionURI:settings.webDAVactiveConnectionURI||'',",
"});",
"})()",
@@ -0,0 +1,262 @@
/**
* Exercises the Adaptive-only PostgREST Journal provider through visible,
* safety-gated onboarding. The shared Adaptive Journal runner owns restart
* persistence, device-generated Setup URI transfer, Fast Fetch, and a
* two-device text and binary return journey.
*/
import { parsePostgRESTConnectionURI } from "@vrtmrz/livesync-commonlib/journal-storage";
import { runAdaptiveJournalObsidianRoundTrip, runNpmScript } from "../runner/adaptiveJournal.ts";
import { assertEqual } from "../runner/liveSyncWorkflow.ts";
import { captureGuideDialogue, modalByTitle, selectRadioOption, type SetupState } from "../runner/setupUri.ts";
import { withObsidianPage } from "../runner/ui.ts";
type PostgRESTConfig = {
apiKey: string;
endpoint: string;
schema: string;
vaultCredential: string;
vaultId: string;
};
const supportedArguments = new Set(["--keep-postgrest", "--manage-postgrest"]);
const unsupportedArguments = process.argv.slice(2).filter((argument) => !supportedArguments.has(argument));
if (unsupportedArguments.length > 0) {
throw new Error(`Unsupported Adaptive PostgREST argument: ${unsupportedArguments.join(", ")}`);
}
const managePostgREST = process.argv.includes("--manage-postgrest");
const keepPostgRESTFixture = process.argv.includes("--keep-postgrest");
const uiTimeoutMs = Number(process.env.E2E_OBSIDIAN_SETUP_URI_TIMEOUT_MS ?? 30000);
function environmentValue(primary: string, legacy: string, fallback: string): string {
return process.env[primary] ?? process.env[legacy] ?? fallback;
}
function loadPostgRESTConfig(): PostgRESTConfig {
const endpoint = environmentValue("POSTGREST_ENDPOINT", "postgrestEndpoint", "http://127.0.0.1:3001").replace(
/\/+$/u,
""
);
const endpointUrl = new URL(endpoint);
if (
(endpointUrl.protocol !== "http:" && endpointUrl.protocol !== "https:") ||
endpointUrl.username !== "" ||
endpointUrl.password !== "" ||
endpointUrl.search !== "" ||
endpointUrl.hash !== ""
) {
throw new Error("Adaptive PostgREST requires a complete HTTP or HTTPS endpoint without embedded credentials.");
}
const config = {
apiKey: environmentValue("POSTGREST_API_KEY", "postgrestApiKey", ""),
endpoint,
schema: environmentValue("POSTGREST_SCHEMA", "postgrestSchema", "livesync_api"),
vaultCredential: environmentValue(
"POSTGREST_VAULT_CREDENTIAL",
"postgrestVaultCredential",
"adaptive-cli-vault-credential-0000000000001"
),
vaultId: environmentValue("POSTGREST_VAULT_ID", "postgrestVaultId", "adaptive-cli-vault-01"),
};
if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(config.schema)) {
throw new Error("Adaptive PostgREST requires a valid exposed schema identifier.");
}
if (!/^[A-Za-z0-9_-]{16,128}$/u.test(config.vaultId) || config.vaultCredential.length === 0) {
throw new Error("Adaptive PostgREST requires a provisioned Vault ID and Vault credential.");
}
return config;
}
function postgRESTHeaders(config: PostgRESTConfig): Headers {
const headers = new Headers({
Accept: "application/json",
"Accept-Profile": config.schema,
"Content-Profile": config.schema,
"X-LiveSync-Vault-Credential": config.vaultCredential,
"X-LiveSync-Vault-ID": config.vaultId,
});
if (config.apiKey) headers.set("apikey", config.apiKey);
return headers;
}
async function readEstimatedSize(config: PostgRESTConfig): Promise<number> {
const response = await fetch(`${config.endpoint}/rpc/livesync_adaptive_status`, {
headers: postgRESTHeaders(config),
method: "GET",
signal: AbortSignal.timeout(5000),
});
if (!response.ok) {
await response.body?.cancel().catch(() => undefined);
throw new Error(`Adaptive PostgREST status failed with HTTP ${response.status}.`);
}
const value = (await response.json()) as unknown;
const body = Array.isArray(value) && value.length === 1 ? value[0] : value;
const estimatedSize = Number(
body && typeof body === "object" && !Array.isArray(body)
? (body as { estimated_size?: unknown }).estimated_size
: Number.NaN
);
if (!Number.isFinite(estimatedSize) || estimatedSize < 0) {
throw new Error("Adaptive PostgREST status returned an invalid estimated size.");
}
return estimatedSize;
}
async function assertPostgRESTReachable(config: PostgRESTConfig): Promise<void> {
await readEstimatedSize(config);
}
async function enterManualAdaptivePostgRESTSettings(
port: number,
config: PostgRESTConfig,
vaultPassphrase: string
): Promise<string[]> {
const screenshots: string[] = [];
await withObsidianPage(port, async (page) => {
const invitation = page.locator(".notice").filter({ hasText: "Welcome to Self-hosted LiveSync" });
await invitation.waitFor({ state: "visible", timeout: uiTimeoutMs });
await invitation.locator(".sls-onboarding-invitation-action").click({ timeout: uiTimeoutMs });
const intro = modalByTitle(page, "Welcome to Self-hosted LiveSync");
await intro.waitFor({ state: "visible", timeout: uiTimeoutMs });
await selectRadioOption(intro, "I am setting this up for the first time");
await intro
.getByRole("button", { name: "Yes, I want to set up a new synchronisation" })
.click({ timeout: uiTimeoutMs });
const method = modalByTitle(page, "Connection Method");
await method.waitFor({ state: "visible", timeout: uiTimeoutMs });
await selectRadioOption(method, "Configure a remote manually");
await method.getByRole("button", { name: "Proceed with manual configuration" }).click({ timeout: uiTimeoutMs });
const encryption = modalByTitle(page, "End-to-End Encryption");
await encryption.waitFor({ state: "visible", timeout: uiTimeoutMs });
await encryption
.locator("label.row")
.filter({ hasText: "End-to-End Encryption" })
.locator('input[type="checkbox"]')
.first()
.check({ timeout: uiTimeoutMs });
await encryption.locator('input[name="e2ee-passphrase"]').fill(vaultPassphrase);
});
screenshots.push(
await captureGuideDialogue(port, "guide-adaptive-postgrest-encryption.png", "End-to-End Encryption")
);
await withObsidianPage(port, async (page) => {
await modalByTitle(page, "End-to-End Encryption")
.getByRole("button", { name: "Proceed", exact: true })
.click({ timeout: uiTimeoutMs });
const remoteSelection = modalByTitle(page, "Choose a synchronisation remote");
await remoteSelection.waitFor({ state: "visible", timeout: uiTimeoutMs });
await selectRadioOption(remoteSelection, "PostgREST Journal");
await remoteSelection
.getByRole("button", { name: "Continue to PostgREST setup", exact: true })
.click({ timeout: uiTimeoutMs });
const postgRESTModal = modalByTitle(page, "PostgREST Journal Configuration");
await postgRESTModal.waitFor({ state: "visible", timeout: uiTimeoutMs });
await postgRESTModal.locator('input[name="postgrest-endpoint"]').fill(config.endpoint);
await postgRESTModal.locator('input[name="postgrest-vault-id"]').fill(config.vaultId);
await postgRESTModal.locator('input[name="postgrest-vault-credential"]').fill(config.vaultCredential);
await postgRESTModal.locator('input[name="postgrest-schema"]').fill(config.schema);
if (config.apiKey) await postgRESTModal.locator('input[name="postgrest-api-key"]').fill(config.apiKey);
await postgRESTModal.locator('input[name="postgrest-use-internal-api"]').check({ timeout: uiTimeoutMs });
if (
(await postgRESTModal.locator('select[name="postgrest-journal-format"]').count()) !== 0 ||
(await postgRESTModal.locator('select[name="postgrest-pack-read-policy"]').count()) !== 0
) {
throw new Error("Adaptive-only PostgREST onboarding exposed an Opaque or Pack retrieval choice.");
}
if (
(await postgRESTModal
.getByRole("button", { name: "Continue with verified settings", exact: true })
.count()) !== 0
) {
throw new Error("Adaptive PostgREST could continue before its server safety check completed.");
}
await postgRESTModal
.getByRole("button", { name: "Check PostgREST server", exact: true })
.click({ timeout: uiTimeoutMs });
await postgRESTModal
.getByText("The required PostgREST RPC operations and binary semantics were verified.", { exact: false })
.waitFor({ state: "visible", timeout: uiTimeoutMs });
await postgRESTModal
.getByRole("button", { name: "Continue with verified settings", exact: true })
.waitFor({ state: "visible", timeout: uiTimeoutMs });
if (
(await postgRESTModal.getByRole("button", { name: "Save without connecting", exact: true }).count()) !== 0
) {
throw new Error("Adaptive PostgREST onboarding exposed the unverified Settings-only save action.");
}
});
screenshots.push(
await captureGuideDialogue(port, "guide-adaptive-postgrest-safety-check.png", "PostgREST Journal Configuration")
);
await withObsidianPage(port, async (page) => {
const postgRESTModal = modalByTitle(page, "PostgREST Journal Configuration");
await postgRESTModal
.getByRole("button", { name: "Continue with verified settings", exact: true })
.click({ timeout: uiTimeoutMs });
await modalByTitle(page, "Setup Complete: Preparing to Initialise Server").waitFor({
state: "visible",
timeout: uiTimeoutMs,
});
});
return screenshots;
}
function assertAdaptivePostgRESTSettings(state: SetupState, config: PostgRESTConfig, label: string): void {
assertEqual(state.remoteType, "POSTGREST", `${label} did not retain PostgREST as its active remote.`);
assertEqual(state.journalFormat, "adaptive-v1", `${label} did not retain the Adaptive Journal format.`);
assertEqual(state.packReadPolicy, "whole-pack", `${label} did not retain complete Pack retrieval.`);
assertEqual(state.remoteConfigurationCount, 1, `${label} did not retain exactly one remote profile.`);
const connection = parsePostgRESTConnectionURI(state.postgrestActiveConnectionURI);
assertEqual(connection.endpoint, config.endpoint, `${label} retained a different PostgREST endpoint.`);
assertEqual(connection.schema, config.schema, `${label} retained a different PostgREST schema.`);
if (connection.vaultId !== config.vaultId || connection.vaultCredential !== config.vaultCredential) {
throw new Error(`${label} did not retain the provisioned PostgREST Vault ID and Vault credential.`);
}
if (connection.apiKey !== config.apiKey) {
throw new Error(`${label} did not retain the configured PostgREST client API key.`);
}
assertEqual(
connection.useCustomRequestHandler,
true,
`${label} did not retain the Obsidian internal request API selection.`
);
}
async function main(): Promise<void> {
const config = loadPostgRESTConfig();
let shouldStopPostgREST = false;
try {
if (managePostgREST) {
await runNpmScript("test:docker-postgrest:start");
shouldStopPostgREST = !keepPostgRESTFixture;
}
await assertPostgRESTReachable(config);
await runAdaptiveJournalObsidianRoundTrip({
label: "Adaptive PostgREST",
slug: "adaptive-postgrest",
targetDescription: `Temporary PostgREST target: ${config.endpoint}`,
enterManualSettings: async (port, vaultPassphrase) =>
await enterManualAdaptivePostgRESTSettings(port, config, vaultPassphrase),
assertSettings: (state, label) => assertAdaptivePostgRESTSettings(state, config, label),
});
} finally {
if (shouldStopPostgREST) {
await runNpmScript("test:docker-postgrest:stop").catch((error: unknown) => {
console.warn(error instanceof Error ? error.message : error);
});
}
}
}
main().catch((error: unknown) => {
console.error(error instanceof Error ? error.stack : error);
process.exit(1);
});
+20
View File
@@ -32,6 +32,7 @@ const testSteps: Step[] = [
},
{ name: "Object Storage upload", args: ["run", "test:e2e:obsidian:minio-upload"] },
{ name: "Adaptive WebDAV workflow", args: ["run", "test:e2e:obsidian:adaptive-webdav"] },
{ name: "Adaptive PostgREST workflow", args: ["run", "test:e2e:obsidian:adaptive-postgrest"] },
{
name: "Object Storage Setup URI workflow",
args: ["run", "test:e2e:obsidian:object-storage-setup-uri-workflow"],
@@ -49,11 +50,13 @@ const manageCouchDb = process.argv.includes("--manage-couchdb") || process.argv.
const manageMinio = process.argv.includes("--manage-minio") || process.argv.includes("--manage-services");
const manageP2P = process.argv.includes("--manage-p2p") || process.argv.includes("--manage-services");
const manageWebDAV = process.argv.includes("--manage-webdav") || process.argv.includes("--manage-services");
const managePostgREST = process.argv.includes("--manage-postgrest") || process.argv.includes("--manage-services");
const keepServices = process.argv.includes("--keep-services");
const keepCouchDb = keepServices || process.argv.includes("--keep-couchdb");
const keepMinio = keepServices || process.argv.includes("--keep-minio");
const keepP2P = keepServices || process.argv.includes("--keep-p2p");
const keepWebDAV = keepServices || process.argv.includes("--keep-webdav");
const keepPostgREST = keepServices || process.argv.includes("--keep-postgrest");
function npmBinary(): string {
return process.platform === "win32" ? "npm.cmd" : "npm";
@@ -116,11 +119,20 @@ async function stopManagedWebDAV(): Promise<void> {
});
}
async function stopManagedPostgREST(): Promise<void> {
await runStep({
name: "stop PostgREST fixture",
args: ["run", "test:docker-postgrest:stop"],
optional: true,
});
}
async function main(): Promise<void> {
let shouldStopCouchDb = false;
let shouldStopMinio = false;
let shouldStopP2P = false;
let shouldStopWebDAV = false;
let shouldStopPostgREST = false;
try {
if (manageCouchDb) {
await stopManagedCouchDb();
@@ -142,11 +154,19 @@ async function main(): Promise<void> {
await runStep({ name: "start WebDAV fixture", args: ["run", "test:docker-webdav:start"] });
shouldStopWebDAV = !keepWebDAV;
}
if (managePostgREST) {
await stopManagedPostgREST();
await runStep({ name: "start PostgREST fixture", args: ["run", "test:docker-postgrest:start"] });
shouldStopPostgREST = !keepPostgREST;
}
for (const step of testSteps) {
await runStep(step);
}
} finally {
if (shouldStopPostgREST) {
await stopManagedPostgREST();
}
if (shouldStopWebDAV) {
await stopManagedWebDAV();
}
+2 -1
View File
@@ -20,6 +20,7 @@ const focusedScenarios = new Set([
"cli-to-obsidian-sync",
"minio-upload",
"adaptive-webdav",
"adaptive-postgrest",
"object-storage-setup-uri-workflow",
"p2p-setup-uri-workflow",
"startup-scan",
@@ -40,7 +41,7 @@ real-Obsidian scenario. Supported scenarios:
${[...focusedScenarios].map((scenario) => ` ${scenario}`).join("\n")}
This wrapper does not start CouchDB, Object Storage, WebDAV, or the P2P signalling
This wrapper does not start CouchDB, Object Storage, PostgREST, WebDAV, or the P2P signalling
relay. Use the documented service commands or the complete
local-suite:services wrapper when required.`;
}