document-drive 3.2.0-dev.5 → 3.2.0-dev.6

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.
Files changed (71) hide show
  1. package/dist/src/cache/memory.js +2 -2
  2. package/dist/src/cache/memory.js.map +1 -1
  3. package/dist/src/cache/redis.js +3 -3
  4. package/dist/src/cache/redis.js.map +1 -1
  5. package/dist/src/drive-document-model/gen/utils.d.ts.map +1 -1
  6. package/dist/src/drive-document-model/gen/utils.js +8 -3
  7. package/dist/src/drive-document-model/gen/utils.js.map +1 -1
  8. package/dist/src/drive-document-model/src/tests/document-model.test.js +1 -1
  9. package/dist/src/drive-document-model/src/tests/document-model.test.js.map +1 -1
  10. package/dist/src/read-mode/service.js +1 -1
  11. package/dist/src/read-mode/service.js.map +1 -1
  12. package/dist/src/server/base-server.d.ts +1 -1
  13. package/dist/src/server/base-server.d.ts.map +1 -1
  14. package/dist/src/server/base-server.js +64 -35
  15. package/dist/src/server/base-server.js.map +1 -1
  16. package/dist/src/server/sync-manager.d.ts.map +1 -1
  17. package/dist/src/server/sync-manager.js +5 -5
  18. package/dist/src/server/sync-manager.js.map +1 -1
  19. package/dist/src/server/types.d.ts +2 -2
  20. package/dist/src/server/types.d.ts.map +1 -1
  21. package/dist/src/storage/browser.d.ts +3 -3
  22. package/dist/src/storage/browser.d.ts.map +1 -1
  23. package/dist/src/storage/browser.js +20 -18
  24. package/dist/src/storage/browser.js.map +1 -1
  25. package/dist/src/storage/filesystem.d.ts +3 -3
  26. package/dist/src/storage/filesystem.d.ts.map +1 -1
  27. package/dist/src/storage/filesystem.js +18 -18
  28. package/dist/src/storage/filesystem.js.map +1 -1
  29. package/dist/src/storage/ipfs.d.ts +4 -4
  30. package/dist/src/storage/ipfs.d.ts.map +1 -1
  31. package/dist/src/storage/ipfs.js +9 -9
  32. package/dist/src/storage/ipfs.js.map +1 -1
  33. package/dist/src/storage/memory.d.ts +3 -3
  34. package/dist/src/storage/memory.d.ts.map +1 -1
  35. package/dist/src/storage/memory.js +15 -15
  36. package/dist/src/storage/memory.js.map +1 -1
  37. package/dist/src/storage/prisma/prisma.d.ts +5 -6
  38. package/dist/src/storage/prisma/prisma.d.ts.map +1 -1
  39. package/dist/src/storage/prisma/prisma.js +37 -27
  40. package/dist/src/storage/prisma/prisma.js.map +1 -1
  41. package/dist/src/storage/types.d.ts +5 -5
  42. package/dist/src/storage/types.d.ts.map +1 -1
  43. package/dist/src/utils/default-drives-manager.js +3 -3
  44. package/dist/src/utils/default-drives-manager.js.map +1 -1
  45. package/dist/src/utils/gql-transformations.d.ts +13 -0
  46. package/dist/src/utils/gql-transformations.d.ts.map +1 -0
  47. package/dist/src/utils/gql-transformations.js +25 -0
  48. package/dist/src/utils/gql-transformations.js.map +1 -0
  49. package/dist/src/utils/graphql.d.ts.map +1 -1
  50. package/dist/src/utils/graphql.js +15 -19
  51. package/dist/src/utils/graphql.js.map +1 -1
  52. package/dist/src/utils/misc.js +1 -1
  53. package/dist/src/utils/misc.js.map +1 -1
  54. package/dist/test/cache.test.js +21 -21
  55. package/dist/test/cache.test.js.map +1 -1
  56. package/dist/test/graphql.test.js +1 -1
  57. package/dist/test/graphql.test.js.map +1 -1
  58. package/dist/test/queue.test.js +6 -6
  59. package/dist/test/queue.test.js.map +1 -1
  60. package/dist/test/read-mode.test.js +28 -16
  61. package/dist/test/read-mode.test.js.map +1 -1
  62. package/dist/test/server/driveOperationsConflictResolution.test.js +433 -362
  63. package/dist/test/server/driveOperationsConflictResolution.test.js.map +1 -1
  64. package/dist/test/server.test.js +11 -11
  65. package/dist/test/server.test.js.map +1 -1
  66. package/dist/test/signature-migration.test.js +8 -4
  67. package/dist/test/signature-migration.test.js.map +1 -1
  68. package/dist/test/storage.test.js +80 -69
  69. package/dist/test/storage.test.js.map +1 -1
  70. package/dist/tsconfig.tsbuildinfo +1 -1
  71. package/package.json +4 -4
@@ -1,5 +1,12 @@
1
- import { documentModelDocumentModelModule, generateId, } from "document-model";
1
+ import { BrowserStorage } from "#storage/browser";
2
+ import { FilesystemStorage } from "#storage/filesystem";
3
+ import { PrismaClient } from "#storage/prisma/client/index";
4
+ import { PrismaStorage } from "#storage/prisma/prisma";
5
+ import { documentModelDocumentModelModule, } from "document-model";
6
+ import { existsSync, rmSync } from "fs";
7
+ import path from "path";
2
8
  import { beforeEach, describe, expect, it } from "vitest";
9
+ import { InMemoryCache, MemoryStorage } from "../../index.js";
3
10
  import { addFolder, copyNode, } from "../../src/drive-document-model/gen/node/creators.js";
4
11
  import { reducer as documentDriveReducer } from "../../src/drive-document-model/gen/reducer.js";
5
12
  import { createDocument } from "../../src/drive-document-model/gen/utils.js";
@@ -10,380 +17,444 @@ import { DriveBasicClient } from "../utils.js";
10
17
  function sortNodes(nodes) {
11
18
  return nodes.sort((a, b) => (a.id < b.id ? -1 : 1));
12
19
  }
13
- describe("Drive Operations", () => {
14
- const documentModels = [
15
- documentModelDocumentModelModule,
16
- driveDocumentModelModule,
17
- ];
18
- let server;
19
- beforeEach(async () => {
20
- server = new ReactorBuilder(documentModels).build();
21
- await server.initialize();
22
- });
23
- const driveId = generateId();
24
- async function buildDrive() {
25
- await server.addDrive({
26
- id: driveId,
27
- global: { name: "test" },
28
- local: {
29
- availableOffline: false,
30
- sharingType: "PRIVATE",
31
- listeners: [],
32
- triggers: [],
33
- },
20
+ /**
21
+ * Currently we do not have good standalone tests for operations storage. The
22
+ * storage tests in storage.test.ts test the IDocumentStorage interface, which
23
+ * is not the same.
24
+ *
25
+ * We should test these in isolation.
26
+ */
27
+ const storageImplementations = [
28
+ ["Memory Storage", () => Promise.resolve(new MemoryStorage())],
29
+ [
30
+ "File System Storage",
31
+ () => {
32
+ const basePath = path.join(__dirname, "test-storage");
33
+ // delete the base path
34
+ if (existsSync(basePath)) {
35
+ rmSync(basePath, { recursive: true, force: true });
36
+ }
37
+ return Promise.resolve(new FilesystemStorage(basePath));
38
+ },
39
+ ],
40
+ [
41
+ "Browser Storage",
42
+ async () => {
43
+ const storage = new BrowserStorage();
44
+ await storage.clear();
45
+ return storage;
46
+ },
47
+ ],
48
+ [
49
+ "PrismaStorage",
50
+ async () => {
51
+ const prisma = new PrismaClient();
52
+ await prisma.$executeRawUnsafe('DELETE FROM "Attachment";');
53
+ await prisma.$executeRawUnsafe('DELETE FROM "Operation";');
54
+ await prisma.$executeRawUnsafe('DELETE FROM "Document";');
55
+ await prisma.$executeRawUnsafe('DELETE FROM "DriveDocument";');
56
+ await prisma.$executeRawUnsafe('DELETE FROM "Drive";');
57
+ return new PrismaStorage(prisma, new InMemoryCache());
58
+ },
59
+ ],
60
+ /*[
61
+ "IPFSStorage",
62
+ async () => {
63
+ const helia = await createHelia();
64
+ return new IPFSStorage(helia);
65
+ },
66
+ ],*/
67
+ ];
68
+ describe.each(storageImplementations)("%s", async (_, buildStorage) => {
69
+ describe("Drive Operations", () => {
70
+ const documentModels = [
71
+ documentModelDocumentModelModule,
72
+ driveDocumentModelModule,
73
+ ];
74
+ let server;
75
+ beforeEach(async () => {
76
+ const storage = await buildStorage();
77
+ server = new ReactorBuilder(documentModels)
78
+ .withStorage(storage)
79
+ .build();
80
+ await server.initialize();
34
81
  });
35
- return await server.getDrive(driveId);
36
- }
37
- it("should not re-apply existing operations", async () => {
38
- const initialDriveDocument = await buildDrive();
39
- let pushOperationResult;
40
- createDocument();
41
- const client1 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
42
- const client2 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
43
- client1.dispatchDriveAction(addFolder({ id: "1", name: "test1" }));
44
- pushOperationResult = await client1.pushOperationsToServer();
45
- expect(pushOperationResult.status).toBe("SUCCESS");
46
- client2.dispatchDriveAction(addFolder({ id: "2", name: "test2" }));
47
- pushOperationResult = await client2.pushOperationsToServer();
48
- expect(pushOperationResult.status).toBe("SUCCESS");
49
- await client1.syncDocument();
50
- expect(client1.getUnsyncedOperations()).toMatchObject([]);
51
- const syncedOperations = client1.getDocument().operations
52
- .global;
53
- client1.setUnsyncedOperations(syncedOperations);
54
- pushOperationResult = await client1.pushOperationsToServer();
55
- const drive = await server.getDrive(driveId);
56
- expect(drive.state.global.nodes.length).toBe(2);
57
- expect(drive.state.global.nodes).toMatchObject([
58
- { id: "1", name: "test1" },
59
- { id: "2", name: "test2" },
60
- ]);
61
- expect(drive.operations.global.length).toBe(2);
62
- expect(drive.operations.global).toMatchObject([
63
- {
64
- type: "ADD_FOLDER",
65
- input: { id: "1", name: "test1" },
66
- scope: "global",
67
- index: 1,
68
- skip: 1,
69
- },
70
- {
71
- type: "ADD_FOLDER",
72
- input: { id: "2", name: "test2" },
73
- scope: "global",
74
- index: 2,
75
- skip: 0,
76
- },
77
- ]);
78
- });
79
- it("should resolve conflicts when 5 clients are pushing changes to the same drive", async () => {
80
- const initialDriveDocument = await buildDrive();
81
- let pushOperationResult;
82
- createDocument();
83
- const client1 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
84
- const client2 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
85
- const client3 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
86
- const client4 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
87
- const client5 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
88
- // Client1 Add folder and push to server
89
- client1.dispatchDriveAction(addFolder({ id: "1", name: "test1" }));
90
- pushOperationResult = await client1.pushOperationsToServer();
91
- expect(pushOperationResult.status).toBe("SUCCESS");
92
- // Client2 Add folder and push to server
93
- client2.dispatchDriveAction(addFolder({ id: "2", name: "test2" }));
94
- pushOperationResult = await client2.pushOperationsToServer();
95
- expect(pushOperationResult.status).toBe("SUCCESS");
96
- // Client1 sync with server
97
- await client1.syncDocument();
98
- expect(client1.getUnsyncedOperations()).toMatchObject([]);
99
- // Clien1 push already synced operations to server (this should not create new operations in the server document)
100
- const syncedOperations = client1.getDocument().operations
101
- .global;
102
- client1.setUnsyncedOperations(syncedOperations);
103
- pushOperationResult = await client1.pushOperationsToServer();
104
- // Client3 sync with server
105
- await client3.syncDocument();
106
- // Client3 add folder and push to server
107
- client3.dispatchDriveAction(addFolder({ id: "3", name: "test3" }));
108
- pushOperationResult = await client3.pushOperationsToServer();
109
- expect(pushOperationResult.status).toBe("SUCCESS");
110
- // Client4 sync with server (partially syncs at this point)
111
- await client4.syncDocument();
112
- // Client3 add folder and push to server
113
- client3.dispatchDriveAction(addFolder({ id: "4", name: "test4" }));
114
- pushOperationResult = await client3.pushOperationsToServer();
115
- expect(pushOperationResult.status).toBe("SUCCESS");
116
- // Client4 add folder and push to server
117
- client4.dispatchDriveAction(addFolder({ id: "5", name: "test5" }));
118
- pushOperationResult = await client4.pushOperationsToServer();
119
- expect(pushOperationResult.status).toBe("SUCCESS");
120
- // Client5 add folder and push to server
121
- client5.dispatchDriveAction(addFolder({ id: "6", name: "test6" }));
122
- pushOperationResult = await client5.pushOperationsToServer();
123
- expect(pushOperationResult.status).toBe("SUCCESS");
124
- // Check if the operations are in the server
125
- const drive = await server.getDrive(driveId);
126
- expect(drive.state.global.nodes.length).toBe(6);
127
- expect(drive.state.global.nodes).toMatchObject([
128
- { id: "1", name: "test1" },
129
- { id: "2", name: "test2" },
130
- { id: "3", name: "test3" },
131
- { id: "4", name: "test4" },
132
- { id: "5", name: "test5" },
133
- { id: "6", name: "test6" },
134
- ]);
135
- expect(drive.operations.global.slice(-6)).toMatchObject([
136
- {
137
- type: "ADD_FOLDER",
138
- input: { id: "1", name: "test1" },
139
- scope: "global",
140
- index: 7,
141
- skip: 7,
142
- },
143
- {
144
- type: "ADD_FOLDER",
145
- input: { id: "2", name: "test2" },
146
- scope: "global",
147
- index: 8,
148
- skip: 0,
149
- },
150
- {
151
- type: "ADD_FOLDER",
152
- input: { id: "3", name: "test3" },
153
- scope: "global",
154
- index: 9,
155
- skip: 0,
156
- },
157
- {
158
- type: "ADD_FOLDER",
159
- input: { id: "4", name: "test4" },
160
- scope: "global",
161
- index: 10,
162
- skip: 0,
163
- },
164
- {
165
- type: "ADD_FOLDER",
166
- input: { id: "5", name: "test5" },
167
- scope: "global",
168
- index: 11,
169
- skip: 0,
170
- },
171
- {
172
- type: "ADD_FOLDER",
173
- input: { id: "6", name: "test6" },
174
- scope: "global",
175
- index: 12,
176
- skip: 0,
177
- },
178
- ]);
179
- });
180
- it("Should not throw an error when adding the same folder twice (same client)", async () => {
181
- const initialDriveDocument = await buildDrive();
182
- let pushOperationResult;
183
- createDocument();
184
- const client1 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
185
- const addFolderAction = addFolder({
186
- id: "1",
187
- name: "test1",
82
+ let driveId;
83
+ async function buildDrive() {
84
+ const drive = await server.addDrive({
85
+ global: { name: "test" },
86
+ local: {
87
+ availableOffline: false,
88
+ sharingType: "PRIVATE",
89
+ listeners: [],
90
+ triggers: [],
91
+ },
92
+ });
93
+ driveId = drive.header.id;
94
+ return await server.getDrive(driveId);
95
+ }
96
+ it("boilerplate is accurate", async () => {
97
+ const a = await server.addDrive({
98
+ global: { name: "test" },
99
+ local: {
100
+ availableOffline: false,
101
+ sharingType: "PRIVATE",
102
+ listeners: [],
103
+ triggers: [],
104
+ },
105
+ });
106
+ const b = await server.getDrive(a.header.id);
107
+ expect(a).toMatchObject(b);
188
108
  });
189
- client1.dispatchDriveAction(addFolderAction);
190
- pushOperationResult = await client1.pushOperationsToServer();
191
- expect(pushOperationResult.status).toBe("SUCCESS");
192
- client1.dispatchDriveAction(addFolderAction);
193
- pushOperationResult = await client1.pushOperationsToServer();
194
- expect(pushOperationResult.status).toBe("SUCCESS");
195
- expect(client1.getDocument().operations.global.length).toBe(2);
196
- expect(client1.getDocument().operations.global[1]).toMatchObject({
197
- type: "ADD_FOLDER",
198
- index: 1,
199
- skip: 0,
200
- input: { id: "1", name: "test1" },
201
- error: "Node with id 1 already exists!",
109
+ it("should not re-apply existing operations", async () => {
110
+ const initialDriveDocument = await buildDrive();
111
+ let pushOperationResult;
112
+ const client1 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
113
+ const client2 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
114
+ client1.dispatchDriveAction(addFolder({ id: "1", name: "test1" }));
115
+ pushOperationResult = await client1.pushOperationsToServer();
116
+ expect(pushOperationResult.status).toBe("SUCCESS");
117
+ client2.dispatchDriveAction(addFolder({ id: "2", name: "test2" }));
118
+ pushOperationResult = await client2.pushOperationsToServer();
119
+ expect(pushOperationResult.status).toBe("SUCCESS");
120
+ await client1.syncDocument();
121
+ expect(client1.getUnsyncedOperations()).toMatchObject([]);
122
+ const syncedOperations = client1.getDocument().operations
123
+ .global;
124
+ client1.setUnsyncedOperations(syncedOperations);
125
+ pushOperationResult = await client1.pushOperationsToServer();
126
+ const drive = await server.getDrive(driveId);
127
+ expect(drive.state.global.nodes.length).toBe(2);
128
+ expect(drive.state.global.nodes).toMatchObject([
129
+ { id: "1", name: "test1" },
130
+ { id: "2", name: "test2" },
131
+ ]);
132
+ expect(drive.operations.global.length).toBe(2);
133
+ expect(drive.operations.global).toMatchObject([
134
+ {
135
+ type: "ADD_FOLDER",
136
+ input: { id: "1", name: "test1" },
137
+ scope: "global",
138
+ index: 1,
139
+ skip: 1,
140
+ },
141
+ {
142
+ type: "ADD_FOLDER",
143
+ input: { id: "2", name: "test2" },
144
+ scope: "global",
145
+ index: 2,
146
+ skip: 0,
147
+ },
148
+ ]);
202
149
  });
203
- const drive = await server.getDrive(driveId);
204
- expect(drive.state.global.nodes.length).toBe(1);
205
- expect(drive.state.global.nodes).toMatchObject([
206
- { id: "1", name: "test1" },
207
- ]);
208
- expect(drive.operations.global.length).toBe(2);
209
- expect(drive.operations.global).toMatchObject([
210
- {
211
- type: "ADD_FOLDER",
212
- input: { id: "1", name: "test1" },
213
- error: undefined,
214
- index: 0,
215
- skip: 0,
216
- },
217
- {
150
+ it("should resolve conflicts when 5 clients are pushing changes to the same drive", async () => {
151
+ const initialDriveDocument = await buildDrive();
152
+ let pushOperationResult;
153
+ createDocument();
154
+ const client1 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
155
+ const client2 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
156
+ const client3 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
157
+ const client4 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
158
+ const client5 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
159
+ // Client1 Add folder and push to server
160
+ client1.dispatchDriveAction(addFolder({ id: "1", name: "test1" }));
161
+ pushOperationResult = await client1.pushOperationsToServer();
162
+ expect(pushOperationResult.status).toBe("SUCCESS");
163
+ // Client2 Add folder and push to server
164
+ client2.dispatchDriveAction(addFolder({ id: "2", name: "test2" }));
165
+ pushOperationResult = await client2.pushOperationsToServer();
166
+ expect(pushOperationResult.status).toBe("SUCCESS");
167
+ // Client1 sync with server
168
+ await client1.syncDocument();
169
+ expect(client1.getUnsyncedOperations()).toMatchObject([]);
170
+ // Clien1 push already synced operations to server (this should not create new operations in the server document)
171
+ const syncedOperations = client1.getDocument().operations
172
+ .global;
173
+ client1.setUnsyncedOperations(syncedOperations);
174
+ pushOperationResult = await client1.pushOperationsToServer();
175
+ // Client3 sync with server
176
+ await client3.syncDocument();
177
+ // Client3 add folder and push to server
178
+ client3.dispatchDriveAction(addFolder({ id: "3", name: "test3" }));
179
+ pushOperationResult = await client3.pushOperationsToServer();
180
+ expect(pushOperationResult.status).toBe("SUCCESS");
181
+ // Client4 sync with server (partially syncs at this point)
182
+ await client4.syncDocument();
183
+ // Client3 add folder and push to server
184
+ client3.dispatchDriveAction(addFolder({ id: "4", name: "test4" }));
185
+ pushOperationResult = await client3.pushOperationsToServer();
186
+ expect(pushOperationResult.status).toBe("SUCCESS");
187
+ // Client4 add folder and push to server
188
+ client4.dispatchDriveAction(addFolder({ id: "5", name: "test5" }));
189
+ pushOperationResult = await client4.pushOperationsToServer();
190
+ expect(pushOperationResult.status).toBe("SUCCESS");
191
+ // Client5 add folder and push to server
192
+ client5.dispatchDriveAction(addFolder({ id: "6", name: "test6" }));
193
+ pushOperationResult = await client5.pushOperationsToServer();
194
+ expect(pushOperationResult.status).toBe("SUCCESS");
195
+ // Check if the operations are in the server
196
+ const drive = await server.getDrive(driveId);
197
+ expect(drive.state.global.nodes.length).toBe(6);
198
+ expect(drive.state.global.nodes).toMatchObject([
199
+ { id: "1", name: "test1" },
200
+ { id: "2", name: "test2" },
201
+ { id: "3", name: "test3" },
202
+ { id: "4", name: "test4" },
203
+ { id: "5", name: "test5" },
204
+ { id: "6", name: "test6" },
205
+ ]);
206
+ expect(drive.operations.global.slice(-6)).toMatchObject([
207
+ {
208
+ type: "ADD_FOLDER",
209
+ input: { id: "1", name: "test1" },
210
+ scope: "global",
211
+ index: 7,
212
+ skip: 7,
213
+ },
214
+ {
215
+ type: "ADD_FOLDER",
216
+ input: { id: "2", name: "test2" },
217
+ scope: "global",
218
+ index: 8,
219
+ skip: 0,
220
+ },
221
+ {
222
+ type: "ADD_FOLDER",
223
+ input: { id: "3", name: "test3" },
224
+ scope: "global",
225
+ index: 9,
226
+ skip: 0,
227
+ },
228
+ {
229
+ type: "ADD_FOLDER",
230
+ input: { id: "4", name: "test4" },
231
+ scope: "global",
232
+ index: 10,
233
+ skip: 0,
234
+ },
235
+ {
236
+ type: "ADD_FOLDER",
237
+ input: { id: "5", name: "test5" },
238
+ scope: "global",
239
+ index: 11,
240
+ skip: 0,
241
+ },
242
+ {
243
+ type: "ADD_FOLDER",
244
+ input: { id: "6", name: "test6" },
245
+ scope: "global",
246
+ index: 12,
247
+ skip: 0,
248
+ },
249
+ ]);
250
+ });
251
+ it("Should not throw an error when adding the same folder twice (same client)", async () => {
252
+ const initialDriveDocument = await buildDrive();
253
+ let pushOperationResult;
254
+ createDocument();
255
+ const client1 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
256
+ const addFolderAction = addFolder({
257
+ id: "1",
258
+ name: "test1",
259
+ });
260
+ client1.dispatchDriveAction(addFolderAction);
261
+ pushOperationResult = await client1.pushOperationsToServer();
262
+ expect(pushOperationResult.status).toBe("SUCCESS");
263
+ client1.dispatchDriveAction(addFolderAction);
264
+ pushOperationResult = await client1.pushOperationsToServer();
265
+ expect(pushOperationResult.status).toBe("SUCCESS");
266
+ expect(client1.getDocument().operations.global.length).toBe(2);
267
+ expect(client1.getDocument().operations.global[1]).toMatchObject({
218
268
  type: "ADD_FOLDER",
219
- input: { id: "1", name: "test1" },
220
- scope: "global",
221
269
  index: 1,
222
270
  skip: 0,
271
+ input: { id: "1", name: "test1" },
223
272
  error: "Node with id 1 already exists!",
224
- },
225
- ]);
226
- });
227
- it("Should not throw an error when adding the same folder twice (2 clients)", { retry: 10 }, async () => {
228
- const initialDriveDocument = await buildDrive();
229
- let pushOperationResult;
230
- createDocument();
231
- const client1 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
232
- const client2 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
233
- client1.dispatchDriveAction(addFolder({
234
- id: "1",
235
- name: "test1",
236
- }));
237
- pushOperationResult = await client1.pushOperationsToServer();
238
- expect(pushOperationResult.status).toBe("SUCCESS");
239
- expect(client1.getDocument().operations.global.length).toBe(1);
240
- expect(client1.getDocument().operations.global[0]).toMatchObject({
241
- type: "ADD_FOLDER",
242
- index: 0,
243
- skip: 0,
244
- input: { id: "1", name: "test1" },
245
- error: undefined,
246
- });
247
- client2.dispatchDriveAction(addFolder({
248
- id: "1",
249
- name: "test2",
250
- }));
251
- pushOperationResult = await client2.pushOperationsToServer();
252
- expect(pushOperationResult.status).toBe("SUCCESS");
253
- expect(client2.getDocument().operations.global.length).toBe(1);
254
- expect(client2.getDocument().operations.global[0]).toMatchObject({
255
- type: "ADD_FOLDER",
256
- index: 0,
257
- skip: 0,
258
- input: { id: "1", name: "test2" },
259
- error: undefined,
273
+ });
274
+ const drive = await server.getDrive(driveId);
275
+ expect(drive.state.global.nodes.length).toBe(1);
276
+ expect(drive.state.global.nodes).toMatchObject([
277
+ { id: "1", name: "test1" },
278
+ ]);
279
+ expect(drive.operations.global.length).toBe(2);
280
+ expect(drive.operations.global).toMatchObject([
281
+ {
282
+ type: "ADD_FOLDER",
283
+ input: { id: "1", name: "test1" },
284
+ index: 0,
285
+ skip: 0,
286
+ },
287
+ {
288
+ type: "ADD_FOLDER",
289
+ input: { id: "1", name: "test1" },
290
+ scope: "global",
291
+ index: 1,
292
+ skip: 0,
293
+ error: "Node with id 1 already exists!",
294
+ },
295
+ ]);
260
296
  });
261
- const drive = await server.getDrive(driveId);
262
- expect(drive.state.global.nodes.length).toBe(1);
263
- expect(drive.state.global.nodes).toMatchObject([
264
- { id: "1", name: "test1" },
265
- ]);
266
- expect(drive.operations.global.length).toBe(2);
267
- expect(drive.operations.global).toMatchObject([
268
- {
297
+ it("Should not throw an error when adding the same folder twice (2 clients)", { retry: 10 }, async () => {
298
+ const initialDriveDocument = await buildDrive();
299
+ let pushOperationResult;
300
+ createDocument();
301
+ const client1 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
302
+ const client2 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
303
+ client1.dispatchDriveAction(addFolder({
304
+ id: "1",
305
+ name: "test1",
306
+ }));
307
+ pushOperationResult = await client1.pushOperationsToServer();
308
+ expect(pushOperationResult.status).toBe("SUCCESS");
309
+ expect(client1.getDocument().operations.global.length).toBe(1);
310
+ expect(client1.getDocument().operations.global[0]).toMatchObject({
269
311
  type: "ADD_FOLDER",
312
+ index: 0,
313
+ skip: 0,
270
314
  input: { id: "1", name: "test1" },
271
315
  error: undefined,
272
- index: 1,
273
- skip: 1,
274
- },
275
- {
316
+ });
317
+ client2.dispatchDriveAction(addFolder({
318
+ id: "1",
319
+ name: "test2",
320
+ }));
321
+ pushOperationResult = await client2.pushOperationsToServer();
322
+ expect(pushOperationResult.status).toBe("SUCCESS");
323
+ expect(client2.getDocument().operations.global.length).toBe(1);
324
+ expect(client2.getDocument().operations.global[0]).toMatchObject({
276
325
  type: "ADD_FOLDER",
277
- input: { id: "1", name: "test2" },
278
- scope: "global",
279
- index: 2,
326
+ index: 0,
280
327
  skip: 0,
281
- error: "Node with id 1 already exists!",
282
- },
283
- ]);
284
- });
285
- // TODO: This test is flaky.
286
- it("should resolve conflicts without duplicate ids when copy folders", async () => {
287
- return;
288
- let idCounter = 0;
289
- const generateId = () => {
290
- idCounter++;
291
- return `${idCounter}`;
292
- };
293
- const initialDriveDocument = await buildDrive();
294
- let pushOperationResult;
295
- createDocument();
296
- const client1 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
297
- const client2 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
298
- const idFolder1 = generateId();
299
- const idFolder2 = generateId();
300
- expect(idFolder1).toBe("1");
301
- expect(idFolder2).toBe("2");
302
- // Add folders in client 1 and push to server
303
- client1.dispatchDriveAction(addFolder({ id: idFolder1, name: "1" }));
304
- pushOperationResult = await client1.pushOperationsToServer();
305
- expect(pushOperationResult.status).toBe("SUCCESS");
306
- client1.dispatchDriveAction(addFolder({
307
- id: idFolder2,
308
- name: "2",
309
- parentFolder: idFolder1,
310
- }));
311
- pushOperationResult = await client1.pushOperationsToServer();
312
- expect(pushOperationResult.status).toBe("SUCCESS");
313
- // Sync client 2 with server
314
- await client2.syncDocument();
315
- // Copy folder 1 to root in client 1 and push to server
316
- const copyNodesInput = generateNodesCopy({
317
- srcId: idFolder1,
318
- targetName: "1",
319
- targetParentFolder: undefined,
320
- }, generateId, client1.getDocument().state.global.nodes);
321
- const copyActions = copyNodesInput.map((copyNodeInput) => copyNode(copyNodeInput));
322
- for (const copyAction of copyActions) {
323
- client1.dispatchDriveAction(copyAction);
324
- }
325
- pushOperationResult = await client1.pushOperationsToServer();
326
- expect(pushOperationResult.status).toBe("SUCCESS");
327
- let drive = await server.getDrive(driveId);
328
- expect(drive.state.global.nodes.length).toBe(4);
329
- expect(drive.state.global.nodes).toMatchObject([
330
- { id: "1", name: "1", parentFolder: null },
331
- { id: "2", name: "2", parentFolder: "1" },
332
- { id: "3", name: "1 (copy) 1", parentFolder: null },
333
- { id: "4", name: "2", parentFolder: "3" },
334
- ]);
335
- /* CLIENT 2 */
336
- // generate copy nodes input for client 2
337
- const copyNodesInput2 = generateNodesCopy({
338
- srcId: idFolder1,
339
- targetName: "1",
340
- targetParentFolder: undefined,
341
- }, generateId, client2.getDocument().state.global.nodes);
342
- const copyNodesInput3 = generateNodesCopy({
343
- srcId: idFolder1,
344
- targetName: "1",
345
- targetParentFolder: undefined,
346
- }, generateId, client2.getDocument().state.global.nodes);
347
- const copyActions2 = copyNodesInput2.map((copyNodeInput) => copyNode(copyNodeInput));
348
- const copyActions3 = copyNodesInput3.map((copyNodeInput) => copyNode(copyNodeInput));
349
- // apply copy actions (1) to client 2
350
- for (const copyAction of copyActions2) {
351
- client2.dispatchDriveAction(copyAction);
352
- }
353
- // push operations to server
354
- pushOperationResult = await client2.pushOperationsToServer();
355
- expect(pushOperationResult.status).toBe("SUCCESS");
356
- drive = await server.getDrive(driveId);
357
- expect(drive.state.global.nodes.length).toBe(6);
358
- expect(drive.state.global.nodes).toMatchObject([
359
- { id: "1", name: "1", parentFolder: null },
360
- { id: "2", name: "2", parentFolder: "1" },
361
- { id: "3", name: "1 (copy) 1", parentFolder: null },
362
- { id: "4", name: "2", parentFolder: "3" },
363
- { id: "5", name: "1 (copy) 2", parentFolder: null },
364
- { id: "6", name: "2", parentFolder: "5" },
365
- ]);
366
- // apply copy actions (2) to client 2
367
- for (const copyAction of copyActions3) {
368
- client2.dispatchDriveAction(copyAction);
369
- }
370
- // sync client 2 with server
371
- await client2.syncDocument();
372
- const client2Nodes = client2.getDocument().state
373
- .global.nodes;
374
- // TODO: validate that there are not duplicated operations after operation id implementation
375
- expect(client2Nodes).toHaveLength(8);
376
- const sortedClient2Nodes = sortNodes(client2Nodes);
377
- expect(sortedClient2Nodes).toMatchObject([
378
- { id: "1", name: "1", parentFolder: null },
379
- { id: "2", name: "2", parentFolder: "1" },
380
- { id: "3", name: "1 (copy) 1", parentFolder: null },
381
- { id: "4", name: "2", parentFolder: "3" },
382
- { id: "5", name: "1 (copy) 2", parentFolder: null },
383
- { id: "6", name: "2", parentFolder: "5" },
384
- { id: "7", name: "1 (copy) 3", parentFolder: null },
385
- { id: "8", name: "2", parentFolder: "7" },
386
- ]);
328
+ input: { id: "1", name: "test2" },
329
+ error: undefined,
330
+ });
331
+ const drive = await server.getDrive(driveId);
332
+ expect(drive.state.global.nodes.length).toBe(1);
333
+ expect(drive.state.global.nodes).toMatchObject([
334
+ { id: "1", name: "test1" },
335
+ ]);
336
+ expect(drive.operations.global.length).toBe(2);
337
+ expect(drive.operations.global).toMatchObject([
338
+ {
339
+ type: "ADD_FOLDER",
340
+ input: { id: "1", name: "test1" },
341
+ error: undefined,
342
+ index: 1,
343
+ skip: 1,
344
+ },
345
+ {
346
+ type: "ADD_FOLDER",
347
+ input: { id: "1", name: "test2" },
348
+ scope: "global",
349
+ index: 2,
350
+ skip: 0,
351
+ error: "Node with id 1 already exists!",
352
+ },
353
+ ]);
354
+ });
355
+ // TODO: This test is flaky.
356
+ it("should resolve conflicts without duplicate ids when copy folders", async () => {
357
+ return;
358
+ let idCounter = 0;
359
+ const generateId = () => {
360
+ idCounter++;
361
+ return `${idCounter}`;
362
+ };
363
+ const initialDriveDocument = await buildDrive();
364
+ let pushOperationResult;
365
+ createDocument();
366
+ const client1 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
367
+ const client2 = new DriveBasicClient(server, driveId, initialDriveDocument, documentDriveReducer);
368
+ const idFolder1 = generateId();
369
+ const idFolder2 = generateId();
370
+ expect(idFolder1).toBe("1");
371
+ expect(idFolder2).toBe("2");
372
+ // Add folders in client 1 and push to server
373
+ client1.dispatchDriveAction(addFolder({ id: idFolder1, name: "1" }));
374
+ pushOperationResult = await client1.pushOperationsToServer();
375
+ expect(pushOperationResult.status).toBe("SUCCESS");
376
+ client1.dispatchDriveAction(addFolder({
377
+ id: idFolder2,
378
+ name: "2",
379
+ parentFolder: idFolder1,
380
+ }));
381
+ pushOperationResult = await client1.pushOperationsToServer();
382
+ expect(pushOperationResult.status).toBe("SUCCESS");
383
+ // Sync client 2 with server
384
+ await client2.syncDocument();
385
+ // Copy folder 1 to root in client 1 and push to server
386
+ const copyNodesInput = generateNodesCopy({
387
+ srcId: idFolder1,
388
+ targetName: "1",
389
+ targetParentFolder: undefined,
390
+ }, generateId, client1.getDocument().state.global.nodes);
391
+ const copyActions = copyNodesInput.map((copyNodeInput) => copyNode(copyNodeInput));
392
+ for (const copyAction of copyActions) {
393
+ client1.dispatchDriveAction(copyAction);
394
+ }
395
+ pushOperationResult = await client1.pushOperationsToServer();
396
+ expect(pushOperationResult.status).toBe("SUCCESS");
397
+ let drive = await server.getDrive(driveId);
398
+ expect(drive.state.global.nodes.length).toBe(4);
399
+ expect(drive.state.global.nodes).toMatchObject([
400
+ { id: "1", name: "1", parentFolder: null },
401
+ { id: "2", name: "2", parentFolder: "1" },
402
+ { id: "3", name: "1 (copy) 1", parentFolder: null },
403
+ { id: "4", name: "2", parentFolder: "3" },
404
+ ]);
405
+ /* CLIENT 2 */
406
+ // generate copy nodes input for client 2
407
+ const copyNodesInput2 = generateNodesCopy({
408
+ srcId: idFolder1,
409
+ targetName: "1",
410
+ targetParentFolder: undefined,
411
+ }, generateId, client2.getDocument().state.global.nodes);
412
+ const copyNodesInput3 = generateNodesCopy({
413
+ srcId: idFolder1,
414
+ targetName: "1",
415
+ targetParentFolder: undefined,
416
+ }, generateId, client2.getDocument().state.global.nodes);
417
+ const copyActions2 = copyNodesInput2.map((copyNodeInput) => copyNode(copyNodeInput));
418
+ const copyActions3 = copyNodesInput3.map((copyNodeInput) => copyNode(copyNodeInput));
419
+ // apply copy actions (1) to client 2
420
+ for (const copyAction of copyActions2) {
421
+ client2.dispatchDriveAction(copyAction);
422
+ }
423
+ // push operations to server
424
+ pushOperationResult = await client2.pushOperationsToServer();
425
+ expect(pushOperationResult.status).toBe("SUCCESS");
426
+ drive = await server.getDrive(driveId);
427
+ expect(drive.state.global.nodes.length).toBe(6);
428
+ expect(drive.state.global.nodes).toMatchObject([
429
+ { id: "1", name: "1", parentFolder: null },
430
+ { id: "2", name: "2", parentFolder: "1" },
431
+ { id: "3", name: "1 (copy) 1", parentFolder: null },
432
+ { id: "4", name: "2", parentFolder: "3" },
433
+ { id: "5", name: "1 (copy) 2", parentFolder: null },
434
+ { id: "6", name: "2", parentFolder: "5" },
435
+ ]);
436
+ // apply copy actions (2) to client 2
437
+ for (const copyAction of copyActions3) {
438
+ client2.dispatchDriveAction(copyAction);
439
+ }
440
+ // sync client 2 with server
441
+ await client2.syncDocument();
442
+ const client2Nodes = client2.getDocument()
443
+ .state.global.nodes;
444
+ // TODO: validate that there are not duplicated operations after operation id implementation
445
+ expect(client2Nodes).toHaveLength(8);
446
+ const sortedClient2Nodes = sortNodes(client2Nodes);
447
+ expect(sortedClient2Nodes).toMatchObject([
448
+ { id: "1", name: "1", parentFolder: null },
449
+ { id: "2", name: "2", parentFolder: "1" },
450
+ { id: "3", name: "1 (copy) 1", parentFolder: null },
451
+ { id: "4", name: "2", parentFolder: "3" },
452
+ { id: "5", name: "1 (copy) 2", parentFolder: null },
453
+ { id: "6", name: "2", parentFolder: "5" },
454
+ { id: "7", name: "1 (copy) 3", parentFolder: null },
455
+ { id: "8", name: "2", parentFolder: "7" },
456
+ ]);
457
+ });
387
458
  });
388
459
  });
389
460
  //# sourceMappingURL=driveOperationsConflictResolution.test.js.map