Scripted multi-client harness against a real server (~110 scenario tests, server-control, managed-websocket, test-runner). Wires the new package into frontend/package.json workspaces and the lint script. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
66 lines
3.1 KiB
TypeScript
66 lines
3.1 KiB
TypeScript
import type { AssertableState } from "../utils/assertable-state";
|
|
import type { TestDefinition } from "../test-definition";
|
|
|
|
export const catchupCreateAndUpdateNotSkippedTest: TestDefinition = {
|
|
description:
|
|
"Client 1 disconnects (sync disabled). Client 0 creates a doc and " +
|
|
"then updates it. When Client 1 reconnects, the server's catch-up " +
|
|
"stream sends only the doc's *latest* version (the update), not the " +
|
|
"full history. Pre-fix the wire's `is_new_file` was set to " +
|
|
"`creation == latest_version`, so the catch-up flagged the doc as " +
|
|
"non-new even though Client 1 had never seen its creation. Client " +
|
|
"1's `processRemoteChange` then dropped it as a 'stale RemoteChange " +
|
|
"for untracked, non-new document' and the doc was silently lost. " +
|
|
"Post-fix `is_new_file` in the catch-up stream means 'new relative " +
|
|
"to the recipient's watermark' (`creation > last_seen_vault_update_id`).",
|
|
clients: 2,
|
|
steps: [
|
|
{ type: "enable-sync", client: 0 },
|
|
{ type: "enable-sync", client: 1 },
|
|
// Establish a baseline so Client 1's last_seen is non-zero before
|
|
// we take it offline. This makes the bug genuinely about catch-up
|
|
// missing the create rather than just an empty-vault first sync.
|
|
{ type: "create", client: 0, path: "warmup.md", content: "w\n" },
|
|
{ type: "barrier" },
|
|
|
|
// Client 1 goes offline.
|
|
{ type: "disable-sync", client: 1 },
|
|
|
|
// Client 0 creates the doc (vault_update_id v_C, after Client 1's
|
|
// watermark). Client 1 doesn't see this because it's offline.
|
|
{ type: "create", client: 0, path: "doc.md", content: "v1\n" },
|
|
// Wait for the create's HTTP to land before the update; otherwise
|
|
// both writes are coalesced into a single POST and the server
|
|
// never sees the doc as "create followed by update".
|
|
{ type: "sync", client: 0 },
|
|
|
|
// Client 0 updates the doc (vault_update_id v_X > v_C). The
|
|
// server's `latest_document_versions` view now returns the
|
|
// *update* row — its `creation_vault_update_id != vault_update_id`.
|
|
{
|
|
type: "update",
|
|
client: 0,
|
|
path: "doc.md",
|
|
content: "v1\nupdate\n"
|
|
},
|
|
{ type: "sync", client: 0 },
|
|
|
|
// Client 1 reconnects. Server's catch-up replays docs with
|
|
// `vault_update_id > last_seen`. For doc.md it sends v_X with
|
|
// `is_new_file` derived from `creation_vault_update_id >
|
|
// last_seen_vault_update_id` (post-fix) — so Client 1 treats it
|
|
// as a fresh create and downloads the latest content.
|
|
{ type: "enable-sync", client: 1 },
|
|
{ type: "barrier" },
|
|
|
|
{
|
|
type: "assert-consistent",
|
|
verify: (state: AssertableState): void => {
|
|
state.assertFileCount(2);
|
|
state.assertFileExists("doc.md");
|
|
state.assertContent("doc.md", "v1\nupdate\n");
|
|
state.assertContent("warmup.md", "w\n");
|
|
}
|
|
}
|
|
]
|
|
};
|