test(obsidian): verify WebDAV repository identity

This commit is contained in:
vorotamoroz
2026-08-02 07:57:39 +00:00
parent d6fd2fa4c8
commit c76159ea50
2 changed files with 35 additions and 11 deletions
+2
View File
@@ -18,6 +18,7 @@ export type SetupState = {
endpoint: string; endpoint: string;
bucket: string; bucket: string;
bucketPrefix: string; bucketPrefix: string;
expectedRepositoryId: string;
journalFormat: string; journalFormat: string;
packReadPolicy: string; packReadPolicy: string;
p2pEnabled: boolean; p2pEnabled: boolean;
@@ -345,6 +346,7 @@ export async function readSetupState(cliBinary: string, environment: NodeJS.Proc
"endpoint:settings.endpoint||'',", "endpoint:settings.endpoint||'',",
"bucket:settings.bucket||'',", "bucket:settings.bucket||'',",
"bucketPrefix:settings.bucketPrefix||'',", "bucketPrefix:settings.bucketPrefix||'',",
"expectedRepositoryId:settings.expectedRepositoryId||'',",
"journalFormat:settings.journalFormat||'',", "journalFormat:settings.journalFormat||'',",
"packReadPolicy:settings.packReadPolicy||'',", "packReadPolicy:settings.packReadPolicy||'',",
"p2pEnabled:settings.P2P_Enabled===true,", "p2pEnabled:settings.P2P_Enabled===true,",
+33 -11
View File
@@ -254,10 +254,22 @@ async function enterManualAdaptiveWebDAVSettings(
return screenshots; return screenshots;
} }
function assertAdaptiveWebDAVSettings(state: SetupState, webDAV: WebDAVConfig, prefix: string, label: string): void { function assertAdaptiveWebDAVSettings(
state: SetupState,
webDAV: WebDAVConfig,
prefix: string,
label: string,
expectedRepositoryId?: string
): string {
assertEqual(state.remoteType, "WEBDAV", `${label} did not retain WebDAV as its active remote.`); assertEqual(state.remoteType, "WEBDAV", `${label} did not retain WebDAV as its active remote.`);
assertEqual(state.journalFormat, "adaptive-v1", `${label} did not retain the Adaptive Journal format.`); 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.packReadPolicy, "whole-pack", `${label} did not retain complete Pack retrieval.`);
if (!/^[A-Za-z0-9_-]{43}$/u.test(state.expectedRepositoryId)) {
throw new Error(`${label} did not retain a canonical repository ID.`);
}
if (expectedRepositoryId !== undefined) {
assertEqual(state.expectedRepositoryId, expectedRepositoryId, `${label} retained a different repository ID.`);
}
assertEqual(state.remoteConfigurationCount, 1, `${label} did not retain exactly one remote profile.`); assertEqual(state.remoteConfigurationCount, 1, `${label} did not retain exactly one remote profile.`);
const connection = parseWebDAVConnectionURI(state.webDAVactiveConnectionURI); const connection = parseWebDAVConnectionURI(state.webDAVactiveConnectionURI);
assertEqual(connection.endpoint, webDAV.endpoint, `${label} retained a different WebDAV endpoint.`); assertEqual(connection.endpoint, webDAV.endpoint, `${label} retained a different WebDAV endpoint.`);
@@ -269,6 +281,7 @@ function assertAdaptiveWebDAVSettings(state: SetupState, webDAV: WebDAVConfig, p
true, true,
`${label} did not retain the Obsidian internal request API selection.` `${label} did not retain the Obsidian internal request API selection.`
); );
return state.expectedRepositoryId;
} }
function deterministicBytes(length: number, seed: number): Uint8Array { function deterministicBytes(length: number, seed: number): Uint8Array {
@@ -390,7 +403,12 @@ async function waitForAdaptiveObjects(
); );
} }
async function assertAdaptiveRepository(webDAV: WebDAVConfig, prefix: string, keys: string[]): Promise<void> { async function assertAdaptiveRepository(
webDAV: WebDAVConfig,
prefix: string,
keys: string[],
expectedRepositoryId: string
): Promise<void> {
for (const legacyPrefix of ["a1~delta~", "a1~index~", "a1~metadata~"]) { for (const legacyPrefix of ["a1~delta~", "a1~index~", "a1~metadata~"]) {
if (keys.some((key) => key.startsWith(legacyPrefix))) { if (keys.some((key) => key.startsWith(legacyPrefix))) {
throw new Error(`Adaptive WebDAV wrote a retired object family ${legacyPrefix}: ${keys.join(", ")}`); throw new Error(`Adaptive WebDAV wrote a retired object family ${legacyPrefix}: ${keys.join(", ")}`);
@@ -412,9 +430,11 @@ async function assertAdaptiveRepository(webDAV: WebDAVConfig, prefix: string, ke
assertEqual(manifest.format, "adaptive-journal", "Unexpected Adaptive WebDAV manifest format."); assertEqual(manifest.format, "adaptive-journal", "Unexpected Adaptive WebDAV manifest format.");
assertEqual(manifest.formatVersion, 1, "Unexpected Adaptive WebDAV manifest version."); assertEqual(manifest.formatVersion, 1, "Unexpected Adaptive WebDAV manifest version.");
assertEqual(manifest.objectLayout, "commit-bundle-v1", "Unexpected Adaptive WebDAV object layout."); assertEqual(manifest.objectLayout, "commit-bundle-v1", "Unexpected Adaptive WebDAV object layout.");
if (typeof manifest.repositoryId !== "string" || manifest.repositoryId.length === 0) { assertEqual(
throw new Error("Adaptive WebDAV manifest did not contain a repository ID."); manifest.repositoryId,
} expectedRepositoryId,
"Adaptive WebDAV did not create the repository with the preselected identity."
);
if (typeof manifest.manifestAuth !== "string" || manifest.manifestAuth.length === 0) { if (typeof manifest.manifestAuth !== "string" || manifest.manifestAuth.length === 0) {
throw new Error("Adaptive WebDAV manifest did not contain its authentication value."); throw new Error("Adaptive WebDAV manifest did not contain its authentication value.");
} }
@@ -435,6 +455,7 @@ async function main(): Promise<void> {
let generatedSetup: SetupArtifact | undefined; let generatedSetup: SetupArtifact | undefined;
let shouldStopWebDAV = false; let shouldStopWebDAV = false;
let observedRequests = 0; let observedRequests = 0;
let repositoryId = "";
try { try {
if (manageWebDAV) { if (manageWebDAV) {
@@ -463,12 +484,12 @@ async function main(): Promise<void> {
screenshots.push(await acknowledgeDisabledOptionalFeatures(session.remoteDebuggingPort, captures)); screenshots.push(await acknowledgeDisabledOptionalFeatures(session.remoteDebuggingPort, captures));
const state = await finishInitialisation(session.remoteDebuggingPort, context.cliBinary, session.cliEnv); const state = await finishInitialisation(session.remoteDebuggingPort, context.cliBinary, session.cliEnv);
await resumeCompatibilityReviewIfShown(session.remoteDebuggingPort); await resumeCompatibilityReviewIfShown(session.remoteDebuggingPort);
assertAdaptiveWebDAVSettings(state, webDAV, prefix, "The first device"); repositoryId = assertAdaptiveWebDAVSettings(state, webDAV, prefix, "The first device");
await writePayloadViaObsidian(context.cliBinary, session.cliEnv, firstText, firstBinarySeed); await writePayloadViaObsidian(context.cliBinary, session.cliEnv, firstText, firstBinarySeed);
observedRequests += await pushAndObserve(session, context.cliBinary); observedRequests += await pushAndObserve(session, context.cliBinary);
const keys = await waitForAdaptiveObjects(webDAV, prefix, 1, 1); const keys = await waitForAdaptiveObjects(webDAV, prefix, 1, 1);
await assertAdaptiveRepository(webDAV, prefix, keys); await assertAdaptiveRepository(webDAV, prefix, keys, repositoryId);
} catch (error) { } catch (error) {
await captureFailure(session, "first-device"); await captureFailure(session, "first-device");
throw error; throw error;
@@ -481,7 +502,7 @@ async function main(): Promise<void> {
await waitForLiveSyncCoreReady(context.cliBinary, session.cliEnv); await waitForLiveSyncCoreReady(context.cliBinary, session.cliEnv);
await resumeCompatibilityReviewIfShown(session.remoteDebuggingPort); await resumeCompatibilityReviewIfShown(session.remoteDebuggingPort);
const state = await readSetupState(context.cliBinary, session.cliEnv); const state = await readSetupState(context.cliBinary, session.cliEnv);
assertAdaptiveWebDAVSettings(state, webDAV, prefix, "The restarted first device"); assertAdaptiveWebDAVSettings(state, webDAV, prefix, "The restarted first device", repositoryId);
const generated = await generateSetupURIFromDevice( const generated = await generateSetupURIFromDevice(
session.remoteDebuggingPort, session.remoteDebuggingPort,
randomBytes(24).toString("base64url"), randomBytes(24).toString("base64url"),
@@ -512,7 +533,7 @@ async function main(): Promise<void> {
screenshots.push(await skipMissingRemoteConfiguration(session.remoteDebuggingPort, secondDeviceCaptures)); screenshots.push(await skipMissingRemoteConfiguration(session.remoteDebuggingPort, secondDeviceCaptures));
const state = await finishInitialisation(session.remoteDebuggingPort, context.cliBinary, session.cliEnv); const state = await finishInitialisation(session.remoteDebuggingPort, context.cliBinary, session.cliEnv);
await resumeCompatibilityReviewIfShown(session.remoteDebuggingPort); await resumeCompatibilityReviewIfShown(session.remoteDebuggingPort);
assertAdaptiveWebDAVSettings(state, webDAV, prefix, "The second device"); assertAdaptiveWebDAVSettings(state, webDAV, prefix, "The second device", repositoryId);
observedRequests += await pushAndObserve(session, context.cliBinary); observedRequests += await pushAndObserve(session, context.cliBinary);
await waitForText(vaultB, firstText); await waitForText(vaultB, firstText);
await waitForBinary(vaultB, deterministicBytes(binaryLength, firstBinarySeed)); await waitForBinary(vaultB, deterministicBytes(binaryLength, firstBinarySeed));
@@ -520,7 +541,7 @@ async function main(): Promise<void> {
await writePayloadViaObsidian(context.cliBinary, session.cliEnv, secondText, secondBinarySeed); await writePayloadViaObsidian(context.cliBinary, session.cliEnv, secondText, secondBinarySeed);
observedRequests += await pushAndObserve(session, context.cliBinary); observedRequests += await pushAndObserve(session, context.cliBinary);
const keys = await waitForAdaptiveObjects(webDAV, prefix, 2, 2); const keys = await waitForAdaptiveObjects(webDAV, prefix, 2, 2);
await assertAdaptiveRepository(webDAV, prefix, keys); await assertAdaptiveRepository(webDAV, prefix, keys, repositoryId);
} catch (error) { } catch (error) {
await captureFailure(session, "second-device"); await captureFailure(session, "second-device");
throw error; throw error;
@@ -536,7 +557,8 @@ async function main(): Promise<void> {
await readSetupState(context.cliBinary, session.cliEnv), await readSetupState(context.cliBinary, session.cliEnv),
webDAV, webDAV,
prefix, prefix,
"The final first-device session" "The final first-device session",
repositoryId
); );
observedRequests += await pushAndObserve(session, context.cliBinary); observedRequests += await pushAndObserve(session, context.cliBinary);
await waitForText(vaultA, secondText); await waitForText(vaultA, secondText);