Reorder params and add error
This commit is contained in:
parent
3177691e13
commit
85bb96a44e
6 changed files with 23 additions and 14 deletions
|
|
@ -19,6 +19,13 @@ export class AuthenticationError extends Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ConflictingPathError extends Error {
|
||||||
|
public constructor(path: string) {
|
||||||
|
super(`Conflicting path: ${path}`);
|
||||||
|
this.name = "ConflictingPathError";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class PermanentSyncError extends Error {}
|
export class PermanentSyncError extends Error {}
|
||||||
|
|
||||||
export class LocalChangesDuringReconciliation extends Error {}
|
export class LocalChangesDuringReconciliation extends Error {}
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,10 @@ test("current state keeps absent optional fields absent across save and reload",
|
||||||
const restart = (): Database =>
|
const restart = (): Database =>
|
||||||
new Database(
|
new Database(
|
||||||
saved,
|
saved,
|
||||||
|
"vault",
|
||||||
async (next) => {
|
async (next) => {
|
||||||
saved = structuredClone(next);
|
saved = structuredClone(next);
|
||||||
},
|
},
|
||||||
"vault",
|
|
||||||
async () => structuredClone(saved)
|
async () => structuredClone(saved)
|
||||||
);
|
);
|
||||||
const db = restart();
|
const db = restart();
|
||||||
|
|
@ -36,10 +36,10 @@ test("current reset state is restored after an uncertain save", async () => {
|
||||||
};
|
};
|
||||||
const db = new Database(
|
const db = new Database(
|
||||||
undefined,
|
undefined,
|
||||||
|
"vault",
|
||||||
async () => {
|
async () => {
|
||||||
throw new Error("interrupted");
|
throw new Error("interrupted");
|
||||||
},
|
},
|
||||||
"vault",
|
|
||||||
async () => structuredClone(saved)
|
async () => structuredClone(saved)
|
||||||
);
|
);
|
||||||
await assert.rejects(db.save(), /interrupted/);
|
await assert.rejects(db.save(), /interrupted/);
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,8 @@ export class Database {
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
initial: StoredDatabase | undefined,
|
initial: StoredDatabase | undefined,
|
||||||
private readonly saveData: (data: StoredDatabase) => Promise<void>,
|
|
||||||
vaultKey: string,
|
vaultKey: string,
|
||||||
|
private readonly saveData: (data: StoredDatabase) => Promise<void>,
|
||||||
private readonly loadData: () => Promise<StoredDatabase | undefined>
|
private readonly loadData: () => Promise<StoredDatabase | undefined>
|
||||||
) {
|
) {
|
||||||
const state =
|
const state =
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,12 @@ for (const change of ["version", "identity", "path", "delete"] as const) {
|
||||||
const content = new TextEncoder().encode("same bytes");
|
const content = new TextEncoder().encode("same bytes");
|
||||||
const digest = await hash(content);
|
const digest = await hash(content);
|
||||||
const state = createEmptyDatabase("test");
|
const state = createEmptyDatabase("test");
|
||||||
state.local.a = "a.md";
|
state.actualFileManifest.a = "a.md";
|
||||||
state.documents.a = { base: { vaultUpdateId: 1, hash: digest } };
|
state.documents.a = { base: { vaultUpdateId: 1, hash: digest } };
|
||||||
const database = new Database(
|
const database = new Database(
|
||||||
state,
|
state,
|
||||||
async () => undefined,
|
|
||||||
"test",
|
"test",
|
||||||
|
async () => undefined,
|
||||||
async () => undefined
|
async () => undefined
|
||||||
);
|
);
|
||||||
const sent: { documentsWithCursors: DocumentWithCursors[] }[] = [];
|
const sent: { documentsWithCursors: DocumentWithCursors[] }[] = [];
|
||||||
|
|
@ -39,17 +39,17 @@ for (const change of ["version", "identity", "path", "delete"] as const) {
|
||||||
read: async (): Promise<{ content: Uint8Array }> => {
|
read: async (): Promise<{ content: Uint8Array }> => {
|
||||||
const current = database.state;
|
const current = database.state;
|
||||||
assert.ok(current.documents.a?.base);
|
assert.ok(current.documents.a?.base);
|
||||||
assert.ok(current.local.a !== undefined);
|
assert.ok(current.actualFileManifest.a !== undefined);
|
||||||
if (change === "delete") {
|
if (change === "delete") {
|
||||||
delete current.local.a;
|
delete current.actualFileManifest.a;
|
||||||
} else if (change === "version") {
|
} else if (change === "version") {
|
||||||
current.documents.a.base.vaultUpdateId = 2;
|
current.documents.a.base.vaultUpdateId = 2;
|
||||||
} else if (change === "identity") {
|
} else if (change === "identity") {
|
||||||
current.local.replacement = current.local.a;
|
current.actualFileManifest.replacement = current.actualFileManifest.a;
|
||||||
current.documents.replacement = current.documents.a;
|
current.documents.replacement = current.documents.a;
|
||||||
delete current.local.a;
|
delete current.actualFileManifest.a;
|
||||||
} else {
|
} else {
|
||||||
current.local.a = "moved.md";
|
current.actualFileManifest.a = "moved.md";
|
||||||
}
|
}
|
||||||
|
|
||||||
return { content };
|
return { content };
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { spawnSync } from "node:child_process";
|
||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
import { pathToFileURL } from "node:url";
|
import { pathToFileURL } from "node:url";
|
||||||
import { test } from "node:test";
|
import { test } from "node:test";
|
||||||
|
import { ConflictingPathError } from "../errors/errors";
|
||||||
import {
|
import {
|
||||||
allocatePortablePath,
|
allocatePortablePath,
|
||||||
validatePortablePaths,
|
validatePortablePaths,
|
||||||
|
|
@ -87,7 +88,7 @@ test("shared directories remain distinct from aliases and file ancestors", () =>
|
||||||
]) {
|
]) {
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
validatePortablePaths(paths);
|
validatePortablePaths(paths);
|
||||||
}, /Conflicting path/);
|
}, ConflictingPathError);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -236,13 +237,13 @@ test("allocation resolves both ancestor and leaf collisions without changing sib
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("manifest conflicts throw TypeError", () => {
|
test("manifest conflicts throw ConflictingPathError", () => {
|
||||||
assert.throws(
|
assert.throws(
|
||||||
() => {
|
() => {
|
||||||
validatePortablePaths(["a.md", "A.md"]);
|
validatePortablePaths(["a.md", "A.md"]);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "TypeError",
|
name: "ConflictingPathError",
|
||||||
message: "Conflicting path: A.md"
|
message: "Conflicting path: A.md"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { ConflictingPathError } from "../errors/errors";
|
||||||
import type { DocumentId, RelativePath } from "../persistence/database";
|
import type { DocumentId, RelativePath } from "../persistence/database";
|
||||||
|
|
||||||
export const INTERNAL_DIRECTORY = ".vault-link-sync";
|
export const INTERNAL_DIRECTORY = ".vault-link-sync";
|
||||||
|
|
@ -78,7 +79,7 @@ export function validatePortablePaths(paths: Iterable<RelativePath>): void {
|
||||||
previous &&
|
previous &&
|
||||||
(previous.spelling !== spelling || previous.file || file)
|
(previous.spelling !== spelling || previous.file || file)
|
||||||
) {
|
) {
|
||||||
throw new TypeError(`Conflicting path: ${path}`);
|
throw new ConflictingPathError(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
const node = previous ?? { id: nodes.size + 1, spelling, file };
|
const node = previous ?? { id: nodes.size + 1, spelling, file };
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue