More tests
This commit is contained in:
parent
5707add47c
commit
cc44b66fcd
4 changed files with 138 additions and 8 deletions
|
|
@ -89,6 +89,8 @@ import { serverPauseDeleteRecreateTest } from "./tests/server-pause-delete-recre
|
|||
import { onlineBothCreateSamePathDeconflictTest } from "./tests/online-both-create-same-path-deconflict.test";
|
||||
import { onlineCreateUpdateWhileOtherCreatesSamePathTest } from "./tests/online-create-update-while-other-creates-same-path.test";
|
||||
import { displacedFileNotMarkedDeletedTest } from "./tests/displaced-file-not-marked-deleted.test";
|
||||
import { remoteUpdateResurrectsDeletedDocTest } from "./tests/remote-update-resurrects-deleted-doc.test";
|
||||
import { localUpdateSurvivesRemoteRenameTest } from "./tests/local-update-survives-remote-rename.test";
|
||||
|
||||
export const TESTS: Partial<Record<string, TestDefinition>> = {
|
||||
"rename-create-conflict": renameCreateConflictTest,
|
||||
|
|
@ -198,5 +200,8 @@ export const TESTS: Partial<Record<string, TestDefinition>> = {
|
|||
onlineBothCreateSamePathDeconflictTest,
|
||||
"online-create-update-while-other-creates-same-path":
|
||||
onlineCreateUpdateWhileOtherCreatesSamePathTest,
|
||||
"displaced-file-not-marked-deleted": displacedFileNotMarkedDeletedTest
|
||||
"displaced-file-not-marked-deleted": displacedFileNotMarkedDeletedTest,
|
||||
"remote-update-resurrects-deleted-doc": remoteUpdateResurrectsDeletedDocTest,
|
||||
"local-update-survives-remote-rename":
|
||||
localUpdateSurvivesRemoteRenameTest
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
import type { AssertableState } from "../utils/assertable-state";
|
||||
import type { TestDefinition } from "../test-definition";
|
||||
|
||||
export const localUpdateSurvivesRemoteRenameTest: TestDefinition = {
|
||||
description:
|
||||
"Client 0 has a local content edit pending while a remote rename for " +
|
||||
"the same doc arrives over the WebSocket. The remote rename's internal " +
|
||||
"move relocates the disk file from the old path (where the user wrote) " +
|
||||
"to the new server path. Previously, the queued LocalUpdate's " +
|
||||
"`event.path` was left pointing at the now-vacated old path, so " +
|
||||
"`skipIfOversized`'s `getFileSize(event.path)` threw " +
|
||||
"`FileNotFoundError`, which `processEvent`'s catch silently swallowed " +
|
||||
"as 'Skipping sync event 'local-update' because the file no longer " +
|
||||
"exists' — and the user's edit was lost. The fix routes the size " +
|
||||
"check through `tracked.path` (the doc's current disk path), " +
|
||||
"matching the path `processLocalUpdate` itself reads from.",
|
||||
clients: 2,
|
||||
steps: [
|
||||
{ type: "create", client: 0, path: "doc.md", content: "v1\n" },
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "barrier" },
|
||||
|
||||
// Pause client 0's WebSocket so the upcoming remote rename buffers
|
||||
// there until we've already enqueued client 0's local content
|
||||
// edit. This guarantees the LocalUpdate sits in client 0's queue
|
||||
// when the rename's RemoteChange drains.
|
||||
{ type: "pause-websocket", client: 0 },
|
||||
|
||||
{
|
||||
type: "rename",
|
||||
client: 1,
|
||||
oldPath: "doc.md",
|
||||
newPath: "renamed.md"
|
||||
},
|
||||
{ type: "sync", client: 1 },
|
||||
|
||||
// Client 0 still believes the file is at `doc.md` (its WebSocket is
|
||||
// paused, so the rename hasn't reached it). The user edits content
|
||||
// at `doc.md`. This pushes a LocalUpdate(D, path=doc.md,
|
||||
// originalPath=doc.md, isUserRename=false) into client 0's queue.
|
||||
{
|
||||
type: "update",
|
||||
client: 0,
|
||||
path: "doc.md",
|
||||
content: "v1\nclient 0 edit\n"
|
||||
},
|
||||
|
||||
// Resume the WebSocket. The buffered remote rename (server-broadcast)
|
||||
// drains. `processRemoteUpdate` does an internal `move(doc.md,
|
||||
// renamed.md)` and, because there's a pending LocalUpdate for D,
|
||||
// takes the else branch (re-enqueue v_K, setDocument(renamed.md, …)).
|
||||
// Then drain reaches the LocalUpdate. Pre-fix: skipped silently.
|
||||
// Post-fix: PUTs the user's content to the doc (at its new path,
|
||||
// since this is a content-only edit, not a user rename).
|
||||
{ type: "resume-websocket", client: 0 },
|
||||
|
||||
{ type: "barrier" },
|
||||
|
||||
{
|
||||
type: "assert-consistent",
|
||||
verify: (state: AssertableState): void => {
|
||||
state.assertFileCount(1);
|
||||
state.assertFileExists("renamed.md");
|
||||
state.assertContent(
|
||||
"renamed.md",
|
||||
"v1\nclient 0 edit\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
@ -28,13 +28,7 @@ export const moveRemoteUpdateRevertsRenameTest: TestDefinition = {
|
|||
{
|
||||
type: "assert-consistent",
|
||||
verify: (s: AssertableState): void => {
|
||||
s.assertFileCount(1);
|
||||
const [content] = Array.from(s.files.values());
|
||||
if (content !== "updated by client 1") {
|
||||
throw new Error(
|
||||
`Expected "updated by client 1", got: "${content}"`
|
||||
);
|
||||
}
|
||||
s.assertFileCount(1).assertContent("renamed.md", "updated by client 1");
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
import type { AssertableState } from "../utils/assertable-state";
|
||||
import type { TestDefinition } from "../test-definition";
|
||||
|
||||
export const remoteUpdateResurrectsDeletedDocTest: TestDefinition = {
|
||||
description:
|
||||
"Client 1 updates, deletes, and recreates P (with a new docId D2). " +
|
||||
"While the buffered remote events are being processed by client 0, " +
|
||||
"client 0 also makes a local edit to P. The local edit lands in the " +
|
||||
"queue while v17 is mid-process, sending v17 down processRemoteUpdate's " +
|
||||
"re-enqueue branch. The deferred v17 must NOT later resurrect D1 as a " +
|
||||
"conflict-… file at P after the delete and the D2 create have drained.",
|
||||
clients: 2,
|
||||
steps: [
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
|
||||
{ type: "create", client: 1, path: "P.md", content: "v8 content\n" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "pause-websocket", client: 0 },
|
||||
|
||||
{
|
||||
type: "update",
|
||||
client: 1,
|
||||
path: "P.md",
|
||||
content: "v17 content from client 1\n"
|
||||
},
|
||||
{ type: "sync", client: 1 },
|
||||
{ type: "delete", client: 1, path: "P.md" },
|
||||
{ type: "sync", client: 1 },
|
||||
{
|
||||
type: "create",
|
||||
client: 1,
|
||||
path: "P.md",
|
||||
content: "v21 content (D2)\n"
|
||||
},
|
||||
{ type: "sync", client: 1 },
|
||||
|
||||
{ type: "resume-websocket", client: 0 },
|
||||
|
||||
{
|
||||
type: "update",
|
||||
client: 0,
|
||||
path: "P.md",
|
||||
content: "local edit by client 0\n"
|
||||
},
|
||||
|
||||
{ type: "barrier" },
|
||||
|
||||
{
|
||||
type: "assert-consistent",
|
||||
verify: (state: AssertableState): void => {
|
||||
state
|
||||
.assertFileCount(1)
|
||||
.assertContent("P.md", "v21 content (D2)\n");
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue