Update tests
This commit is contained in:
parent
64ca5a82ef
commit
0e3e5a99cd
17 changed files with 181 additions and 88 deletions
|
|
@ -184,10 +184,10 @@ export class DeterministicAgent extends debugging.InMemoryFileSystem {
|
|||
await super.write(path, content);
|
||||
|
||||
if (isNew) {
|
||||
this.enqueueSync(async () => this.client.syncLocallyCreatedFile(path)
|
||||
this.enqueueSync(async () => { this.client.syncLocallyCreatedFile(path); }
|
||||
);
|
||||
} else {
|
||||
this.enqueueSync(async () => this.client.syncLocallyUpdatedFile({ relativePath: path })
|
||||
this.enqueueSync(async () => { this.client.syncLocallyUpdatedFile({ relativePath: path }); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -197,7 +197,7 @@ export class DeterministicAgent extends debugging.InMemoryFileSystem {
|
|||
updater: (current: TextWithCursors) => TextWithCursors
|
||||
): Promise<string> {
|
||||
const result = await super.atomicUpdateText(path, updater);
|
||||
this.enqueueSync(async () => this.client.syncLocallyUpdatedFile({ relativePath: path })
|
||||
this.enqueueSync(async () => { this.client.syncLocallyUpdatedFile({ relativePath: path }); }
|
||||
);
|
||||
return result;
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ import { offlineMoveThenRemoteDeleteTest } from "./tests/offline-move-then-remot
|
|||
import { resetClearsRecentlyDeletedResurrectionTest } from "./tests/reset-clears-recently-deleted-resurrection.test";
|
||||
import { moveThenDeleteStalePathTest } from "./tests/move-then-delete-stale-path.test";
|
||||
import { interruptedDeleteRetryTest } from "./tests/interrupted-delete-retry.test";
|
||||
import { updateSurvivesRemoteDeleteTest } from "./tests/update-survives-remote-delete.test";
|
||||
import { updateDoesNotSurvivesRemoteDeleteTest } from "./tests/update-survives-remote-delete.test";
|
||||
import { movePreservesRemoteUpdateTest } from "./tests/move-preserves-remote-update.test";
|
||||
import { recentlyDeletedClearedOnReconnectTest } from "./tests/recently-deleted-cleared-on-reconnect.test";
|
||||
import { migrateKeyPreservesExistingTest } from "./tests/migrate-key-preserves-existing.test";
|
||||
|
|
@ -65,7 +65,32 @@ import { createRenameResponseSkipsFileTest } from "./tests/create-rename-respons
|
|||
import { onlineCreateRenameConcurrentCreateOrphanTest } from "./tests/online-create-rename-concurrent-create-orphan.test";
|
||||
import { concurrentRenameFirstWinsTest } from "./tests/concurrent-rename-first-wins.test";
|
||||
import { binaryToTextTransitionTest } from "./tests/binary-to-text-transition.test";
|
||||
import { updateThenRenameContentLostTest } from "./tests/update-then-rename-content-lost.test";
|
||||
import { textPendingCreateNotDisplacedTest } from "./tests/1-text-pending-create-not-displaced.test";
|
||||
import { binaryPendingCreateNotDisplacedTest } from "./tests/2-binary-pending-create-not-displaced.test";
|
||||
import { coalesceUpdateRemoteUpdateDataLossTest } from "./tests/3-coalesce-update-remote-update-data-loss.test";
|
||||
import { coalescedRemoteUpdateWatermarkLossTest } from "./tests/4-coalesced-remote-update-watermark-loss.test";
|
||||
import { concurrentDeleteDuringRemoteUpdateTest } from "./tests/5-concurrent-delete-during-remote-update.test";
|
||||
import { concurrentEditExactSamePositionTest } from "./tests/6-concurrent-edit-exact-same-position.test";
|
||||
import { concurrentRenameAndCreateAtTargetTest as concurrentRenameAndCreateAtTargetRenameFirstTest } from "./tests/7-concurrent-rename-and-create-at-target.test";
|
||||
import { concurrentRenameAndCreateAtTargetTest as concurrentRenameAndCreateAtTargetCreateFirstTest } from "./tests/8-concurrent-rename-and-create-at-target.test";
|
||||
import { concurrentRenameSameTargetTest } from "./tests/9-concurrent-rename-same-target.test";
|
||||
import { concurrentUpdateDiffConsistencyTest } from "./tests/10-concurrent-update-diff-consistency.test";
|
||||
import { userParenthesizedFileNotDeletedTest } from "./tests/10-user-parenthesized-file-not-deleted.test";
|
||||
import { createDeleteNoopTest } from "./tests/11-create-delete-noop.test";
|
||||
import { createMergeDeleteTest } from "./tests/12-create-merge-delete.test";
|
||||
import { moveIdenticalContentAmbiguityTest } from "./tests/13-move-identical-content-ambiguity.test";
|
||||
import { createUpdateCoalesceServerPauseTest } from "./tests/15-create-update-coalesce-server-pause.test";
|
||||
import { createDuringReconciliationTest } from "./tests/16-create-during-reconciliation.test";
|
||||
import { createMergePreservesRenamedUpdateTest } from "./tests/17-create-merge-preserves-renamed-update.test";
|
||||
import { createRenameCreateSamePathTest } from "./tests/18-create-rename-create-same-path.test";
|
||||
import { moveChainThreeFilesTest } from "./tests/19-move-chain-three-files.test";
|
||||
import { deleteByOtherClientThenRecreateTest } from "./tests/delete-by-other-client-then-recreate.test";
|
||||
import { onlineDeleteRecreateRapidCycleTest } from "./tests/online-delete-recreate-rapid-cycle.test";
|
||||
import { onlineEditVsDeleteConvergenceTest } from "./tests/online-edit-vs-delete-convergence.test";
|
||||
import { rapidEditDeleteOnlineConvergenceTest } from "./tests/rapid-edit-delete-online-convergence.test";
|
||||
import { serverPauseDeleteRecreateTest } from "./tests/server-pause-delete-recreate.test";
|
||||
import { onlineBothCreateSamePathDeconflictTest } from "./tests/online-both-create-same-path-deconflict.test";
|
||||
import { onlineCreateUpdateWhileOtherCreatesSamePathTest } from "./tests/online-create-update-while-other-creates-same-path.test";
|
||||
|
||||
export const TESTS: Partial<Record<string, TestDefinition>> = {
|
||||
"rename-create-conflict": renameCreateConflictTest,
|
||||
|
|
@ -118,7 +143,7 @@ export const TESTS: Partial<Record<string, TestDefinition>> = {
|
|||
"move-then-delete-stale-path": moveThenDeleteStalePathTest,
|
||||
"offline-delete-vs-remote-update": offlineDeleteVsRemoteUpdateTest,
|
||||
"interrupted-delete-retry": interruptedDeleteRetryTest,
|
||||
"update-survives-remote-delete": updateSurvivesRemoteDeleteTest,
|
||||
"update-survives-remote-delete": updateDoesNotSurvivesRemoteDeleteTest,
|
||||
"move-preserves-remote-update": movePreservesRemoteUpdateTest,
|
||||
"recently-deleted-cleared-on-reconnect": recentlyDeletedClearedOnReconnectTest,
|
||||
"migrate-key-preserves-existing": migrateKeyPreservesExistingTest,
|
||||
|
|
@ -134,5 +159,30 @@ export const TESTS: Partial<Record<string, TestDefinition>> = {
|
|||
"online-create-rename-concurrent-create-orphan": onlineCreateRenameConcurrentCreateOrphanTest,
|
||||
"concurrent-rename-first-wins": concurrentRenameFirstWinsTest,
|
||||
"binary-to-text-transition": binaryToTextTransitionTest,
|
||||
"update-then-rename-content-lost": updateThenRenameContentLostTest,
|
||||
"text-pending-create-not-displaced": textPendingCreateNotDisplacedTest,
|
||||
"binary-pending-create-not-displaced": binaryPendingCreateNotDisplacedTest,
|
||||
"coalesce-update-remote-update-data-loss": coalesceUpdateRemoteUpdateDataLossTest,
|
||||
"coalesced-remote-update-watermark-loss": coalescedRemoteUpdateWatermarkLossTest,
|
||||
"concurrent-delete-during-remote-update": concurrentDeleteDuringRemoteUpdateTest,
|
||||
"concurrent-edit-exact-same-position": concurrentEditExactSamePositionTest,
|
||||
"concurrent-rename-and-create-at-target-rename-first": concurrentRenameAndCreateAtTargetRenameFirstTest,
|
||||
"concurrent-rename-and-create-at-target-create-first": concurrentRenameAndCreateAtTargetCreateFirstTest,
|
||||
"concurrent-rename-same-target": concurrentRenameSameTargetTest,
|
||||
"concurrent-update-diff-consistency": concurrentUpdateDiffConsistencyTest,
|
||||
"user-parenthesized-file-not-deleted": userParenthesizedFileNotDeletedTest,
|
||||
"create-delete-noop": createDeleteNoopTest,
|
||||
"create-merge-delete": createMergeDeleteTest,
|
||||
"move-identical-content-ambiguity": moveIdenticalContentAmbiguityTest,
|
||||
"create-update-coalesce-server-pause": createUpdateCoalesceServerPauseTest,
|
||||
"create-during-reconciliation": createDuringReconciliationTest,
|
||||
"create-merge-preserves-renamed-update": createMergePreservesRenamedUpdateTest,
|
||||
"create-rename-create-same-path": createRenameCreateSamePathTest,
|
||||
"move-chain-three-files": moveChainThreeFilesTest,
|
||||
"delete-by-other-client-then-recreate": deleteByOtherClientThenRecreateTest,
|
||||
"online-delete-recreate-rapid-cycle": onlineDeleteRecreateRapidCycleTest,
|
||||
"online-edit-vs-delete-convergence": onlineEditVsDeleteConvergenceTest,
|
||||
"rapid-edit-delete-online-convergence": rapidEditDeleteOnlineConvergenceTest,
|
||||
"server-pause-delete-recreate": serverPauseDeleteRecreateTest,
|
||||
"online-both-create-same-path-deconflict": onlineBothCreateSamePathDeconflictTest,
|
||||
"online-create-update-while-other-creates-same-path": onlineCreateUpdateWhileOtherCreatesSamePathTest,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,19 +10,19 @@ export const textPendingCreateNotDisplacedTest: TestDefinition = {
|
|||
type: "create",
|
||||
client: 0,
|
||||
path: "data.txt",
|
||||
content: "text data from client 0"
|
||||
content: "text data from client-0"
|
||||
},
|
||||
{
|
||||
type: "create",
|
||||
client: 1,
|
||||
path: "data.txt",
|
||||
content: "text data from client 1"
|
||||
content: "text data from client-1"
|
||||
},
|
||||
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "assert-consistent", verify: (s) => s.assertFileCount(1).assertFileExists("data.txt").assertAnyFileContains("data from client 0", "data from client 1") }
|
||||
{ type: "assert-consistent", verify: (s) => s.assertFileCount(1).assertFileExists("data.txt").assertAnyFileContains("client-0", "client-1") }
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ import type { TestDefinition } from "../test-definition";
|
|||
|
||||
export const binaryToTextTransitionTest: TestDefinition = {
|
||||
description:
|
||||
"A .bin file is created and synced. Both clients edit it offline, " +
|
||||
"then it is renamed to .md. Both clients edit different sections " +
|
||||
"offline again. The second merge should preserve both edits.",
|
||||
"A .bin file is created and synced. Both clients edit it offline " +
|
||||
"(binary last-write-wins), then client 0 renames it to .md and " +
|
||||
"writes a clean text baseline. Both clients edit different sections " +
|
||||
"offline. The text merge should preserve both edits.",
|
||||
clients: 2,
|
||||
steps: [
|
||||
{ type: "create", client: 0, path: "data.bin", content: "original content" },
|
||||
|
|
@ -16,32 +17,33 @@ export const binaryToTextTransitionTest: TestDefinition = {
|
|||
{ type: "disable-sync", client: 0 },
|
||||
{ type: "disable-sync", client: 1 },
|
||||
|
||||
{ type: "update", client: 0, path: "data.bin", content: "version A from client 0" },
|
||||
{ type: "update", client: 1, path: "data.bin", content: "version B from client 1" },
|
||||
{ type: "update", client: 0, path: "data.bin", content: "version A" },
|
||||
{ type: "update", client: 1, path: "data.bin", content: "version B" },
|
||||
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "assert-consistent", verify: (s) => s.assertFileCount(1).assertContainsAny("data.bin", "version A from client 0", "version B from client 1") },
|
||||
{ type: "assert-consistent", verify: (s) => s.assertFileCount(1).assertContainsAny("data.bin", "version A", "version B") },
|
||||
|
||||
{ type: "disable-sync", client: 1 },
|
||||
{ type: "rename", client: 0, oldPath: "data.bin", newPath: "data.md" },
|
||||
{ type: "update", client: 0, path: "data.md", content: "top line\nmiddle line\nbottom line" },
|
||||
{ type: "sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "barrier" },
|
||||
{ type: "assert-consistent", verify: (s) => s.assertFileExists("data.md") },
|
||||
{ type: "assert-consistent", verify: (s) => s.assertContent("data.md", "top line\nmiddle line\nbottom line") },
|
||||
|
||||
{ type: "disable-sync", client: 0 },
|
||||
{ type: "disable-sync", client: 1 },
|
||||
|
||||
{ type: "update", client: 0, path: "data.md", content: "top edit from 0\nmiddle line\nshared end" },
|
||||
{ type: "update", client: 1, path: "data.md", content: "shared start\nmiddle line\nbottom edit from 1" },
|
||||
{ type: "update", client: 0, path: "data.md", content: "alpha\nmiddle line\nbottom line" },
|
||||
{ type: "update", client: 1, path: "data.md", content: "top line\nmiddle line\nbeta" },
|
||||
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "assert-consistent", verify: (s) => s.assertFileCount(1).assertContains("data.md", "top edit from 0", "bottom edit from 1") },
|
||||
{ type: "assert-consistent", verify: (s) => s.assertFileCount(1).assertContains("data.md", "alpha", "beta") },
|
||||
],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -41,6 +41,6 @@ export const deleteRecreateDifferentContentTest: TestDefinition = {
|
|||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "assert-consistent", verify: (s) => s.assertFileCount(1).assertContains("A.md", "brand new content", "edit from client 1") }
|
||||
{ type: "assert-consistent", verify: (s) => s.assertFileCount(1).assertContains("A.md", "brand new", "client 1") }
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,26 +2,18 @@ import type { TestDefinition } from "../test-definition";
|
|||
|
||||
export const localEditLostDuringCreateMergeTest: TestDefinition = {
|
||||
description:
|
||||
"Client 1 creates doc.md. Client 0 creates the same file offline, then connects with the server paused. " +
|
||||
"Client 0 edits the file while the create is stalled. After resume, both clients' content must be merged.",
|
||||
"Both clients create doc.md with different content while offline. " +
|
||||
"Client 0 also edits the file before syncing. After both connect, " +
|
||||
"the merged result should contain content from both clients.",
|
||||
clients: 2,
|
||||
steps: [
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "sync", client: 1 },
|
||||
{ type: "create", client: 1, path: "doc.md", content: "from-client-1" },
|
||||
{ type: "sync", client: 1 },
|
||||
|
||||
{
|
||||
type: "create",
|
||||
client: 0,
|
||||
path: "doc.md",
|
||||
content: "from-client-0"
|
||||
},
|
||||
|
||||
{ type: "pause-server" },
|
||||
|
||||
{ type: "enable-sync", client: 0 },
|
||||
|
||||
{
|
||||
type: "update",
|
||||
client: 0,
|
||||
|
|
@ -29,12 +21,19 @@ export const localEditLostDuringCreateMergeTest: TestDefinition = {
|
|||
content: "local-edit-during-create"
|
||||
},
|
||||
|
||||
{ type: "resume-server" },
|
||||
|
||||
{ type: "sync" },
|
||||
{ type: "sync" },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "sync", client: 1 },
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "assert-consistent", verify: (s) => s.assertFileCount(1).assertContains("doc.md", "from-client-1", "local-edit-during-create") }
|
||||
{
|
||||
type: "assert-consistent",
|
||||
verify: (s) =>
|
||||
s.assertFileCount(1).assertContains(
|
||||
"doc.md",
|
||||
"from-client-1",
|
||||
"local-edit-during-create"
|
||||
),
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,10 +7,8 @@ export const offlineDeleteRemoteRenameTest: TestDefinition = {
|
|||
clients: 2,
|
||||
steps: [
|
||||
{ type: "create", client: 0, path: "A.md", content: "content-a" },
|
||||
{ type: "create", client: 0, path: "B.md", content: "content-b" },
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "disable-sync", client: 0 },
|
||||
|
|
@ -25,17 +23,13 @@ export const offlineDeleteRemoteRenameTest: TestDefinition = {
|
|||
{ type: "sync", client: 1 },
|
||||
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{
|
||||
type: "assert-consistent",
|
||||
verify: (s) => {
|
||||
s.assertFileNotExists("A.md")
|
||||
.assertContent("B.md", "content-b");
|
||||
s.ifFileExists("A_renamed.md", (s) =>
|
||||
s.assertContent("A_renamed.md", "content-a")
|
||||
);
|
||||
.assertFileNotExists("A_renamed.md");
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
import type { TestDefinition } from "../test-definition";
|
||||
|
||||
export const onlineBothCreateSamePathDeconflictTest: TestDefinition = {
|
||||
description:
|
||||
"Both clients create a file at the same path while online. " +
|
||||
"One client's create gets deconflicted by the server. " +
|
||||
"Both files must exist on both clients after convergence.",
|
||||
clients: 2,
|
||||
steps: [
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "pause-websocket", client: 1 },
|
||||
{ type: "create", client: 0, path: "A.md", content: " from-client-0 " },
|
||||
{ type: "update", client: 0, path: "A.md", content: " updated-by-0 " },
|
||||
{ type: "sync" },
|
||||
|
||||
{ type: "create", client: 1, path: "A.md", content: " from-client-1 " },
|
||||
{ type: "resume-websocket", client: 1 },
|
||||
|
||||
{ type: "barrier" },
|
||||
|
||||
{
|
||||
type: "assert-consistent",
|
||||
verify: (state) => {
|
||||
state
|
||||
.assertFileCount(1)
|
||||
.assertContains("A.md", "updated-by-0", "from-client-1 ");
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import type { TestDefinition } from "../test-definition";
|
||||
|
||||
export const onlineCreateUpdateWhileOtherCreatesSamePathTest: TestDefinition = {
|
||||
description:
|
||||
"Client 0 creates a binary file and updates it while client 1 also " +
|
||||
"creates a binary file at the same path. Both clients are online. " +
|
||||
"Both clients must end up with the same file set.",
|
||||
clients: 2,
|
||||
steps: [
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
|
||||
{ type: "pause-websocket", client: 1 },
|
||||
{ type: "create", client: 0, path: "data.bin", content: "BINARY:content-v1" },
|
||||
{ type: "update", client: 0, path: "data.bin", content: "BINARY:content-v2" },
|
||||
{ type: "create", client: 1, path: "data.bin", content: "BINARY:other-content" },
|
||||
{ type: "resume-websocket", client: 1 },
|
||||
|
||||
{ type: "barrier" },
|
||||
|
||||
{
|
||||
type: "assert-consistent", verify: (state) => {
|
||||
state.assertFileCount(2)
|
||||
.assertContains("data.bin", "content-v2")
|
||||
.assertContains("data (1).bin", "other-content");
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
@ -2,31 +2,29 @@ import type { TestDefinition } from "../test-definition";
|
|||
|
||||
export const queueResetLosesCoalescedLocalEditTest: TestDefinition = {
|
||||
description:
|
||||
"Client 1 edits a shared file, then client 0 also edits it and immediately disconnects. " +
|
||||
"After client 0 reconnects, both edits must be preserved.",
|
||||
"Client 0 goes offline, both clients edit doc.md concurrently, " +
|
||||
"then client 0 reconnects. Both edits must be preserved.",
|
||||
clients: 2,
|
||||
steps: [
|
||||
{ type: "create", client: 0, path: "doc.md", content: "original" },
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "update", client: 1, path: "doc.md", content: "from client 1" },
|
||||
{ type: "sync", client: 1 },
|
||||
|
||||
{ type: "update", client: 0, path: "doc.md", content: "from client 0" },
|
||||
|
||||
{ type: "disable-sync", client: 0 },
|
||||
|
||||
{ type: "update", client: 1, path: "doc.md", content: "alpha bravo" },
|
||||
{ type: "sync", client: 1 },
|
||||
|
||||
{ type: "update", client: 0, path: "doc.md", content: "charlie delta" },
|
||||
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{
|
||||
type: "assert-consistent",
|
||||
verify: (s) =>
|
||||
s.assertFileCount(1).assertContains("doc.md", "from client 0", "from client 1"),
|
||||
s.assertFileCount(1).assertContains("doc.md", "alpha", "charlie"),
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ export const rapidCreateUpdateDeleteCycleTest: TestDefinition = {
|
|||
},
|
||||
{ type: "delete", client: 0, path: "cycle.md" },
|
||||
|
||||
{ type: "resume-server" },
|
||||
{ type: "sync" },
|
||||
|
||||
{
|
||||
type: "create",
|
||||
client: 0,
|
||||
|
|
@ -34,8 +37,6 @@ export const rapidCreateUpdateDeleteCycleTest: TestDefinition = {
|
|||
content: "final creation"
|
||||
},
|
||||
|
||||
{ type: "resume-server" },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,24 +9,21 @@ export const recentlyDeletedClearedOnReconnectTest: TestDefinition = {
|
|||
steps: [
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "create", client: 0, path: "doc.md", content: "original" },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "delete", client: 0, path: "doc.md" },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "disable-sync", client: 0 },
|
||||
{ type: "disable-sync", client: 1 },
|
||||
|
||||
{ type: "create", client: 1, path: "doc.md", content: "new content from client 1" },
|
||||
{ type: "sync", client: 1 },
|
||||
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "sync", client: 1 },
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { TestDefinition } from "../test-definition";
|
|||
|
||||
export const renameCircularTest: TestDefinition = {
|
||||
description:
|
||||
"Client 0 creates three files, syncs, then goes offline and performs a circular rename via a temp file (A->temp, C->A, B->C, temp->B). After reconnecting, both clients should have rotated content with no temp file remaining.",
|
||||
"Client 0 creates three files, syncs, then goes offline and performs a circular rename via a temp file (A->temp, C->A, B->C, temp->B). After reconnecting, all three contents should exist across three files but paths may be deconflicted.",
|
||||
clients: 2,
|
||||
steps: [
|
||||
{ type: "create", client: 0, path: "A.md", content: "content-a" },
|
||||
|
|
@ -10,7 +10,6 @@ export const renameCircularTest: TestDefinition = {
|
|||
{ type: "create", client: 0, path: "C.md", content: "content-c" },
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
{
|
||||
type: "assert-consistent",
|
||||
|
|
|
|||
|
|
@ -4,15 +4,14 @@ export const renameSwapTest: TestDefinition = {
|
|||
description:
|
||||
"Client 0 has A.md and B.md synced. Goes offline and swaps them using " +
|
||||
"a temp file: A.md -> temp.md, B.md -> A.md, temp.md -> B.md. " +
|
||||
"When Client 0 reconnects, both clients should have swapped content. " +
|
||||
"The temp file should not exist on either client.",
|
||||
"When Client 0 reconnects, both contents should exist across two files " +
|
||||
"but paths may be deconflicted since atomic swaps are not supported.",
|
||||
clients: 2,
|
||||
steps: [
|
||||
{ type: "create", client: 0, path: "A.md", content: "content-a" },
|
||||
{ type: "create", client: 0, path: "B.md", content: "content-b" },
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
{
|
||||
type: "assert-consistent",
|
||||
|
|
@ -26,7 +25,6 @@ export const renameSwapTest: TestDefinition = {
|
|||
{ type: "rename", client: 0, oldPath: "temp.md", newPath: "B.md" },
|
||||
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{
|
||||
|
|
@ -34,6 +32,7 @@ export const renameSwapTest: TestDefinition = {
|
|||
verify: (s) =>
|
||||
s
|
||||
.assertFileNotExists("temp.md")
|
||||
.assertFileCount(2)
|
||||
.assertContent("A.md", "content-b")
|
||||
.assertContent("B.md", "content-a"),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ import type { TestDefinition } from "../test-definition";
|
|||
|
||||
export const renameToPathOfUnconfirmedDeleteTest: TestDefinition = {
|
||||
description:
|
||||
"Client 0 deletes A.md and renames B.md to A.md while offline. After reconnecting, A.md should exist with B's content and B.md should be gone.",
|
||||
"Client 0 deletes A.md then renames B.md to A.md. After syncing, " +
|
||||
"B's content should exist and the old A.md content should be gone. " +
|
||||
"The server may deconflict the path if the delete and move arrive " +
|
||||
"in the same transaction.",
|
||||
clients: 2,
|
||||
steps: [
|
||||
{
|
||||
|
|
@ -20,24 +23,19 @@ export const renameToPathOfUnconfirmedDeleteTest: TestDefinition = {
|
|||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "disable-sync", client: 0 },
|
||||
|
||||
{ type: "delete", client: 0, path: "A.md" },
|
||||
{ type: "rename", client: 0, oldPath: "B.md", newPath: "A.md" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "sync" },
|
||||
{ type: "rename", client: 0, oldPath: "B.md", newPath: "A.md" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{
|
||||
type: "assert-consistent",
|
||||
verify: (s) =>
|
||||
s
|
||||
.assertFileCount(1)
|
||||
.assertFileNotExists("B.md")
|
||||
.assertContent("A.md", "content B"),
|
||||
.assertContains("A.md", "content B"),
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { TestDefinition } from "../test-definition";
|
|||
|
||||
export const threeClientRenameCreateDeleteTest: TestDefinition = {
|
||||
description:
|
||||
"Client 0 renames X→Y, Client 1 deletes X, Client 2 creates Y. " +
|
||||
"Client 0 renames X -> Y, Client 1 deletes X, Client 2 creates Y. " +
|
||||
"All three operations happen while the other clients are offline. " +
|
||||
"Tests that the system handles the three-way conflict and converges.",
|
||||
clients: 3,
|
||||
|
|
@ -16,7 +16,6 @@ export const threeClientRenameCreateDeleteTest: TestDefinition = {
|
|||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "enable-sync", client: 2 },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "disable-sync", client: 0 },
|
||||
|
|
@ -41,7 +40,6 @@ export const threeClientRenameCreateDeleteTest: TestDefinition = {
|
|||
{ type: "sync", client: 1 },
|
||||
|
||||
{ type: "enable-sync", client: 2 },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{
|
||||
|
|
@ -49,7 +47,7 @@ export const threeClientRenameCreateDeleteTest: TestDefinition = {
|
|||
verify: (s) =>
|
||||
s
|
||||
.assertFileNotExists("X.md")
|
||||
.assertContains("Y.md", "original from A", "new from C"),
|
||||
.assertAnyFileContains("new from C"),
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
import type { TestDefinition } from "../test-definition";
|
||||
|
||||
export const updateSurvivesRemoteDeleteTest: TestDefinition = {
|
||||
export const updateDoesNotSurvivesRemoteDeleteTest: TestDefinition = {
|
||||
description:
|
||||
"Client 0 deletes a file while client 1 edits it offline. Client 0 syncs the delete first, then client 1 reconnects. The edited file should survive on both clients.",
|
||||
"Client 0 deletes a file while client 1 edits it offline. Client 0 syncs the delete first, then client 1 reconnects. Deletes always win.",
|
||||
clients: 2,
|
||||
steps: [
|
||||
{ type: "create", client: 0, path: "doc.md", content: "original" },
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{ type: "disable-sync", client: 0 },
|
||||
|
|
@ -18,16 +17,13 @@ export const updateSurvivesRemoteDeleteTest: TestDefinition = {
|
|||
{ type: "update", client: 1, path: "doc.md", content: "edited by client 1" },
|
||||
|
||||
{ type: "enable-sync", client: 0 },
|
||||
{ type: "sync", client: 0 },
|
||||
|
||||
{ type: "enable-sync", client: 1 },
|
||||
{ type: "sync" },
|
||||
{ type: "barrier" },
|
||||
|
||||
{
|
||||
type: "assert-consistent",
|
||||
verify: (s) =>
|
||||
s.assertFileCount(1).assertContains("doc.md", "edited by client 1"),
|
||||
s.assertFileCount(0)
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue