.
This commit is contained in:
parent
25f64ec690
commit
ff1f474324
1 changed files with 3271 additions and 2003 deletions
|
|
@ -3,7 +3,7 @@ import assert from "node:assert/strict";
|
|||
import { it } from "node:test";
|
||||
import {
|
||||
Database,
|
||||
emptyState,
|
||||
createEmptyDatabase,
|
||||
type RelativePath,
|
||||
type VaultUpdateId
|
||||
} from "../persistence/database";
|
||||
|
|
@ -15,7 +15,7 @@ import { hash } from "../utils/hash";
|
|||
import { CursorTracker } from "./cursor-tracker";
|
||||
|
||||
function createDatabase(digest: string): Database {
|
||||
const state = emptyState("test");
|
||||
const state = createEmptyDatabase("test");
|
||||
state.local.document = "note.md";
|
||||
state.documents.document = { base: { vaultUpdateId: 1, hash: digest } };
|
||||
return new Database(
|
||||
|
|
@ -35,7 +35,7 @@ it("keeps the last usable cursor while retaining only the latest future update",
|
|||
onRemoteCursorsUpdateReceived: new EventListeners<
|
||||
(cursors: ClientCursors[]) => Promise<void>
|
||||
>(),
|
||||
updateLocalCursors: () => {}
|
||||
updateLocalCursors: () => undefined
|
||||
} as unknown as WebSocketManager;
|
||||
const notifier = new EventListeners<(path: RelativePath) => unknown>();
|
||||
const tracker = new CursorTracker(
|
||||
|
|
@ -44,7 +44,9 @@ it("keeps the last usable cursor while retaining only the latest future update",
|
|||
{ read: async () => ({ content }) } as unknown as FileSystemOperations,
|
||||
notifier
|
||||
);
|
||||
context.after(() => tracker.reset());
|
||||
context.after(() => {
|
||||
tracker.reset();
|
||||
});
|
||||
let received: ClientCursors[] = [];
|
||||
tracker.onRemoteCursorsUpdated.add((cursors) => {
|
||||
received = cursors;
|
||||
|
|
@ -65,15 +67,17 @@ it("keeps the last usable cursor while retaining only the latest future update",
|
|||
await websocket.onRemoteCursorsUpdateReceived.triggerAsync([
|
||||
cursor(remoteVersion)
|
||||
]);
|
||||
assert.equal(received[0].documentsWithCursors[0].vault_update_id, 1);
|
||||
assert.equal(received[0]?.documentsWithCursors[0]?.vault_update_id, 1);
|
||||
}
|
||||
|
||||
assert.ok(database.state.documents.document);
|
||||
database.state.documents.document.base = {
|
||||
vaultUpdateId: 2,
|
||||
hash: contentHash
|
||||
};
|
||||
await notifier.triggerAsync("note.md");
|
||||
assert.equal(
|
||||
received[0].documentsWithCursors[0].vault_update_id,
|
||||
received[0]?.documentsWithCursors[0]?.vault_update_id,
|
||||
1,
|
||||
"superseded future cursors must not reappear"
|
||||
);
|
||||
|
|
@ -128,6 +132,7 @@ it(
|
|||
// cannot guarantee that the cursor snapshot has been published.
|
||||
await published.promise;
|
||||
};
|
||||
|
||||
try {
|
||||
await tracker.sendLocalCursorsToServer({
|
||||
"note.md": [{ start: 1, end: 2 }]
|
||||
|
|
@ -149,11 +154,11 @@ it(
|
|||
database.state.local.document = path;
|
||||
await notifier.triggerAsync(path);
|
||||
assert.equal(
|
||||
sent.at(-1)!.documentsWithCursors[0].relative_path,
|
||||
sent.at(-1)?.documentsWithCursors[0]?.relative_path,
|
||||
path
|
||||
);
|
||||
websocket.onWebSocketStatusChanged.trigger(false);
|
||||
const count = sent.length;
|
||||
const count = Number(sent.length);
|
||||
const readCount = read.mock.callCount();
|
||||
context.mock.timers.tick(30_000);
|
||||
// A surviving heartbeat would start a snapshot read in the send lock's
|
||||
|
|
@ -176,8 +181,8 @@ for (const interruption of ["disconnect", "reset"] as const) {
|
|||
it(`an in-flight cursor read cannot resurrect presence after ${interruption}`, async () => {
|
||||
const content = new TextEncoder().encode("current");
|
||||
const contentHash = await hash(content);
|
||||
const entered = Promise.withResolvers<void>();
|
||||
const release = Promise.withResolvers<void>();
|
||||
const entered = Promise.withResolvers<undefined>();
|
||||
const release = Promise.withResolvers<undefined>();
|
||||
const database = createDatabase(contentHash);
|
||||
const websocket = {
|
||||
onWebSocketStatusChanged: new EventListeners<
|
||||
|
|
@ -186,14 +191,14 @@ for (const interruption of ["disconnect", "reset"] as const) {
|
|||
onRemoteCursorsUpdateReceived: new EventListeners<
|
||||
(cursors: ClientCursors[]) => Promise<void>
|
||||
>(),
|
||||
updateLocalCursors: () => {}
|
||||
updateLocalCursors: () => undefined
|
||||
} as unknown as WebSocketManager;
|
||||
const tracker = new CursorTracker(
|
||||
database,
|
||||
websocket,
|
||||
{
|
||||
read: async () => {
|
||||
entered.resolve();
|
||||
entered.resolve(undefined);
|
||||
await release.promise;
|
||||
return { content };
|
||||
}
|
||||
|
|
@ -223,10 +228,14 @@ for (const interruption of ["disconnect", "reset"] as const) {
|
|||
const queued = websocket.onRemoteCursorsUpdateReceived.triggerAsync([
|
||||
cursor
|
||||
]);
|
||||
if (interruption === "reset") tracker.reset();
|
||||
else websocket.onWebSocketStatusChanged.trigger(false);
|
||||
if (interruption === "reset") {
|
||||
tracker.reset();
|
||||
} else {
|
||||
websocket.onWebSocketStatusChanged.trigger(false);
|
||||
}
|
||||
|
||||
assert.deepEqual(received, []);
|
||||
release.resolve();
|
||||
release.resolve(undefined);
|
||||
await first;
|
||||
await queued;
|
||||
assert.deepEqual(
|
||||
|
|
@ -241,8 +250,8 @@ for (const interruption of ["disconnect", "reset"] as const) {
|
|||
it("a remote cursor becomes outdated if the document advances during its snapshot read", async () => {
|
||||
const content = new TextEncoder().encode("old");
|
||||
const database = createDatabase(await hash(content));
|
||||
const entered = Promise.withResolvers<void>(),
|
||||
release = Promise.withResolvers<void>();
|
||||
const entered = Promise.withResolvers<undefined>(),
|
||||
release = Promise.withResolvers<undefined>();
|
||||
const websocket = {
|
||||
onWebSocketStatusChanged: new EventListeners<
|
||||
(connected: boolean) => unknown
|
||||
|
|
@ -250,21 +259,21 @@ it("a remote cursor becomes outdated if the document advances during its snapsho
|
|||
onRemoteCursorsUpdateReceived: new EventListeners<
|
||||
(cursors: ClientCursors[]) => Promise<void>
|
||||
>(),
|
||||
updateLocalCursors: () => {}
|
||||
updateLocalCursors: () => undefined
|
||||
} as unknown as WebSocketManager;
|
||||
const tracker = new CursorTracker(
|
||||
database,
|
||||
websocket,
|
||||
{
|
||||
read: async () => {
|
||||
entered.resolve();
|
||||
entered.resolve(undefined);
|
||||
await release.promise;
|
||||
return { content };
|
||||
}
|
||||
} as unknown as FileSystemOperations,
|
||||
new EventListeners<(path: RelativePath) => unknown>()
|
||||
);
|
||||
let outdated: boolean | undefined;
|
||||
let outdated: boolean | undefined = undefined;
|
||||
tracker.onRemoteCursorsUpdated.add((cursors) => {
|
||||
outdated = cursors[0]?.isOutdated;
|
||||
});
|
||||
|
|
@ -283,11 +292,12 @@ it("a remote cursor becomes outdated if the document advances during its snapsho
|
|||
}
|
||||
]);
|
||||
await entered.promise;
|
||||
assert.ok(database.state.documents.document);
|
||||
database.state.documents.document.base = {
|
||||
vaultUpdateId: 2,
|
||||
hash: await hash(new TextEncoder().encode("new content"))
|
||||
};
|
||||
release.resolve();
|
||||
release.resolve(undefined);
|
||||
await update;
|
||||
assert.equal(outdated, true);
|
||||
tracker.reset();
|
||||
|
|
|
|||
Loading…
Reference in a new issue