,,
This commit is contained in:
parent
0c75d3b4c2
commit
3dab48f58b
1 changed files with 1141 additions and 1376 deletions
|
|
@ -1,71 +1,79 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-type-assertion */
|
||||
import assert from "node:assert/strict";
|
||||
import { it } from "node:test";
|
||||
import type { Database } from "../persistence/database";
|
||||
import type {
|
||||
Database,
|
||||
RelativePath,
|
||||
VaultUpdateId
|
||||
} from "../persistence/database";
|
||||
import type { FileOperations } from "../file-operations/file-operations";
|
||||
import type { WebSocketManager } from "../services/websocket-manager";
|
||||
import type { ClientCursors } from "../services/types/ClientCursors";
|
||||
import { DocumentUpToDateness } from "../types/document-up-to-dateness";
|
||||
import { EventListeners } from "../utils/data-structures/event-listeners";
|
||||
import { hash } from "../utils/hash";
|
||||
import { CursorTracker } from "./cursor-tracker";
|
||||
import { FileChangeNotifier } from "./file-change-notifier";
|
||||
|
||||
it("retains future cursors and publishes them when content catches up", async () => {
|
||||
it("keeps the last usable cursor while retaining only the latest future update", async (context) => {
|
||||
const content = new TextEncoder().encode("current");
|
||||
const contentHash = await hash(content);
|
||||
let version = 1;
|
||||
const database = {
|
||||
getDocumentByDocumentId: () => ({
|
||||
documentId: "document",
|
||||
relativePath: "note.md",
|
||||
metadata: { parentVersionId: 2, hash: contentHash }
|
||||
metadata: { parentVersionId: version, hash: contentHash }
|
||||
})
|
||||
} as unknown as Database;
|
||||
const websocket = {
|
||||
onWebSocketStatusChanged: new EventListeners<
|
||||
(connected: boolean) => unknown
|
||||
>(),
|
||||
onWebSocketStatusChanged: new EventListeners(),
|
||||
onRemoteCursorsUpdateReceived: new EventListeners<
|
||||
(cursors: ClientCursors[]) => Promise<void>
|
||||
>(),
|
||||
updateLocalCursors: () => {}
|
||||
} as unknown as WebSocketManager;
|
||||
const files = { read: async () => content } as unknown as FileOperations;
|
||||
const notifier = new FileChangeNotifier();
|
||||
const tracker = new CursorTracker(database, websocket, files, notifier);
|
||||
const future = {
|
||||
const notifier = new EventListeners<(path: RelativePath) => unknown>();
|
||||
const tracker = new CursorTracker(
|
||||
database,
|
||||
websocket,
|
||||
{ read: async () => content } as unknown as FileOperations,
|
||||
notifier
|
||||
);
|
||||
context.after(() => tracker.reset());
|
||||
let received: ClientCursors[] = [];
|
||||
tracker.onRemoteCursorsUpdated.add((cursors) => {
|
||||
received = cursors;
|
||||
});
|
||||
const cursor = (version: number): ClientCursors => ({
|
||||
userName: "Other",
|
||||
deviceId: "device",
|
||||
documentsWithCursors: [
|
||||
{
|
||||
document_id: "document",
|
||||
relative_path: "old-name.md",
|
||||
vault_update_id: 2,
|
||||
cursors: [{ start: 0, end: 1 }]
|
||||
vault_update_id: version,
|
||||
cursors: [{ start: version, end: version }]
|
||||
}
|
||||
],
|
||||
upToDateness: DocumentUpToDateness.Later
|
||||
};
|
||||
const internals = tracker as unknown as {
|
||||
knownRemoteCursors: (typeof future)[];
|
||||
getRelevantAndPruneKnownClientCursors: () => ClientCursors[];
|
||||
};
|
||||
internals.knownRemoteCursors = [future];
|
||||
|
||||
assert.deepEqual(internals.getRelevantAndPruneKnownClientCursors(), []);
|
||||
assert.strictEqual(internals.knownRemoteCursors.length, 1);
|
||||
|
||||
let received: ClientCursors[] = [];
|
||||
tracker.onRemoteCursorsUpdated.add((cursors) => {
|
||||
received = cursors;
|
||||
]
|
||||
});
|
||||
await notifier.onFileChanged.triggerAsync("note.md");
|
||||
|
||||
assert.strictEqual(received.length, 1);
|
||||
assert.strictEqual(
|
||||
received[0].documentsWithCursors[0].relative_path,
|
||||
"note.md"
|
||||
for (const remoteVersion of [1, 2, 3]) {
|
||||
await websocket.onRemoteCursorsUpdateReceived.triggerAsync([
|
||||
cursor(remoteVersion)
|
||||
]);
|
||||
assert.equal(received[0].documentsWithCursors[0].vault_update_id, 1);
|
||||
}
|
||||
version = 2;
|
||||
await notifier.triggerAsync("note.md");
|
||||
assert.equal(
|
||||
received[0].documentsWithCursors[0].vault_update_id,
|
||||
1,
|
||||
"superseded future cursors must not reappear"
|
||||
);
|
||||
version = 3;
|
||||
await notifier.triggerAsync("note.md");
|
||||
assert.equal(received[0].documentsWithCursors[0].vault_update_id, 3);
|
||||
assert.equal(received[0].documentsWithCursors[0].relative_path, "note.md");
|
||||
await websocket.onRemoteCursorsUpdateReceived.triggerAsync([]);
|
||||
assert.deepEqual(received, []);
|
||||
});
|
||||
|
||||
it("refreshes unchanged cursors on reconnect and heartbeat, and follows the document through renames", async (context) => {
|
||||
|
|
@ -79,14 +87,14 @@ it("refreshes unchanged cursors on reconnect and heartbeat, and follows the docu
|
|||
});
|
||||
const contentHash = await hash(content);
|
||||
const database = {
|
||||
getLatestDocumentByRelativePath: (candidate: string) =>
|
||||
getLatestDocumentByRelativePath: (candidate: RelativePath) =>
|
||||
candidate === path ? record() : undefined,
|
||||
getDocumentByDocumentId: () => record()
|
||||
} as unknown as Database;
|
||||
const sent: {
|
||||
documentsWithCursors: {
|
||||
relative_path: string;
|
||||
vault_update_id: number | null;
|
||||
relative_path: RelativePath;
|
||||
vault_update_id: VaultUpdateId | null;
|
||||
}[];
|
||||
}[] = [];
|
||||
const websocket = {
|
||||
|
|
@ -98,7 +106,7 @@ it("refreshes unchanged cursors on reconnect and heartbeat, and follows the docu
|
|||
sent.push(value);
|
||||
}
|
||||
} as unknown as WebSocketManager;
|
||||
const notifier = new FileChangeNotifier();
|
||||
const notifier = new EventListeners<(path: RelativePath) => unknown>();
|
||||
const tracker = new CursorTracker(
|
||||
database,
|
||||
websocket,
|
||||
|
|
@ -125,7 +133,7 @@ it("refreshes unchanged cursors on reconnect and heartbeat, and follows the docu
|
|||
await drain();
|
||||
assert.equal(sent.length, 3);
|
||||
path = "renamed.md";
|
||||
await notifier.onFileChanged.triggerAsync(path);
|
||||
await notifier.triggerAsync(path);
|
||||
assert.equal(sent.at(-1)!.documentsWithCursors[0].relative_path, path);
|
||||
websocket.onWebSocketStatusChanged.trigger(false);
|
||||
const count = sent.length;
|
||||
|
|
@ -173,7 +181,7 @@ for (const interruption of ["disconnect", "reset"] as const) {
|
|||
return content;
|
||||
}
|
||||
} as unknown as FileOperations,
|
||||
new FileChangeNotifier()
|
||||
new EventListeners<(path: RelativePath) => unknown>()
|
||||
);
|
||||
let received: ClientCursors[] = [];
|
||||
tracker.onRemoteCursorsUpdated.add((cursors) => {
|
||||
|
|
@ -243,7 +251,7 @@ it("a remote cursor becomes outdated if the document advances during its snapsho
|
|||
return content;
|
||||
}
|
||||
} as unknown as FileOperations,
|
||||
new FileChangeNotifier()
|
||||
new EventListeners<(path: RelativePath) => unknown>()
|
||||
);
|
||||
let outdated: boolean | undefined;
|
||||
tracker.onRemoteCursorsUpdated.add((cursors) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue