....
This commit is contained in:
parent
3dab48f58b
commit
c869f0a880
1 changed files with 461 additions and 578 deletions
|
|
@ -1,29 +1,35 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-type-assertion */
|
||||
import assert from "node:assert/strict";
|
||||
import { it } from "node:test";
|
||||
import type {
|
||||
import {
|
||||
Database,
|
||||
RelativePath,
|
||||
VaultUpdateId
|
||||
emptyState,
|
||||
type RelativePath,
|
||||
type VaultUpdateId
|
||||
} from "../persistence/database";
|
||||
import type { FileOperations } from "../file-operations/file-operations";
|
||||
import type { FileSystemOperations } from "../file-operations/filesystem-operations";
|
||||
import type { WebSocketManager } from "../services/websocket-manager";
|
||||
import type { ClientCursors } from "../services/types/ClientCursors";
|
||||
import { EventListeners } from "../utils/data-structures/event-listeners";
|
||||
import { hash } from "../utils/hash";
|
||||
import { CursorTracker } from "./cursor-tracker";
|
||||
|
||||
function createDatabase(digest: string): Database {
|
||||
const state = emptyState("test");
|
||||
state.local.document = "note.md";
|
||||
state.documents.document = { base: { vaultUpdateId: 1, hash: digest } };
|
||||
return new Database(
|
||||
state,
|
||||
async () => undefined,
|
||||
"test",
|
||||
async () => undefined
|
||||
);
|
||||
}
|
||||
|
||||
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: version, hash: contentHash }
|
||||
})
|
||||
} as unknown as Database;
|
||||
const database = createDatabase(contentHash);
|
||||
const websocket = {
|
||||
onWebSocketStatusChanged: new EventListeners(),
|
||||
onRemoteCursorsUpdateReceived: new EventListeners<
|
||||
|
|
@ -35,7 +41,7 @@ it("keeps the last usable cursor while retaining only the latest future update",
|
|||
const tracker = new CursorTracker(
|
||||
database,
|
||||
websocket,
|
||||
{ read: async () => content } as unknown as FileOperations,
|
||||
{ read: async () => ({ content }) } as unknown as FileSystemOperations,
|
||||
notifier
|
||||
);
|
||||
context.after(() => tracker.reset());
|
||||
|
|
@ -61,14 +67,20 @@ it("keeps the last usable cursor while retaining only the latest future update",
|
|||
]);
|
||||
assert.equal(received[0].documentsWithCursors[0].vault_update_id, 1);
|
||||
}
|
||||
version = 2;
|
||||
database.state.documents.document.base = {
|
||||
vaultUpdateId: 2,
|
||||
hash: contentHash
|
||||
};
|
||||
await notifier.triggerAsync("note.md");
|
||||
assert.equal(
|
||||
received[0].documentsWithCursors[0].vault_update_id,
|
||||
1,
|
||||
"superseded future cursors must not reappear"
|
||||
);
|
||||
version = 3;
|
||||
database.state.documents.document.base = {
|
||||
vaultUpdateId: 3,
|
||||
hash: contentHash
|
||||
};
|
||||
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");
|
||||
|
|
@ -80,17 +92,7 @@ it("refreshes unchanged cursors on reconnect and heartbeat, and follows the docu
|
|||
context.mock.timers.enable({ apis: ["setInterval"] });
|
||||
const content = new TextEncoder().encode("hello");
|
||||
let path = "note.md";
|
||||
const record = () => ({
|
||||
documentId: "document",
|
||||
relativePath: path,
|
||||
metadata: { parentVersionId: 1, hash: contentHash }
|
||||
});
|
||||
const contentHash = await hash(content);
|
||||
const database = {
|
||||
getLatestDocumentByRelativePath: (candidate: RelativePath) =>
|
||||
candidate === path ? record() : undefined,
|
||||
getDocumentByDocumentId: () => record()
|
||||
} as unknown as Database;
|
||||
const database = createDatabase(await hash(content));
|
||||
const sent: {
|
||||
documentsWithCursors: {
|
||||
relative_path: RelativePath;
|
||||
|
|
@ -110,7 +112,7 @@ it("refreshes unchanged cursors on reconnect and heartbeat, and follows the docu
|
|||
const tracker = new CursorTracker(
|
||||
database,
|
||||
websocket,
|
||||
{ read: async () => content } as unknown as FileOperations,
|
||||
{ read: async () => ({ content }) } as unknown as FileSystemOperations,
|
||||
notifier
|
||||
);
|
||||
const drain = async () => {
|
||||
|
|
@ -133,6 +135,7 @@ it("refreshes unchanged cursors on reconnect and heartbeat, and follows the docu
|
|||
await drain();
|
||||
assert.equal(sent.length, 3);
|
||||
path = "renamed.md";
|
||||
database.state.local.document = path;
|
||||
await notifier.triggerAsync(path);
|
||||
assert.equal(sent.at(-1)!.documentsWithCursors[0].relative_path, path);
|
||||
websocket.onWebSocketStatusChanged.trigger(false);
|
||||
|
|
@ -155,13 +158,7 @@ for (const interruption of ["disconnect", "reset"] as const) {
|
|||
const contentHash = await hash(content);
|
||||
const entered = Promise.withResolvers<void>();
|
||||
const release = Promise.withResolvers<void>();
|
||||
const database = {
|
||||
getDocumentByDocumentId: () => ({
|
||||
documentId: "document",
|
||||
relativePath: "note.md",
|
||||
metadata: { parentVersionId: 1, hash: contentHash }
|
||||
})
|
||||
} as unknown as Database;
|
||||
const database = createDatabase(contentHash);
|
||||
const websocket = {
|
||||
onWebSocketStatusChanged: new EventListeners<
|
||||
(connected: boolean) => unknown
|
||||
|
|
@ -178,9 +175,9 @@ for (const interruption of ["disconnect", "reset"] as const) {
|
|||
read: async () => {
|
||||
entered.resolve();
|
||||
await release.promise;
|
||||
return content;
|
||||
return { content };
|
||||
}
|
||||
} as unknown as FileOperations,
|
||||
} as unknown as FileSystemOperations,
|
||||
new EventListeners<(path: RelativePath) => unknown>()
|
||||
);
|
||||
let received: ClientCursors[] = [];
|
||||
|
|
@ -223,7 +220,7 @@ 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");
|
||||
let metadata = { parentVersionId: 1, hash: await hash(content) };
|
||||
const database = createDatabase(await hash(content));
|
||||
const entered = Promise.withResolvers<void>(),
|
||||
release = Promise.withResolvers<void>();
|
||||
const websocket = {
|
||||
|
|
@ -236,21 +233,15 @@ it("a remote cursor becomes outdated if the document advances during its snapsho
|
|||
updateLocalCursors: () => {}
|
||||
} as unknown as WebSocketManager;
|
||||
const tracker = new CursorTracker(
|
||||
{
|
||||
getDocumentByDocumentId: () => ({
|
||||
documentId: "document",
|
||||
relativePath: "note.md",
|
||||
metadata
|
||||
})
|
||||
} as unknown as Database,
|
||||
database,
|
||||
websocket,
|
||||
{
|
||||
read: async () => {
|
||||
entered.resolve();
|
||||
await release.promise;
|
||||
return content;
|
||||
return { content };
|
||||
}
|
||||
} as unknown as FileOperations,
|
||||
} as unknown as FileSystemOperations,
|
||||
new EventListeners<(path: RelativePath) => unknown>()
|
||||
);
|
||||
let outdated: boolean | undefined;
|
||||
|
|
@ -272,8 +263,8 @@ it("a remote cursor becomes outdated if the document advances during its snapsho
|
|||
}
|
||||
]);
|
||||
await entered.promise;
|
||||
metadata = {
|
||||
parentVersionId: 2,
|
||||
database.state.documents.document.base = {
|
||||
vaultUpdateId: 2,
|
||||
hash: await hash(new TextEncoder().encode("new content"))
|
||||
};
|
||||
release.resolve();
|
||||
|
|
|
|||
Loading…
Reference in a new issue