Compare commits

...

1 Commits

Author SHA1 Message Date
Guido D'Orsi
5c67363a10 fix(colist): correct successor order when handling conflicts 2025-04-30 12:16:27 +02:00
3 changed files with 63 additions and 16 deletions

View File

@@ -0,0 +1,5 @@
---
"cojson": patch
---
fix(colist): correct successor order when handling conflicts

View File

@@ -320,9 +320,8 @@ export class RawCoListView<
});
}
// traverse successors in reverse for correct insertion behavior
for (const successor of entry.successors) {
todo.push(successor);
for (let i = entry.successors.length - 1; i >= 0; i--) {
todo.push(entry.successors[i]!);
}
}
}
@@ -473,12 +472,6 @@ export class RawCoList<
after: opIDBefore,
}));
if (opIDBefore !== "start") {
// When added as successors we need to reverse the items
// to keep the same insertion order
changes.reverse();
}
this.core.makeTransaction(changes, privacy);
this.rebuildFromCore();

View File

@@ -11,10 +11,8 @@ import {
const Crypto = await WasmCrypto.create();
let jazzCloud = setupTestNode({ isSyncServer: true });
beforeEach(async () => {
jazzCloud = setupTestNode({ isSyncServer: true });
setupTestNode({ isSyncServer: true });
});
test("Empty CoList works", () => {
@@ -128,11 +126,11 @@ test("appendItems at index", () => {
content.appendItems(["hello", "world"], 0, "trusting");
expect(content.toJSON()).toEqual([
"first",
"hello",
"world",
"second",
"third",
"fourth",
"hello",
"world",
]);
});
@@ -252,7 +250,7 @@ test("mixing prepend and append", () => {
expect(list.toJSON()).toEqual([1, 2, 3]);
});
test.skip("Items appended to start", () => {
test("Items appended to start", () => {
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
const coValue = node.createCoValue({
@@ -271,7 +269,7 @@ test.skip("Items appended to start", () => {
expect(content.toJSON()).toEqual(["first", "second", "third"]);
});
test.skip("syncing changes with an older timestamp", async () => {
test("syncing appends with an older timestamp", async () => {
const client = setupTestNode({
connected: true,
});
@@ -312,8 +310,59 @@ test.skip("syncing changes with an older timestamp", async () => {
otherClient.connectToSyncServer();
await waitFor(() => {
list.rebuildFromCore(); // TODO: This is a hack to ensure the list is rebuilt, we can remove it after the incremental processing lands
expect(list.toJSON()).toEqual([1, 2, 3, 5, 4, 6]);
});
listOnOtherClient.rebuildFromCore();
expect(listOnOtherClient.toJSON()).toEqual(list.toJSON());
});
test("syncing prepends with an older timestamp", async () => {
const client = setupTestNode({
connected: true,
});
const otherClient = setupTestNode({});
const otherClientConnection = otherClient.connectToSyncServer();
const coValue = client.node.createCoValue({
type: "colist",
ruleset: { type: "unsafeAllowAll" },
meta: null,
...Crypto.createdNowUnique(),
});
const list = expectList(coValue.getCurrentContent());
list.prepend(1, undefined, "trusting");
list.prepend(2, undefined, "trusting");
const listOnOtherClient = await loadCoValueOrFail(otherClient.node, list.id);
otherClientConnection.peerState.gracefulShutdown();
listOnOtherClient.prepend(3, undefined, "trusting");
await new Promise((resolve) => setTimeout(resolve, 1));
list.prepend(4, undefined, "trusting");
await new Promise((resolve) => setTimeout(resolve, 1));
listOnOtherClient.prepend(5, undefined, "trusting");
await new Promise((resolve) => setTimeout(resolve, 1));
list.prepend(6, undefined, "trusting");
otherClient.connectToSyncServer();
await waitFor(() => {
list.rebuildFromCore(); // TODO: This is a hack to ensure the list is rebuilt, we can remove it after the incremental processing lands
expect(list.toJSON()).toEqual([6, 4, 5, 3, 2, 1]);
});
listOnOtherClient.rebuildFromCore();
expect(listOnOtherClient.toJSON()).toEqual(list.toJSON());
});