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.
- package/dist/src/cache/memory.js +2 -2
- package/dist/src/cache/memory.js.map +1 -1
- package/dist/src/cache/redis.js +3 -3
- package/dist/src/cache/redis.js.map +1 -1
- package/dist/src/drive-document-model/gen/utils.d.ts.map +1 -1
- package/dist/src/drive-document-model/gen/utils.js +8 -3
- package/dist/src/drive-document-model/gen/utils.js.map +1 -1
- package/dist/src/drive-document-model/src/tests/document-model.test.js +1 -1
- package/dist/src/drive-document-model/src/tests/document-model.test.js.map +1 -1
- package/dist/src/read-mode/service.js +1 -1
- package/dist/src/read-mode/service.js.map +1 -1
- package/dist/src/server/base-server.d.ts +1 -1
- package/dist/src/server/base-server.d.ts.map +1 -1
- package/dist/src/server/base-server.js +64 -35
- package/dist/src/server/base-server.js.map +1 -1
- package/dist/src/server/sync-manager.d.ts.map +1 -1
- package/dist/src/server/sync-manager.js +5 -5
- package/dist/src/server/sync-manager.js.map +1 -1
- package/dist/src/server/types.d.ts +2 -2
- package/dist/src/server/types.d.ts.map +1 -1
- package/dist/src/storage/browser.d.ts +3 -3
- package/dist/src/storage/browser.d.ts.map +1 -1
- package/dist/src/storage/browser.js +20 -18
- package/dist/src/storage/browser.js.map +1 -1
- package/dist/src/storage/filesystem.d.ts +3 -3
- package/dist/src/storage/filesystem.d.ts.map +1 -1
- package/dist/src/storage/filesystem.js +18 -18
- package/dist/src/storage/filesystem.js.map +1 -1
- package/dist/src/storage/ipfs.d.ts +4 -4
- package/dist/src/storage/ipfs.d.ts.map +1 -1
- package/dist/src/storage/ipfs.js +9 -9
- package/dist/src/storage/ipfs.js.map +1 -1
- package/dist/src/storage/memory.d.ts +3 -3
- package/dist/src/storage/memory.d.ts.map +1 -1
- package/dist/src/storage/memory.js +15 -15
- package/dist/src/storage/memory.js.map +1 -1
- package/dist/src/storage/prisma/prisma.d.ts +5 -6
- package/dist/src/storage/prisma/prisma.d.ts.map +1 -1
- package/dist/src/storage/prisma/prisma.js +37 -27
- package/dist/src/storage/prisma/prisma.js.map +1 -1
- package/dist/src/storage/types.d.ts +5 -5
- package/dist/src/storage/types.d.ts.map +1 -1
- package/dist/src/utils/default-drives-manager.js +3 -3
- package/dist/src/utils/default-drives-manager.js.map +1 -1
- package/dist/src/utils/gql-transformations.d.ts +13 -0
- package/dist/src/utils/gql-transformations.d.ts.map +1 -0
- package/dist/src/utils/gql-transformations.js +25 -0
- package/dist/src/utils/gql-transformations.js.map +1 -0
- package/dist/src/utils/graphql.d.ts.map +1 -1
- package/dist/src/utils/graphql.js +15 -19
- package/dist/src/utils/graphql.js.map +1 -1
- package/dist/src/utils/misc.js +1 -1
- package/dist/src/utils/misc.js.map +1 -1
- package/dist/test/cache.test.js +21 -21
- package/dist/test/cache.test.js.map +1 -1
- package/dist/test/graphql.test.js +1 -1
- package/dist/test/graphql.test.js.map +1 -1
- package/dist/test/queue.test.js +6 -6
- package/dist/test/queue.test.js.map +1 -1
- package/dist/test/read-mode.test.js +28 -16
- package/dist/test/read-mode.test.js.map +1 -1
- package/dist/test/server/driveOperationsConflictResolution.test.js +433 -362
- package/dist/test/server/driveOperationsConflictResolution.test.js.map +1 -1
- package/dist/test/server.test.js +11 -11
- package/dist/test/server.test.js.map +1 -1
- package/dist/test/signature-migration.test.js +8 -4
- package/dist/test/signature-migration.test.js.map +1 -1
- package/dist/test/storage.test.js +80 -69
- package/dist/test/storage.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
-
|
|
273
|
-
|
|
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
|
-
|
|
278
|
-
scope: "global",
|
|
279
|
-
index: 2,
|
|
326
|
+
index: 0,
|
|
280
327
|
skip: 0,
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
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
|