cojson 0.9.19 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { afterEach, describe, expect, test, vi } from "vitest";
1
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
2
2
  import { expectMap } from "../coValue.js";
3
3
  import type { CoValueHeader } from "../coValueCore.js";
4
4
  import type { RawAccountID } from "../coValues/account.js";
@@ -12,17 +12,27 @@ import { connectedPeers, newQueuePair } from "../streamUtils.js";
12
12
  import type { SyncMessage } from "../sync.js";
13
13
  import {
14
14
  blockMessageTypeOnOutgoingPeer,
15
+ connectNodeToSyncServer,
15
16
  connectTwoPeers,
17
+ createConnectedTestAgentNode,
18
+ createConnectedTestNode,
16
19
  createTestMetricReader,
17
20
  createTestNode,
18
21
  loadCoValueOrFail,
19
22
  randomAnonymousAccountAndSessionID,
23
+ setupSyncServer,
20
24
  tearDownTestMetricReader,
21
25
  waitFor,
22
26
  } from "./testUtils.js";
23
27
 
24
28
  const Crypto = await WasmCrypto.create();
25
29
 
30
+ let jazzCloud = setupSyncServer();
31
+
32
+ beforeEach(async () => {
33
+ jazzCloud = setupSyncServer();
34
+ });
35
+
26
36
  test("Node replies with initial tx and header to empty subscribe", async () => {
27
37
  const [admin, session] = randomAnonymousAccountAndSessionID();
28
38
  const node = new LocalNode(admin, session, Crypto);
@@ -861,111 +871,34 @@ test.skip("When loading a coValue on one node, the server node it is requested f
861
871
  });
862
872
 
863
873
  test("Can sync a coValue through a server to another client", async () => {
864
- const [admin, session] = randomAnonymousAccountAndSessionID();
865
-
866
- const client1 = new LocalNode(admin, session, Crypto);
874
+ const { node: client1 } = await createConnectedTestNode();
867
875
 
868
876
  const group = client1.createGroup();
869
877
 
870
878
  const map = group.createMap();
871
879
  map.set("hello", "world", "trusting");
872
880
 
873
- const [serverUser, serverSession] = randomAnonymousAccountAndSessionID();
874
-
875
- const server = new LocalNode(serverUser, serverSession, Crypto);
876
-
877
- const [serverAsPeerForClient1, client1AsPeer] = connectedPeers(
878
- "serverFor1",
879
- "client1",
880
- {
881
- peer1role: "server",
882
- peer2role: "client",
883
- // trace: true,
884
- },
885
- );
886
-
887
- client1.syncManager.addPeer(serverAsPeerForClient1);
888
- server.syncManager.addPeer(client1AsPeer);
889
-
890
- const client2 = new LocalNode(
891
- admin,
892
- Crypto.newRandomSessionID(admin.id),
893
- Crypto,
894
- );
895
-
896
- const [serverAsPeerForClient2, client2AsPeer] = connectedPeers(
897
- "serverFor2",
898
- "client2",
899
- {
900
- peer1role: "server",
901
- peer2role: "client",
902
- // trace: true,
903
- },
904
- );
905
-
906
- client2.syncManager.addPeer(serverAsPeerForClient2);
907
- server.syncManager.addPeer(client2AsPeer);
881
+ const { node: client2 } = await createConnectedTestNode();
908
882
 
909
- const mapOnClient2 = await client2.loadCoValueCore(map.core.id);
910
- if (mapOnClient2 === "unavailable") {
911
- throw new Error("Map is unavailable");
912
- }
883
+ const mapOnClient2 = await loadCoValueOrFail(client2, map.id);
913
884
 
914
- expect(expectMap(mapOnClient2.getCurrentContent()).get("hello")).toEqual(
915
- "world",
916
- );
885
+ expect(mapOnClient2.get("hello")).toEqual("world");
917
886
  });
918
887
 
919
888
  test("Can sync a coValue with private transactions through a server to another client", async () => {
920
- const [admin, session] = randomAnonymousAccountAndSessionID();
921
-
922
- const client1 = new LocalNode(admin, session, Crypto);
889
+ const { node: client1 } = await createConnectedTestNode();
923
890
 
924
891
  const group = client1.createGroup();
925
892
 
926
893
  const map = group.createMap();
927
894
  map.set("hello", "world", "private");
895
+ group.addMember("everyone", "reader");
928
896
 
929
- const [serverUser, serverSession] = randomAnonymousAccountAndSessionID();
930
-
931
- const server = new LocalNode(serverUser, serverSession, Crypto);
932
-
933
- const [serverAsPeer, client1AsPeer] = connectedPeers("server", "client1", {
934
- // trace: true,
935
- peer1role: "server",
936
- peer2role: "client",
937
- });
938
-
939
- client1.syncManager.addPeer(serverAsPeer);
940
- server.syncManager.addPeer(client1AsPeer);
897
+ const { node: client2 } = await createConnectedTestNode();
941
898
 
942
- const client2 = new LocalNode(
943
- admin,
944
- client1.crypto.newRandomSessionID(admin.id),
945
- Crypto,
946
- );
947
-
948
- const [serverAsOtherPeer, client2AsPeer] = connectedPeers(
949
- "server",
950
- "client2",
951
- {
952
- // trace: true,
953
- peer1role: "server",
954
- peer2role: "client",
955
- },
956
- );
899
+ const mapOnClient2 = await loadCoValueOrFail(client2, map.id);
957
900
 
958
- client2.syncManager.addPeer(serverAsOtherPeer);
959
- server.syncManager.addPeer(client2AsPeer);
960
-
961
- const mapOnClient2 = await client2.loadCoValueCore(map.core.id);
962
- if (mapOnClient2 === "unavailable") {
963
- throw new Error("Map is unavailable");
964
- }
965
-
966
- expect(expectMap(mapOnClient2.getCurrentContent()).get("hello")).toEqual(
967
- "world",
968
- );
901
+ expect(mapOnClient2.get("hello")).toEqual("world");
969
902
  });
970
903
 
971
904
  test.skip("When a peer's incoming/readable stream closes, we remove the peer", async () => {
@@ -1023,111 +956,32 @@ test.skip("When a peer's incoming/readable stream closes, we remove the peer", a
1023
956
  */
1024
957
  });
1025
958
 
1026
- test.skip("When a peer's outgoing/writable stream closes, we remove the peer", async () => {
1027
- /*
1028
- const [admin, session] = randomAnonymousAccountAndSessionID();
1029
- const node = new LocalNode(admin, session, Crypto);
1030
-
1031
- const group = node.createGroup();
1032
-
1033
- const [inRx] = await Effect.runPromise(newStreamPair());
1034
- const [outRx, outTx] = await Effect.runPromise(newStreamPair());
1035
-
1036
- node.syncManager.addPeer({
1037
- id: "test",
1038
- incoming: inRx,
1039
- outgoing: outTx,
1040
- role: "server",
1041
- });
1042
-
1043
- // expect(yield* Queue.take(outRxQ)).toMatchObject({
1044
- // action: "load",
1045
- // id: admin.id,
1046
- // });
1047
- expect(yield * Queue.take(outRxQ)).toMatchObject({
1048
- action: "load",
1049
- id: group.core.id,
1050
- });
1051
-
1052
- const map = group.createMap();
1053
-
1054
- const mapSubscribeMsg = await reader.read();
1055
-
1056
- expect(mapSubscribeMsg.value).toEqual({
1057
- action: "load",
1058
- ...map.core.knownState(),
1059
- } satisfies SyncMessage);
1060
-
1061
- // expect(yield* Queue.take(outRxQ)).toMatchObject(admContEx(admin.id));
1062
- expect(yield * Queue.take(outRxQ)).toMatchObject(groupContentEx(group));
1063
-
1064
- const mapContentMsg = await reader.read();
1065
-
1066
- expect(mapContentMsg.value).toEqual({
1067
- action: "content",
1068
- id: map.core.id,
1069
- header: map.core.header,
1070
- new: {},
1071
- } satisfies SyncMessage);
1072
-
1073
- reader.releaseLock();
1074
- await outRx.cancel();
1075
-
1076
- map.set("hello", "world", "trusting");
1077
-
1078
- await new Promise((resolve) => setTimeout(resolve, 100));
1079
-
1080
- expect(node.syncManager.peers["test"]).toBeUndefined();
1081
- */
1082
- });
1083
-
1084
959
  test("If we start loading a coValue before connecting to a peer that has it, it will load it once we connect", async () => {
1085
- const [admin, session] = randomAnonymousAccountAndSessionID();
1086
-
1087
- const node1 = new LocalNode(admin, session, Crypto);
960
+ const { node: node1 } = await createConnectedTestNode();
1088
961
 
1089
962
  const group = node1.createGroup();
1090
963
 
1091
964
  const map = group.createMap();
1092
965
  map.set("hello", "world", "trusting");
1093
966
 
1094
- const node2 = new LocalNode(
1095
- admin,
1096
- Crypto.newRandomSessionID(admin.id),
1097
- Crypto,
1098
- );
1099
-
1100
- const [node1asPeer, node2asPeer] = connectedPeers("peer1", "peer2", {
1101
- peer1role: "server",
1102
- peer2role: "client",
1103
- // trace: true,
1104
- });
967
+ const node2 = createTestNode();
1105
968
 
1106
- node1.syncManager.addPeer(node2asPeer);
1107
-
1108
- const mapOnNode2Promise = node2.loadCoValueCore(map.core.id);
969
+ const mapOnNode2Promise = loadCoValueOrFail(node2, map.id);
1109
970
 
1110
971
  expect(node2.coValuesStore.get(map.core.id).state.type).toEqual("unknown");
1111
972
 
1112
- node2.syncManager.addPeer(node1asPeer);
973
+ connectNodeToSyncServer(node2);
1113
974
 
1114
975
  const mapOnNode2 = await mapOnNode2Promise;
1115
- if (mapOnNode2 === "unavailable") {
1116
- throw new Error("Map is unavailable");
1117
- }
1118
976
 
1119
- expect(expectMap(mapOnNode2.getCurrentContent()).get("hello")).toEqual(
1120
- "world",
1121
- );
977
+ expect(mapOnNode2.get("hello")).toEqual("world");
1122
978
  });
1123
979
 
1124
980
  test("should keep the peer state when the peer closes", async () => {
1125
- const {
1126
- client,
1127
- jazzCloud,
1128
- jazzCloudConnectionAsPeer,
1129
- connectionWithClientAsPeer,
1130
- } = createTwoConnectedNodes();
981
+ const client = createTestNode();
982
+
983
+ const { nodeToServerPeer, serverToNodePeer } =
984
+ connectNodeToSyncServer(client);
1131
985
 
1132
986
  const group = jazzCloud.createGroup();
1133
987
  const map = group.createMap();
@@ -1136,25 +990,23 @@ test("should keep the peer state when the peer closes", async () => {
1136
990
  await client.loadCoValueCore(map.core.id);
1137
991
 
1138
992
  const syncManager = client.syncManager;
1139
- const peerState = syncManager.peers[jazzCloudConnectionAsPeer.id];
993
+ const peerState = syncManager.peers[nodeToServerPeer.id];
1140
994
 
1141
995
  // @ts-expect-error Simulating a peer closing, leveraging the direct connection between the client/server peers
1142
- await connectionWithClientAsPeer.outgoing.push("Disconnected");
996
+ await serverToNodePeer.outgoing.push("Disconnected");
1143
997
 
1144
998
  await waitFor(() => peerState?.closed);
1145
999
 
1146
- expect(syncManager.peers[jazzCloudConnectionAsPeer.id]).not.toBeUndefined();
1000
+ expect(syncManager.peers[nodeToServerPeer.id]).not.toBeUndefined();
1147
1001
  });
1148
1002
 
1149
1003
  test("should delete the peer state when the peer closes if deletePeerStateOnClose is true", async () => {
1150
- const {
1151
- client,
1152
- jazzCloud,
1153
- jazzCloudConnectionAsPeer,
1154
- connectionWithClientAsPeer,
1155
- } = createTwoConnectedNodes();
1004
+ const client = createTestNode();
1005
+
1006
+ const { nodeToServerPeer, serverToNodePeer } =
1007
+ connectNodeToSyncServer(client);
1156
1008
 
1157
- jazzCloudConnectionAsPeer.deletePeerStateOnClose = true;
1009
+ nodeToServerPeer.deletePeerStateOnClose = true;
1158
1010
 
1159
1011
  const group = jazzCloud.createGroup();
1160
1012
  const map = group.createMap();
@@ -1164,14 +1016,14 @@ test("should delete the peer state when the peer closes if deletePeerStateOnClos
1164
1016
 
1165
1017
  const syncManager = client.syncManager;
1166
1018
 
1167
- const peerState = syncManager.peers[jazzCloudConnectionAsPeer.id];
1019
+ const peerState = syncManager.peers[nodeToServerPeer.id];
1168
1020
 
1169
1021
  // @ts-expect-error Simulating a peer closing, leveraging the direct connection between the client/server peers
1170
- await connectionWithClientAsPeer.outgoing.push("Disconnected");
1022
+ await serverToNodePeer.outgoing.push("Disconnected");
1171
1023
 
1172
1024
  await waitFor(() => peerState?.closed);
1173
1025
 
1174
- expect(syncManager.peers[jazzCloudConnectionAsPeer.id]).toBeUndefined();
1026
+ expect(syncManager.peers[nodeToServerPeer.id]).toBeUndefined();
1175
1027
  });
1176
1028
 
1177
1029
  describe("sync - extra tests", () => {
@@ -1624,29 +1476,6 @@ describe("sync - extra tests", () => {
1624
1476
  });
1625
1477
  });
1626
1478
 
1627
- function createTwoConnectedNodes() {
1628
- // Setup nodes
1629
- const client = createTestNode();
1630
- const jazzCloud = createTestNode();
1631
-
1632
- // Connect nodes initially
1633
- const [connectionWithClientAsPeer, jazzCloudConnectionAsPeer] =
1634
- connectedPeers("connectionWithClient", "jazzCloudConnection", {
1635
- peer1role: "client",
1636
- peer2role: "server",
1637
- });
1638
-
1639
- client.syncManager.addPeer(jazzCloudConnectionAsPeer);
1640
- jazzCloud.syncManager.addPeer(connectionWithClientAsPeer);
1641
-
1642
- return {
1643
- client,
1644
- jazzCloud,
1645
- connectionWithClientAsPeer,
1646
- jazzCloudConnectionAsPeer,
1647
- };
1648
- }
1649
-
1650
1479
  test("a value created on one node can be loaded on anotehr node even if not directly connected", async () => {
1651
1480
  const userA = createTestNode();
1652
1481
  const userB = createTestNode();
@@ -1671,7 +1500,7 @@ test("a value created on one node can be loaded on anotehr node even if not dire
1671
1500
 
1672
1501
  describe("SyncManager - knownStates vs optimisticKnownStates", () => {
1673
1502
  test("knownStates and optimisticKnownStates are the same when the coValue is fully synced", async () => {
1674
- const { client, jazzCloud } = createTwoConnectedNodes();
1503
+ const { node: client } = await createConnectedTestNode();
1675
1504
 
1676
1505
  // Create test data
1677
1506
  const group = client.createGroup();
@@ -1683,9 +1512,8 @@ describe("SyncManager - knownStates vs optimisticKnownStates", () => {
1683
1512
  // Wait for the full sync to complete
1684
1513
  await mapOnClient.core.waitForSync();
1685
1514
 
1686
- const peerStateClient = client.syncManager.peers["jazzCloudConnection"]!;
1687
- const peerStateJazzCloud =
1688
- jazzCloud.syncManager.peers["connectionWithClient"]!;
1515
+ const peerStateClient = client.syncManager.getPeers()[0]!;
1516
+ const peerStateJazzCloud = jazzCloud.syncManager.getPeers()[0]!;
1689
1517
 
1690
1518
  // The optimisticKnownStates should be the same as the knownStates after the full sync is complete
1691
1519
  expect(
@@ -1699,7 +1527,7 @@ describe("SyncManager - knownStates vs optimisticKnownStates", () => {
1699
1527
  });
1700
1528
 
1701
1529
  test("optimisticKnownStates is updated as new transactions are sent, while knownStates only when the updates are acknowledged", async () => {
1702
- const { client, jazzCloudConnectionAsPeer } = createTwoConnectedNodes();
1530
+ const { node: client, nodeToServerPeer } = await createConnectedTestNode();
1703
1531
 
1704
1532
  // Create test data and sync the first change
1705
1533
  // We want that both the nodes know about the coValue so we can test
@@ -1717,7 +1545,7 @@ describe("SyncManager - knownStates vs optimisticKnownStates", () => {
1717
1545
  // while knownStates is only updated when we receive the "known" messages
1718
1546
  // that are acknowledging the receipt of the content messages
1719
1547
  const outgoing = blockMessageTypeOnOutgoingPeer(
1720
- jazzCloudConnectionAsPeer,
1548
+ nodeToServerPeer,
1721
1549
  "content",
1722
1550
  );
1723
1551
 
@@ -1725,8 +1553,7 @@ describe("SyncManager - knownStates vs optimisticKnownStates", () => {
1725
1553
 
1726
1554
  await client.syncManager.actuallySyncCoValue(map.core);
1727
1555
 
1728
- const peerState = client.syncManager.peers["jazzCloudConnection"]!;
1729
-
1556
+ const peerState = client.syncManager.peers[nodeToServerPeer.id]!;
1730
1557
  expect(peerState.optimisticKnownStates.get(map.core.id)).not.toEqual(
1731
1558
  peerState.knownStates.get(map.core.id),
1732
1559
  );
@@ -1747,7 +1574,7 @@ describe("SyncManager - knownStates vs optimisticKnownStates", () => {
1747
1574
 
1748
1575
  describe("SyncManager.addPeer", () => {
1749
1576
  test("new peer gets a copy of previous peer's knownStates when replacing it", async () => {
1750
- const { client } = createTwoConnectedNodes();
1577
+ const { node: client } = await createConnectedTestNode();
1751
1578
 
1752
1579
  // Create test data
1753
1580
  const group = client.createGroup();
@@ -1759,26 +1586,24 @@ describe("SyncManager.addPeer", () => {
1759
1586
  // Wait for initial sync
1760
1587
  await map.core.waitForSync();
1761
1588
 
1589
+ const firstPeerState = client.syncManager.getPeers()[0]!;
1590
+
1762
1591
  // Store the initial known states
1763
- const initialKnownStates =
1764
- client.syncManager.peers["jazzCloudConnection"]!.knownStates;
1592
+ const initialKnownStates = firstPeerState.knownStates;
1765
1593
 
1766
1594
  // Create new connection with same ID
1767
- const [jazzCloudConnectionAsPeer2] = connectedPeers(
1768
- "jazzCloudConnection",
1769
- "unusedPeer",
1770
- {
1771
- peer1role: "server",
1772
- peer2role: "client",
1773
- },
1774
- );
1595
+ const [secondPeer] = connectedPeers(firstPeerState.id, "unusedPeer", {
1596
+ peer1role: "server",
1597
+ peer2role: "client",
1598
+ });
1775
1599
 
1776
1600
  // Add new peer with same ID
1777
- client.syncManager.addPeer(jazzCloudConnectionAsPeer2);
1601
+ client.syncManager.addPeer(secondPeer);
1602
+
1603
+ const newPeerState = client.syncManager.getPeers()[0]!;
1778
1604
 
1779
1605
  // Verify that the new peer has a copy of the previous known states
1780
- const newPeerKnownStates =
1781
- client.syncManager.peers["jazzCloudConnection"]!.knownStates;
1606
+ const newPeerKnownStates = newPeerState.knownStates;
1782
1607
 
1783
1608
  expect(newPeerKnownStates).not.toBe(initialKnownStates); // Should be a different instance
1784
1609
  expect(newPeerKnownStates.get(map.core.id)).toEqual(
@@ -1787,7 +1612,7 @@ describe("SyncManager.addPeer", () => {
1787
1612
  });
1788
1613
 
1789
1614
  test("new peer with new ID starts with empty knownStates", async () => {
1790
- const { client } = createTwoConnectedNodes();
1615
+ const { node: client } = await createConnectedTestNode();
1791
1616
 
1792
1617
  // Create test data
1793
1618
  const group = client.createGroup();
@@ -1815,23 +1640,19 @@ describe("SyncManager.addPeer", () => {
1815
1640
  });
1816
1641
 
1817
1642
  test("when adding a peer with the same ID as a previous peer, the previous peer is closed", async () => {
1818
- const { client } = createTwoConnectedNodes();
1643
+ const { node: client } = await createConnectedTestNode();
1819
1644
 
1820
1645
  // Store reference to first peer
1821
- const firstPeer = client.syncManager.peers["jazzCloudConnection"]!;
1646
+ const firstPeer = client.syncManager.getPeers()[0]!;
1822
1647
  const closeSpy = vi.spyOn(firstPeer, "gracefulShutdown");
1823
1648
 
1824
1649
  // Create and add replacement peer
1825
- const [jazzCloudConnectionAsPeer2] = connectedPeers(
1826
- "jazzCloudConnection",
1827
- "unusedPeer",
1828
- {
1829
- peer1role: "server",
1830
- peer2role: "client",
1831
- },
1832
- );
1650
+ const [secondPeer] = connectedPeers(firstPeer.id, "unusedPeer", {
1651
+ peer1role: "server",
1652
+ peer2role: "client",
1653
+ });
1833
1654
 
1834
- client.syncManager.addPeer(jazzCloudConnectionAsPeer2);
1655
+ client.syncManager.addPeer(secondPeer);
1835
1656
 
1836
1657
  // Verify thet the first peer had ben closed correctly
1837
1658
  expect(closeSpy).toHaveBeenCalled();
@@ -1839,25 +1660,21 @@ describe("SyncManager.addPeer", () => {
1839
1660
  });
1840
1661
 
1841
1662
  test("when adding a peer with the same ID as a previous peer and the previous peer is closed, do not attempt to close it again", async () => {
1842
- const { client } = createTwoConnectedNodes();
1663
+ const { node: client } = await createConnectedTestNode();
1843
1664
 
1844
1665
  // Store reference to first peer
1845
- const firstPeer = client.syncManager.peers["jazzCloudConnection"]!;
1666
+ const firstPeer = client.syncManager.getPeers()[0]!;
1846
1667
 
1847
1668
  firstPeer.gracefulShutdown();
1848
1669
  const closeSpy = vi.spyOn(firstPeer, "gracefulShutdown");
1849
1670
 
1850
1671
  // Create and add replacement peer
1851
- const [jazzCloudConnectionAsPeer2] = connectedPeers(
1852
- "jazzCloudConnection",
1853
- "unusedPeer",
1854
- {
1855
- peer1role: "server",
1856
- peer2role: "client",
1857
- },
1858
- );
1672
+ const [secondPeer] = connectedPeers(firstPeer.id, "unusedPeer", {
1673
+ peer1role: "server",
1674
+ peer2role: "client",
1675
+ });
1859
1676
 
1860
- client.syncManager.addPeer(jazzCloudConnectionAsPeer2);
1677
+ client.syncManager.addPeer(secondPeer);
1861
1678
 
1862
1679
  // Verify thet the first peer had not been closed again
1863
1680
  expect(closeSpy).not.toHaveBeenCalled();
@@ -1865,24 +1682,15 @@ describe("SyncManager.addPeer", () => {
1865
1682
  });
1866
1683
 
1867
1684
  test("when adding a server peer the local coValues should be sent to it", async () => {
1868
- // Setup nodes
1869
- const client = createTestNode();
1870
- const jazzCloud = createTestNode();
1871
-
1872
- // Connect nodes initially
1873
- const [connectionWithClientAsPeer, jazzCloudConnectionAsPeer] =
1874
- connectedPeers("connectionWithClient", "jazzCloudConnection", {
1875
- peer1role: "client",
1876
- peer2role: "server",
1877
- });
1878
-
1879
- jazzCloud.syncManager.addPeer(connectionWithClientAsPeer);
1685
+ const { node: client, addServerPeer } = await createConnectedTestNode({
1686
+ connected: false,
1687
+ });
1880
1688
 
1881
1689
  const group = client.createGroup();
1882
1690
  const map = group.createMap();
1883
1691
  map.set("key1", "value1", "trusting");
1884
1692
 
1885
- client.syncManager.addPeer(jazzCloudConnectionAsPeer);
1693
+ addServerPeer();
1886
1694
 
1887
1695
  await map.core.waitForSync();
1888
1696
 
@@ -1892,18 +1700,8 @@ describe("SyncManager.addPeer", () => {
1892
1700
 
1893
1701
  describe("loadCoValueCore with retry", () => {
1894
1702
  test("should load the value if available on the server", async () => {
1895
- const { client, jazzCloud } = createTwoConnectedNodes();
1896
-
1897
- const anotherClient = createTestNode();
1898
- const [
1899
- connectionWithAnotherClientAsPeer,
1900
- jazzCloudConnectionAsPeerForAnotherClient,
1901
- ] = connectedPeers("connectionWithAnotherClient", "jazzCloudConnection", {
1902
- peer1role: "client",
1903
- peer2role: "server",
1904
- });
1905
-
1906
- jazzCloud.syncManager.addPeer(connectionWithAnotherClientAsPeer);
1703
+ const { node: client } = await createConnectedTestNode();
1704
+ const { node: anotherClient } = await createConnectedTestNode();
1907
1705
 
1908
1706
  const group = anotherClient.createGroup();
1909
1707
  const map = group.createMap();
@@ -1911,25 +1709,12 @@ describe("loadCoValueCore with retry", () => {
1911
1709
 
1912
1710
  const promise = client.loadCoValueCore(map.id);
1913
1711
 
1914
- anotherClient.syncManager.addPeer(
1915
- jazzCloudConnectionAsPeerForAnotherClient,
1916
- );
1917
1712
  await expect(promise).resolves.not.toBe("unavailable");
1918
1713
  });
1919
1714
 
1920
1715
  test("should handle correctly two subsequent loads", async () => {
1921
- const { client, jazzCloud } = createTwoConnectedNodes();
1922
-
1923
- const anotherClient = createTestNode();
1924
- const [
1925
- connectionWithAnotherClientAsPeer,
1926
- jazzCloudConnectionAsPeerForAnotherClient,
1927
- ] = connectedPeers("connectionWithAnotherClient", "jazzCloudConnection", {
1928
- peer1role: "client",
1929
- peer2role: "server",
1930
- });
1931
-
1932
- jazzCloud.syncManager.addPeer(connectionWithAnotherClientAsPeer);
1716
+ const { node: client } = await createConnectedTestNode();
1717
+ const { node: anotherClient } = await createConnectedTestNode();
1933
1718
 
1934
1719
  const group = anotherClient.createGroup();
1935
1720
  const map = group.createMap();
@@ -1938,10 +1723,6 @@ describe("loadCoValueCore with retry", () => {
1938
1723
  const promise1 = client.loadCoValueCore(map.id);
1939
1724
  const promise2 = client.loadCoValueCore(map.id);
1940
1725
 
1941
- anotherClient.syncManager.addPeer(
1942
- jazzCloudConnectionAsPeerForAnotherClient,
1943
- );
1944
-
1945
1726
  await expect(promise1).resolves.not.toBe("unavailable");
1946
1727
  await expect(promise2).resolves.not.toBe("unavailable");
1947
1728
  });
@@ -1949,8 +1730,7 @@ describe("loadCoValueCore with retry", () => {
1949
1730
 
1950
1731
  describe("waitForSyncWithPeer", () => {
1951
1732
  test("should resolve when the coValue is fully uploaded into the peer", async () => {
1952
- const { client, jazzCloudConnectionAsPeer: peer } =
1953
- createTwoConnectedNodes();
1733
+ const { node: client } = await createConnectedTestNode();
1954
1734
 
1955
1735
  // Create test data
1956
1736
  const group = client.createGroup();
@@ -1959,21 +1739,32 @@ describe("waitForSyncWithPeer", () => {
1959
1739
 
1960
1740
  await client.syncManager.actuallySyncCoValue(map.core);
1961
1741
 
1742
+ const peer = client.syncManager.getPeers()[0];
1743
+
1744
+ if (!peer) {
1745
+ throw new Error("No peer found");
1746
+ }
1747
+
1962
1748
  await expect(
1963
1749
  client.syncManager.waitForSyncWithPeer(peer.id, map.core.id, 100),
1964
1750
  ).resolves.toBe(true);
1965
1751
  });
1966
1752
 
1967
1753
  test("should not resolve when the coValue is not synced", async () => {
1968
- const { client, jazzCloudConnectionAsPeer: peer } =
1969
- createTwoConnectedNodes();
1754
+ const { node: client } = await createConnectedTestNode();
1755
+
1756
+ const peer = client.syncManager.getPeers()[0];
1757
+
1758
+ if (!peer) {
1759
+ throw new Error("No peer found");
1760
+ }
1970
1761
 
1971
1762
  // Create test data
1972
1763
  const group = client.createGroup();
1973
1764
  const map = group.createMap();
1974
1765
  map.set("key1", "value1", "trusting");
1975
1766
 
1976
- vi.spyOn(peer.outgoing, "push").mockImplementation(async () => {
1767
+ vi.spyOn(peer, "pushOutgoingMessage").mockImplementation(async () => {
1977
1768
  return Promise.resolve();
1978
1769
  });
1979
1770
 
@@ -1986,7 +1777,7 @@ describe("waitForSyncWithPeer", () => {
1986
1777
  });
1987
1778
 
1988
1779
  test("Should not crash when syncing an unknown coValue type", async () => {
1989
- const { client, jazzCloud } = createTwoConnectedNodes();
1780
+ const { node: client } = await createConnectedTestNode();
1990
1781
 
1991
1782
  const coValue = client.createCoValue({
1992
1783
  type: "ooops" as any,
@@ -1997,8 +1788,10 @@ test("Should not crash when syncing an unknown coValue type", async () => {
1997
1788
 
1998
1789
  await coValue.waitForSync();
1999
1790
 
1791
+ const { node: anotherClient } = await createConnectedTestNode();
1792
+
2000
1793
  const coValueOnTheOtherNode = await loadCoValueOrFail(
2001
- jazzCloud,
1794
+ anotherClient,
2002
1795
  coValue.getCurrentContent().id,
2003
1796
  );
2004
1797
  expect(coValueOnTheOtherNode.id).toBe(coValue.id);
@@ -2092,6 +1885,163 @@ describe("metrics", () => {
2092
1885
  });
2093
1886
  });
2094
1887
 
1888
+ describe("sync protocol", () => {
1889
+ test("should have the correct messages exchanged between client and server", async () => {
1890
+ // Creating the account from agent to simplify the messages exchange
1891
+ const { node: client, messages } = await createConnectedTestAgentNode();
1892
+
1893
+ const group = client.createGroup();
1894
+ const map = group.createMap();
1895
+ map.set("hello", "world", "trusting");
1896
+
1897
+ const mapOnJazzCloud = await loadCoValueOrFail(jazzCloud, map.id);
1898
+ expect(mapOnJazzCloud.get("hello")).toEqual("world");
1899
+
1900
+ expect(messages).toEqual([
1901
+ {
1902
+ from: "client",
1903
+ msg: {
1904
+ action: "load",
1905
+ header: true,
1906
+ id: group.id,
1907
+ sessions: {
1908
+ [client.currentSessionID]: 3,
1909
+ },
1910
+ },
1911
+ },
1912
+ {
1913
+ from: "server",
1914
+ msg: {
1915
+ action: "load",
1916
+ header: false,
1917
+ id: group.id,
1918
+ sessions: {},
1919
+ },
1920
+ },
1921
+ {
1922
+ from: "client",
1923
+ msg: {
1924
+ action: "load",
1925
+ header: true,
1926
+ id: map.id,
1927
+ sessions: {
1928
+ [client.currentSessionID]: 1,
1929
+ },
1930
+ },
1931
+ },
1932
+ {
1933
+ from: "server",
1934
+ msg: {
1935
+ action: "load",
1936
+ header: false,
1937
+ id: map.id,
1938
+ sessions: {},
1939
+ },
1940
+ },
1941
+ {
1942
+ from: "client",
1943
+ msg: {
1944
+ action: "content",
1945
+ header: {
1946
+ createdAt: expect.any(String),
1947
+ meta: null,
1948
+ ruleset: {
1949
+ initialAdmin: client.account.id,
1950
+ type: "group",
1951
+ },
1952
+ type: "comap",
1953
+ uniqueness: expect.any(String),
1954
+ },
1955
+ id: group.id,
1956
+ new: {
1957
+ [client.currentSessionID]: {
1958
+ after: 0,
1959
+ lastSignature: expect.any(String),
1960
+ newTransactions: expect.any(Array),
1961
+ },
1962
+ },
1963
+ priority: 0,
1964
+ },
1965
+ },
1966
+ {
1967
+ from: "client",
1968
+ msg: {
1969
+ action: "content",
1970
+ header: {
1971
+ createdAt: expect.any(String),
1972
+ meta: null,
1973
+ ruleset: {
1974
+ group: group.id,
1975
+ type: "ownedByGroup",
1976
+ },
1977
+ type: "comap",
1978
+ uniqueness: expect.any(String),
1979
+ },
1980
+ id: map.id,
1981
+ new: {
1982
+ [client.currentSessionID]: {
1983
+ after: 0,
1984
+ lastSignature: expect.any(String),
1985
+ newTransactions: expect.any(Array),
1986
+ },
1987
+ },
1988
+ priority: 3,
1989
+ },
1990
+ },
1991
+ {
1992
+ from: "server",
1993
+ msg: {
1994
+ action: "known",
1995
+ header: true,
1996
+ id: group.id,
1997
+ sessions: {
1998
+ [client.currentSessionID]: 3,
1999
+ },
2000
+ },
2001
+ },
2002
+ {
2003
+ // TODO: This is a redundant message, we should remove it
2004
+ from: "client",
2005
+ msg: {
2006
+ action: "content",
2007
+ header: {
2008
+ createdAt: expect.any(String),
2009
+ meta: null,
2010
+ ruleset: {
2011
+ group: group.id,
2012
+ type: "ownedByGroup",
2013
+ },
2014
+ type: "comap",
2015
+ uniqueness: expect.any(String),
2016
+ },
2017
+ id: map.id,
2018
+ new: {
2019
+ [client.currentSessionID]: {
2020
+ after: 0,
2021
+ lastSignature: expect.any(String),
2022
+ newTransactions: expect.any(Array),
2023
+ },
2024
+ },
2025
+ priority: 3,
2026
+ },
2027
+ },
2028
+ {
2029
+ // TODO: This is a redundant message, we should remove it
2030
+ from: "server",
2031
+ msg: {
2032
+ action: "known",
2033
+ asDependencyOf: undefined,
2034
+ header: true,
2035
+ id: group.id,
2036
+ sessions: {
2037
+ [client.currentSessionID]: 3,
2038
+ },
2039
+ },
2040
+ },
2041
+ ]);
2042
+ });
2043
+ });
2044
+
2095
2045
  function groupContentEx(group: RawGroup) {
2096
2046
  return {
2097
2047
  action: "content",
@@ -2099,27 +2049,9 @@ function groupContentEx(group: RawGroup) {
2099
2049
  };
2100
2050
  }
2101
2051
 
2102
- function _admContEx(adminID: RawAccountID) {
2103
- return {
2104
- action: "content",
2105
- id: adminID,
2106
- };
2107
- }
2108
-
2109
2052
  function groupStateEx(group: RawGroup) {
2110
2053
  return {
2111
2054
  action: "known",
2112
2055
  id: group.core.id,
2113
2056
  };
2114
2057
  }
2115
-
2116
- function _admStateEx(adminID: RawAccountID) {
2117
- return {
2118
- action: "known",
2119
- id: adminID,
2120
- };
2121
- }
2122
-
2123
- function sleep(ms: number) {
2124
- return new Promise((resolve) => setTimeout(resolve, ms));
2125
- }