core-3nweb-client-lib 0.44.11 → 0.45.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.
- package/build/core/asmail/msg/opener.js +1 -1
- package/build/core/asmail/msg/packer.js +1 -1
- package/build/core/index.d.ts +2 -2
- package/build/core/keyring/correspondent-keys.js +1 -1
- package/build/core/keyring/index.js +1 -1
- package/build/core/startup/sign-in.d.ts +1 -1
- package/build/core/startup/sign-up.d.ts +1 -1
- package/build/core/storage/index.d.ts +1 -1
- package/build/lib-client/key-derivation.d.ts +1 -1
- package/build/lib-client/xsp-fs/attrs.d.ts +3 -2
- package/build/lib-client/xsp-fs/attrs.js +7 -6
- package/build/lib-client/xsp-fs/folder-node.js +1 -1
- package/build/lib-client/xsp-fs/node-in-fs.js +1 -0
- package/build/lib-client/xsp-fs/node-persistence.js +1 -1
- package/build/lib-client/xsp-fs/xsp-payload-v2.js +1 -1
- package/build/tests/caps-api/asmail/specs/events.d.ts +2 -0
- package/build/tests/caps-api/asmail/specs/events.js +63 -0
- package/build/tests/caps-api/asmail/specs/send-to-wrong-address.d.ts +2 -0
- package/build/tests/caps-api/asmail/specs/send-to-wrong-address.js +68 -0
- package/build/tests/caps-api/asmail/specs/send-with-attacment-from-storage.d.ts +2 -0
- package/build/tests/caps-api/asmail/specs/send-with-attacment-from-storage.js +259 -0
- package/build/tests/caps-api/asmail/specs/send-without-attachments.d.ts +2 -0
- package/build/tests/caps-api/asmail/specs/send-without-attachments.js +90 -0
- package/build/tests/caps-api/asmail/test-utils.d.ts +12 -0
- package/build/tests/caps-api/asmail/test-utils.js +60 -0
- package/build/tests/caps-api/asmail.js +86 -0
- package/build/tests/caps-api/file-sink-checks/different-cases.d.ts +2 -0
- package/build/tests/caps-api/file-sink-checks/different-cases.js +47 -0
- package/build/tests/caps-api/file-sink-checks/splice.d.ts +2 -0
- package/build/tests/caps-api/file-sink-checks/splice.js +122 -0
- package/build/tests/caps-api/file-sink-checks/truncate.d.ts +2 -0
- package/build/tests/caps-api/file-sink-checks/truncate.js +122 -0
- package/build/tests/caps-api/fs-checks/local-to-synced-linking/link.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/local-to-synced-linking/link.js +82 -0
- package/build/tests/caps-api/fs-checks/local-to-synced-linking/reversed-linking.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/local-to-synced-linking/reversed-linking.js +50 -0
- package/build/tests/caps-api/fs-checks/not-versioned/deleteFile.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/deleteFile.js +73 -0
- package/build/tests/caps-api/fs-checks/not-versioned/deleteFolder.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/deleteFolder.js +75 -0
- package/build/tests/caps-api/fs-checks/not-versioned/getByteSink.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/getByteSink.js +96 -0
- package/build/tests/caps-api/fs-checks/not-versioned/getByteSource.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/getByteSource.js +67 -0
- package/build/tests/caps-api/fs-checks/not-versioned/getXAttr.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/getXAttr.js +77 -0
- package/build/tests/caps-api/fs-checks/not-versioned/link.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/link.js +115 -0
- package/build/tests/caps-api/fs-checks/not-versioned/listFolder.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/listFolder.js +129 -0
- package/build/tests/caps-api/fs-checks/not-versioned/listXAttrs.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/listXAttrs.js +73 -0
- package/build/tests/caps-api/fs-checks/not-versioned/makeFolder.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/makeFolder.js +95 -0
- package/build/tests/caps-api/fs-checks/not-versioned/move.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/move.js +127 -0
- package/build/tests/caps-api/fs-checks/not-versioned/readBytes.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/readBytes.js +80 -0
- package/build/tests/caps-api/fs-checks/not-versioned/readJSONFile.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/readJSONFile.js +59 -0
- package/build/tests/caps-api/fs-checks/not-versioned/readLink.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/readLink.js +39 -0
- package/build/tests/caps-api/fs-checks/not-versioned/readTxtFile.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/readTxtFile.js +54 -0
- package/build/tests/caps-api/fs-checks/not-versioned/readonlyFile.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/readonlyFile.js +55 -0
- package/build/tests/caps-api/fs-checks/not-versioned/readonlySubRoot.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/readonlySubRoot.js +55 -0
- package/build/tests/caps-api/fs-checks/not-versioned/select.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/select.js +119 -0
- package/build/tests/caps-api/fs-checks/not-versioned/stat.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/stat.js +96 -0
- package/build/tests/caps-api/fs-checks/not-versioned/updateXAttrs.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/updateXAttrs.js +126 -0
- package/build/tests/caps-api/fs-checks/not-versioned/writableFile.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/writableFile.js +79 -0
- package/build/tests/caps-api/fs-checks/not-versioned/writableSubRoot.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/writableSubRoot.js +90 -0
- package/build/tests/caps-api/fs-checks/not-versioned/writeBytes.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/writeBytes.js +137 -0
- package/build/tests/caps-api/fs-checks/not-versioned/writeJSONFile.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/writeJSONFile.js +101 -0
- package/build/tests/caps-api/fs-checks/not-versioned/writeTxtFile.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/not-versioned/writeTxtFile.js +100 -0
- package/build/tests/caps-api/fs-checks/sync-on-one-dev/startUpload.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/sync-on-one-dev/startUpload.js +187 -0
- package/build/tests/caps-api/fs-checks/sync-on-one-dev/stat.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/sync-on-one-dev/stat.js +70 -0
- package/build/tests/caps-api/fs-checks/sync-on-one-dev/status.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/sync-on-one-dev/status.js +53 -0
- package/build/tests/caps-api/fs-checks/sync-on-one-dev/upload.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/sync-on-one-dev/upload.js +194 -0
- package/build/tests/caps-api/fs-checks/sync-with-two-devs/conflicts.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/sync-with-two-devs/conflicts.js +396 -0
- package/build/tests/caps-api/fs-checks/sync-with-two-devs/update-propagation.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/sync-with-two-devs/update-propagation.js +229 -0
- package/build/tests/caps-api/fs-checks/test-utils.d.ts +34 -0
- package/build/tests/caps-api/fs-checks/test-utils.js +95 -0
- package/build/tests/caps-api/fs-checks/versioned/archiveCurrent.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/versioned/archiveCurrent.js +73 -0
- package/build/tests/caps-api/fs-checks/versioned/getByteSink.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/versioned/getByteSink.js +122 -0
- package/build/tests/caps-api/fs-checks/versioned/getByteSource.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/versioned/getByteSource.js +71 -0
- package/build/tests/caps-api/fs-checks/versioned/listFolder.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/versioned/listFolder.js +109 -0
- package/build/tests/caps-api/fs-checks/versioned/listVersions.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/versioned/listVersions.js +48 -0
- package/build/tests/caps-api/fs-checks/versioned/readBytes.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/versioned/readBytes.js +90 -0
- package/build/tests/caps-api/fs-checks/versioned/readJSONFile.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/versioned/readJSONFile.js +60 -0
- package/build/tests/caps-api/fs-checks/versioned/readTxtFile.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/versioned/readTxtFile.js +56 -0
- package/build/tests/caps-api/fs-checks/versioned/stat.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/versioned/stat.js +80 -0
- package/build/tests/caps-api/fs-checks/versioned/watchFile.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/versioned/watchFile.js +64 -0
- package/build/tests/caps-api/fs-checks/versioned/writeBytes.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/versioned/writeBytes.js +128 -0
- package/build/tests/caps-api/fs-checks/versioned/writeJSONFile.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/versioned/writeJSONFile.js +111 -0
- package/build/tests/caps-api/fs-checks/versioned/writeTxtFile.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/versioned/writeTxtFile.js +107 -0
- package/build/tests/caps-api/fs-checks/with-core-restarts/xattrs.d.ts +2 -0
- package/build/tests/caps-api/fs-checks/with-core-restarts/xattrs.js +50 -0
- package/build/tests/caps-api/keyrings.js +69 -0
- package/build/tests/caps-api/mailerid.d.ts +1 -0
- package/build/tests/caps-api/mailerid.js +59 -0
- package/build/tests/caps-api/startup/signin-empty-cache.d.ts +1 -0
- package/build/tests/caps-api/startup/signin-empty-cache.js +108 -0
- package/build/tests/caps-api/startup/signin-with-cache.d.ts +1 -0
- package/build/tests/caps-api/startup/signin-with-cache.js +83 -0
- package/build/tests/caps-api/startup/signup-with-token.d.ts +1 -0
- package/build/tests/caps-api/startup/signup-with-token.js +53 -0
- package/build/tests/caps-api/startup/signup.d.ts +1 -0
- package/build/tests/caps-api/startup/signup.js +86 -0
- package/build/tests/caps-api/storage.d.ts +1 -0
- package/build/tests/caps-api/storage.js +225 -0
- package/build/tests/computer.3nweb.core/id-manager.d.ts +1 -0
- package/build/tests/computer.3nweb.core/id-manager.js +78 -0
- package/build/tests/computer.3nweb.core/inbox/msg-indexing.d.ts +1 -0
- package/build/tests/computer.3nweb.core/inbox/msg-indexing.js +145 -0
- package/build/tests/computer.3nweb.core/keyrings.d.ts +1 -0
- package/build/tests/computer.3nweb.core/keyrings.js +64 -0
- package/build/tests/computer.3nweb.core/test-utils.d.ts +21 -0
- package/build/tests/computer.3nweb.core/test-utils.js +92 -0
- package/build/tests/jasmine.d.ts +1 -0
- package/build/tests/jasmine.js +45 -0
- package/build/tests/libs-for-tests/bytes-equal.d.ts +1 -0
- package/build/tests/libs-for-tests/bytes-equal.js +32 -0
- package/build/tests/libs-for-tests/caps-ipc-wrap.d.ts +11 -0
- package/build/tests/libs-for-tests/caps-ipc-wrap.js +55 -0
- package/build/tests/libs-for-tests/core-runner.d.ts +39 -0
- package/build/tests/libs-for-tests/core-runner.js +231 -0
- package/build/tests/libs-for-tests/jasmine-utils.d.ts +13 -0
- package/build/tests/libs-for-tests/jasmine-utils.js +110 -0
- package/build/tests/libs-for-tests/json-equal.d.ts +1 -0
- package/build/tests/libs-for-tests/json-equal.js +78 -0
- package/build/tests/libs-for-tests/services-runner.d.ts +18 -0
- package/build/tests/libs-for-tests/services-runner.js +64 -0
- package/build/tests/libs-for-tests/setups.d.ts +51 -0
- package/build/tests/libs-for-tests/setups.js +305 -0
- package/build/tests/libs-for-tests/spec-module.d.ts +16 -0
- package/build/tests/libs-for-tests/spec-module.js +92 -0
- package/build/tests/libs-for-tests/startup.d.ts +1 -0
- package/build/tests/libs-for-tests/startup.js +29 -0
- package/build/tests/libs-for-tests/watching.d.ts +7 -0
- package/build/tests/libs-for-tests/watching.js +33 -0
- package/build/tests/units/canonical-address.d.ts +1 -0
- package/build/tests/units/canonical-address.js +41 -0
- package/build/tests/units/device-fs.d.ts +1 -0
- package/build/tests/units/device-fs.js +70 -0
- package/build/tests/units/folder-node-serialization.d.ts +1 -0
- package/build/tests/units/folder-node-serialization.js +74 -0
- package/build/tests/units/mid-sigs-NaCl-Ed.d.ts +1 -0
- package/build/tests/units/mid-sigs-NaCl-Ed.js +123 -0
- package/build/tests/units/number-line.d.ts +1 -0
- package/build/tests/units/number-line.js +63 -0
- package/build/tests/units/obj-folders.d.ts +1 -0
- package/build/tests/units/obj-folders.js +152 -0
- package/package.json +11 -3
- package/postinstall.js +1 -1
- package/build/cryptors.d.ts +0 -1
- package/build/cryptors.js +0 -34
- package/build/lib-client/cryptor/cryptor-in-worker.d.ts +0 -20
- package/build/lib-client/cryptor/cryptor-in-worker.js +0 -357
- package/build/lib-client/cryptor/cryptor-wasm.js +0 -1
- package/build/lib-client/cryptor/cryptor.d.ts +0 -30
- package/build/lib-client/cryptor/cryptor.js +0 -44
- package/build/lib-client/cryptor/cryptor.wasm +0 -0
- package/build/lib-client/cryptor/in-proc-js.d.ts +0 -2
- package/build/lib-client/cryptor/in-proc-js.js +0 -57
- package/build/lib-client/cryptor/in-proc-wasm.d.ts +0 -2
- package/build/lib-client/cryptor/in-proc-wasm.js +0 -176
- package/build/lib-client/cryptor/serialization-for-wasm.d.ts +0 -35
- package/build/lib-client/cryptor/serialization-for-wasm.js +0 -57
- package/build/lib-client/cryptor/wasm-mp1-modules.d.ts +0 -5
- package/build/lib-client/cryptor/wasm-mp1-modules.js +0 -78
- package/build/lib-client/cryptor/worker-js.js +0 -131
- package/build/lib-client/cryptor/worker-wasm.js +0 -37
- package/build/protos/cryptor.proto.js +0 -1804
- /package/build/lib-client/{cryptor/cryptor-work-labels.d.ts → cryptor-work-labels.d.ts} +0 -0
- /package/build/lib-client/{cryptor/cryptor-work-labels.js → cryptor-work-labels.js} +0 -0
- /package/build/{lib-client/cryptor/worker-js.d.ts → tests/caps-api/asmail.d.ts} +0 -0
- /package/build/{lib-client/cryptor/worker-wasm.d.ts → tests/caps-api/keyrings.d.ts} +0 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (C) 2022, 2025 - 2026 3NSoft Inc.
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
6
|
+
the terms of the GNU General Public License as published by the Free Software
|
|
7
|
+
Foundation, either version 3 of the License, or (at your option) any later
|
|
8
|
+
version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful, but
|
|
11
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
13
|
+
See the GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License along with
|
|
16
|
+
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.specs = void 0;
|
|
20
|
+
const random_node_1 = require("../../../../lib-common/random-node");
|
|
21
|
+
const json_equal_1 = require("../../../libs-for-tests/json-equal");
|
|
22
|
+
const watching_1 = require("../../../libs-for-tests/watching");
|
|
23
|
+
exports.specs = {
|
|
24
|
+
description: '--',
|
|
25
|
+
its: []
|
|
26
|
+
};
|
|
27
|
+
let it = {
|
|
28
|
+
expectation: 'conflict in folder can be resolved by adopting remote version'
|
|
29
|
+
};
|
|
30
|
+
it.func = async function ({ dev1FS, dev2FS }) {
|
|
31
|
+
const file = 'file-for-conflict';
|
|
32
|
+
let folderStatus = await dev2FS().v.sync.status('');
|
|
33
|
+
expect(folderStatus.state).withContext(`from setup`).toBe('synced');
|
|
34
|
+
// make file simulatneously on both devices
|
|
35
|
+
await dev1FS().writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(100));
|
|
36
|
+
await dev2FS().writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(70));
|
|
37
|
+
folderStatus = await dev2FS().v.sync.status('');
|
|
38
|
+
expect(folderStatus.state).toBe('unsynced');
|
|
39
|
+
// and upload changes from dev1
|
|
40
|
+
await dev1FS().v.sync.upload(file);
|
|
41
|
+
await dev1FS().v.sync.upload('');
|
|
42
|
+
folderStatus = await dev2FS().v.sync.status('');
|
|
43
|
+
expect(folderStatus.state).toBe('conflicting');
|
|
44
|
+
expect(await dev2FS().readTxtFile(file))
|
|
45
|
+
.withContext(`we have placed a different content into these conflicting file versions`)
|
|
46
|
+
.not.toBe(await dev1FS().readTxtFile(file));
|
|
47
|
+
// folder versions diff is a tool for getting folder conflict details
|
|
48
|
+
const diff = await dev2FS().v.sync.diffCurrentAndRemoteFolderVersions('');
|
|
49
|
+
expect(diff).toBeDefined();
|
|
50
|
+
expect(diff.currentVersion).toBe(folderStatus.local.latest);
|
|
51
|
+
expect(diff.remoteVersion).toBe(folderStatus.remote.latest);
|
|
52
|
+
expect(diff.added.inLocal.includes(file)).toBeTrue();
|
|
53
|
+
expect(diff.added.inRemote.includes(file)).toBeTrue();
|
|
54
|
+
expect(diff.nameOverlaps).toContain(file);
|
|
55
|
+
const syncEvents = (0, watching_1.watchForEvents)(obs => dev2FS().watchFolder('', obs), 2, ev => (ev.src === 'sync'));
|
|
56
|
+
// adopt remote in a conflict state
|
|
57
|
+
await dev2FS().v.sync.adoptRemote('').then(() => fail(``), (exc) => {
|
|
58
|
+
expect(exc.type).toBe('fs-sync');
|
|
59
|
+
expect(exc.conflict).toBeTrue();
|
|
60
|
+
});
|
|
61
|
+
await dev2FS().v.sync.adoptRemote('', { remoteVersion: folderStatus.remote.latest });
|
|
62
|
+
await syncEvents.completion;
|
|
63
|
+
expect(syncEvents.collectedEvents.find(ev => ((ev.type === 'entry-removal') && (ev.name === file))))
|
|
64
|
+
.withContext(`Different object on the same path is removed`)
|
|
65
|
+
.toBeDefined();
|
|
66
|
+
expect(syncEvents.collectedEvents.find(ev => ((ev.type === 'entry-addition') && (ev.entry.name === file))))
|
|
67
|
+
.withContext(`Element from remote version is added`)
|
|
68
|
+
.toBeDefined();
|
|
69
|
+
folderStatus = await dev2FS().v.sync.status('');
|
|
70
|
+
expect(folderStatus.state).toBe('synced');
|
|
71
|
+
// we have also adopted here new file object on the same path
|
|
72
|
+
expect(await dev2FS().readTxtFile(file))
|
|
73
|
+
.withContext(`after adoption of folder dev2 reads correct file object`)
|
|
74
|
+
.toBe(await dev1FS().readTxtFile(file));
|
|
75
|
+
let fileStatus = await dev2FS().v.sync.status(file);
|
|
76
|
+
expect(fileStatus.state)
|
|
77
|
+
.withContext(`when a file system item is brought the first time from a server, its state should necessarily be synced`)
|
|
78
|
+
.toBe('synced');
|
|
79
|
+
};
|
|
80
|
+
exports.specs.its.push(it);
|
|
81
|
+
it = {
|
|
82
|
+
expectation: `conflict in folder can be resolved by uploading local version, assembled from elements on both sides of the conflict`
|
|
83
|
+
};
|
|
84
|
+
it.func = async function ({ dev1FS, dev2FS }) {
|
|
85
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
86
|
+
const file = 'file-for-conflict';
|
|
87
|
+
const fileFromDev1 = 'file-from-dev1';
|
|
88
|
+
const fileFromDev2 = 'file-from-dev2';
|
|
89
|
+
let folderStatus = await dev2FS().v.sync.status('');
|
|
90
|
+
expect(folderStatus.state).withContext(`from setup`).toBe('synced');
|
|
91
|
+
expect((_a = folderStatus.synced) === null || _a === void 0 ? void 0 : _a.latest).withContext(`from setup`).toBe(1);
|
|
92
|
+
// make file simulatneously on both devices
|
|
93
|
+
await dev1FS().writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(100));
|
|
94
|
+
await dev1FS().writeTxtFile(fileFromDev1, (0, random_node_1.stringOfB64CharsSync)(100));
|
|
95
|
+
await dev2FS().writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(70));
|
|
96
|
+
await dev2FS().writeTxtFile(fileFromDev2, (0, random_node_1.stringOfB64CharsSync)(70));
|
|
97
|
+
folderStatus = await dev2FS().v.sync.status('');
|
|
98
|
+
expect(folderStatus.state).toBe('unsynced');
|
|
99
|
+
// and upload changes from dev1
|
|
100
|
+
await dev1FS().v.sync.upload(file);
|
|
101
|
+
await dev1FS().v.sync.upload(fileFromDev1);
|
|
102
|
+
await dev1FS().v.sync.upload('');
|
|
103
|
+
// and upload on dev2 files, one will be adopted later, another removed
|
|
104
|
+
await dev2FS().v.sync.upload(file);
|
|
105
|
+
await dev2FS().v.sync.upload(fileFromDev2);
|
|
106
|
+
folderStatus = await dev2FS().v.sync.status('');
|
|
107
|
+
expect(folderStatus.state).toBe('conflicting');
|
|
108
|
+
expect((_b = folderStatus.remote) === null || _b === void 0 ? void 0 : _b.latest).toBe(2);
|
|
109
|
+
// folder versions diff is a tool for getting folder conflict details
|
|
110
|
+
const diff = await dev2FS().v.sync.diffCurrentAndRemoteFolderVersions('');
|
|
111
|
+
expect(diff).toBeDefined();
|
|
112
|
+
expect(diff.currentVersion).toBe(folderStatus.local.latest);
|
|
113
|
+
expect(diff.remoteVersion).toBe(folderStatus.remote.latest);
|
|
114
|
+
expect(diff.added.inLocal.includes(file)).toBeTrue();
|
|
115
|
+
expect(diff.added.inLocal.includes(fileFromDev2)).toBeTrue();
|
|
116
|
+
expect(diff.added.inRemote.includes(file)).toBeTrue();
|
|
117
|
+
expect(diff.added.inRemote.includes(fileFromDev1)).toBeTrue();
|
|
118
|
+
expect(diff.nameOverlaps).toContain(file);
|
|
119
|
+
// we can stat remote child
|
|
120
|
+
let statsRemote = await dev2FS().v.sync.statRemoteItem('', file, diff.remoteVersion);
|
|
121
|
+
let statsLocal = await dev2FS().stat(file);
|
|
122
|
+
let statsOn1 = await dev1FS().stat(file);
|
|
123
|
+
expect((_c = statsRemote.mtime) === null || _c === void 0 ? void 0 : _c.valueOf()).not.toBe((_d = statsLocal.mtime) === null || _d === void 0 ? void 0 : _d.valueOf());
|
|
124
|
+
expect((_e = statsRemote.mtime) === null || _e === void 0 ? void 0 : _e.valueOf()).toBe((_f = statsOn1.mtime) === null || _f === void 0 ? void 0 : _f.valueOf());
|
|
125
|
+
expect(statsRemote.size).not.toBe(statsLocal.size);
|
|
126
|
+
expect(statsRemote.size).toBe(statsOn1.size);
|
|
127
|
+
// we can read remote child
|
|
128
|
+
let remoteChild = await dev2FS().v.sync.getRemoteFileItem('', file, diff.remoteVersion);
|
|
129
|
+
expect(await remoteChild.readTxt()).toBe(await dev1FS().readTxtFile(file));
|
|
130
|
+
expect((await remoteChild.stat()).size).toBe(statsOn1.size);
|
|
131
|
+
// adopt on dev2 some remote elements from dev1
|
|
132
|
+
await dev2FS().v.sync.adoptRemoteFolderItem('', file, { replaceLocalItem: true });
|
|
133
|
+
await dev2FS().v.sync.adoptRemoteFolderItem('', fileFromDev1);
|
|
134
|
+
expect(await dev2FS().readTxtFile(file))
|
|
135
|
+
.toBe(await dev1FS().readTxtFile(file));
|
|
136
|
+
expect(await dev2FS().readTxtFile(fileFromDev1))
|
|
137
|
+
.toBe(await dev1FS().readTxtFile(fileFromDev1));
|
|
138
|
+
expect(await dev1FS().checkFilePresence(fileFromDev2)).toBeFalse();
|
|
139
|
+
// note need of explicit parameters when uploading from conflict state
|
|
140
|
+
folderStatus = await dev2FS().v.sync.status('');
|
|
141
|
+
expect(folderStatus.state).toBe('conflicting');
|
|
142
|
+
await dev2FS().v.sync.upload('').then(() => fail(`Upload in conflict must require explicit parameters`), (exc) => {
|
|
143
|
+
expect(exc.type).toBe('fs-sync');
|
|
144
|
+
expect(exc.versionMismatch).toBeTrue();
|
|
145
|
+
});
|
|
146
|
+
// upload new version, now from dev2
|
|
147
|
+
await dev2FS().v.sync.upload('', { uploadVersion: folderStatus.remote.latest + 1 });
|
|
148
|
+
folderStatus = await dev2FS().v.sync.status('');
|
|
149
|
+
expect(folderStatus.state).toBe('synced');
|
|
150
|
+
expect((_g = folderStatus.synced) === null || _g === void 0 ? void 0 : _g.latest).toBe(3);
|
|
151
|
+
// get new version on dev1 and check contents
|
|
152
|
+
await dev1FS().v.sync.adoptRemote('');
|
|
153
|
+
const lst = await dev1FS().listFolder('');
|
|
154
|
+
expect(lst.find(e => ((e.name === file) && e.isFile))).toBeDefined();
|
|
155
|
+
expect(lst.find(e => ((e.name === fileFromDev1) && e.isFile))).toBeDefined();
|
|
156
|
+
expect(lst.find(e => ((e.name === fileFromDev2) && e.isFile))).toBeDefined();
|
|
157
|
+
expect(await dev2FS().readTxtFile(fileFromDev2))
|
|
158
|
+
.toBe(await dev1FS().readTxtFile(fileFromDev2));
|
|
159
|
+
};
|
|
160
|
+
exports.specs.its.push(it);
|
|
161
|
+
it = {
|
|
162
|
+
expectation: 'conflict in file can be resolved by adopting remote version'
|
|
163
|
+
};
|
|
164
|
+
it.func = async function ({ dev1FS, dev2FS }) {
|
|
165
|
+
const file = 'file-for-conflict';
|
|
166
|
+
// create file on dev1 and upload, so that dev2 can get it
|
|
167
|
+
await dev1FS().writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(100));
|
|
168
|
+
await dev1FS().v.sync.upload(file);
|
|
169
|
+
await dev1FS().v.sync.upload('');
|
|
170
|
+
await dev2FS().v.sync.status(''); // implicit check of server
|
|
171
|
+
await dev2FS().v.sync.adoptRemote('');
|
|
172
|
+
let fileStatus = await dev2FS().v.sync.status(file);
|
|
173
|
+
expect(fileStatus.state).withContext(`when a file system item is brought the first time from a server, its state should necessarily be synced`)
|
|
174
|
+
.toBe('synced');
|
|
175
|
+
const fstObservedVersion = fileStatus.synced.latest;
|
|
176
|
+
// change file on dev1 and propagate
|
|
177
|
+
await dev1FS().writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(200));
|
|
178
|
+
await dev1FS().v.sync.upload(file);
|
|
179
|
+
fileStatus = await dev2FS().v.sync.status(file);
|
|
180
|
+
expect(fileStatus.state).withContext(`changes to a file system item that come from a server can be adopted only explicitly, and state should indicate that current version is behind remote one`).toBe('behind');
|
|
181
|
+
expect(fileStatus.remote.latest).toBeGreaterThan(fstObservedVersion);
|
|
182
|
+
// change file on dev2 to reach conflict state
|
|
183
|
+
await dev2FS().writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(150));
|
|
184
|
+
fileStatus = await dev2FS().v.sync.status(file);
|
|
185
|
+
expect(fileStatus.state).toBe('conflicting');
|
|
186
|
+
const syncEvents = (0, watching_1.watchForEvents)(obs => dev2FS().watchFile(file, obs), 1, ev => (ev.src === 'sync'));
|
|
187
|
+
// adopt remote in a conflict state
|
|
188
|
+
await dev2FS().v.sync.adoptRemote(file).then(() => fail(``), (exc) => {
|
|
189
|
+
expect(exc.type).toBe('fs-sync');
|
|
190
|
+
expect(exc.conflict).toBeTrue();
|
|
191
|
+
});
|
|
192
|
+
await dev2FS().v.sync.adoptRemote(file, { remoteVersion: fileStatus.remote.latest });
|
|
193
|
+
await syncEvents.completion;
|
|
194
|
+
expect(syncEvents.collectedEvents.find(ev => (ev.type === 'file-change')))
|
|
195
|
+
.withContext(``)
|
|
196
|
+
.toBeDefined();
|
|
197
|
+
fileStatus = await dev2FS().v.sync.status('');
|
|
198
|
+
expect(fileStatus.state).toBe('synced');
|
|
199
|
+
expect(await dev2FS().readTxtFile(file))
|
|
200
|
+
.toBe(await dev1FS().readTxtFile(file));
|
|
201
|
+
};
|
|
202
|
+
exports.specs.its.push(it);
|
|
203
|
+
it = {
|
|
204
|
+
expectation: 'conflict in file can be resolved by uploading local version'
|
|
205
|
+
};
|
|
206
|
+
it.func = async function ({ dev1FS, dev2FS }) {
|
|
207
|
+
const file = 'file-for-conflict';
|
|
208
|
+
// create file on dev1 and upload, so that dev2 can get it
|
|
209
|
+
await dev1FS().writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(100));
|
|
210
|
+
await dev1FS().v.sync.upload(file);
|
|
211
|
+
await dev1FS().v.sync.upload('');
|
|
212
|
+
await dev2FS().v.sync.status(''); // implicit check of server
|
|
213
|
+
await dev2FS().v.sync.adoptRemote('');
|
|
214
|
+
// change file on dev1 and dev2
|
|
215
|
+
await dev1FS().writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(200));
|
|
216
|
+
await dev2FS().writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(150));
|
|
217
|
+
// and upload changes from dev1 to reach conflict on dev2
|
|
218
|
+
await dev1FS().v.sync.upload(file);
|
|
219
|
+
let fileStatusOnDev2 = await dev2FS().v.sync.status(file);
|
|
220
|
+
expect(fileStatusOnDev2.state).toBe('conflicting');
|
|
221
|
+
// push version on dev2 higher to see it going down to uploaded level
|
|
222
|
+
for (let i = 0; i < 3; i += 1) {
|
|
223
|
+
await dev2FS().writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(151 + i));
|
|
224
|
+
}
|
|
225
|
+
const remoteVersionBeforeUpload = fileStatusOnDev2.remote.latest;
|
|
226
|
+
expect((await dev2FS().stat(file)).version)
|
|
227
|
+
.toBeGreaterThan(remoteVersionBeforeUpload + 1);
|
|
228
|
+
await dev2FS().v.sync.upload(file).then(() => fail(`Upload in conflicting state should require explicit versioning`), (exc) => {
|
|
229
|
+
expect(exc.versionMismatch).toBeTrue();
|
|
230
|
+
});
|
|
231
|
+
await dev2FS().v.sync.upload(file, { uploadVersion: fileStatusOnDev2.remote.latest + 1 });
|
|
232
|
+
fileStatusOnDev2 = await dev2FS().v.sync.status(file);
|
|
233
|
+
expect(fileStatusOnDev2.state).toBe('synced');
|
|
234
|
+
expect(fileStatusOnDev2.synced.latest).toBe(remoteVersionBeforeUpload + 1);
|
|
235
|
+
await dev1FS().v.sync.status(file); // implicit check of server
|
|
236
|
+
await dev1FS().v.sync.adoptRemote(file);
|
|
237
|
+
expect(await dev2FS().readTxtFile(file))
|
|
238
|
+
.toBe(await dev1FS().readTxtFile(file));
|
|
239
|
+
};
|
|
240
|
+
exports.specs.its.push(it);
|
|
241
|
+
it = {
|
|
242
|
+
expectation: 'upload over conflict concurrent with file write via sink'
|
|
243
|
+
};
|
|
244
|
+
it.func = async function ({ dev1FS, dev2FS }) {
|
|
245
|
+
const file = 'file-for-conflict';
|
|
246
|
+
// create file on dev1 and upload, so that dev2 can get it
|
|
247
|
+
await dev1FS().writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(100));
|
|
248
|
+
await dev1FS().v.sync.upload(file);
|
|
249
|
+
await dev1FS().v.sync.upload('');
|
|
250
|
+
await dev2FS().v.sync.status(''); // implicit check of server
|
|
251
|
+
await dev2FS().v.sync.adoptRemote('');
|
|
252
|
+
// change file on dev1 and dev2
|
|
253
|
+
await dev1FS().writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(200));
|
|
254
|
+
await dev2FS().writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(150));
|
|
255
|
+
// and upload changes from dev1 to reach conflict on dev2
|
|
256
|
+
await dev1FS().v.sync.upload(file);
|
|
257
|
+
let fileStatusOnDev2 = await dev2FS().v.sync.status(file);
|
|
258
|
+
expect(fileStatusOnDev2.state).toBe('conflicting');
|
|
259
|
+
expect(fileStatusOnDev2.remote.latest).toBe(2);
|
|
260
|
+
// push version on dev2 higher to see it going down to uploaded level
|
|
261
|
+
let contentBeforeUpload = undefined;
|
|
262
|
+
for (let i = 0; i < 3; i += 1) {
|
|
263
|
+
contentBeforeUpload = (0, random_node_1.stringOfB64CharsSync)(151 + i);
|
|
264
|
+
await dev2FS().writeTxtFile(file, contentBeforeUpload);
|
|
265
|
+
}
|
|
266
|
+
// start long write to run concurrent with upload
|
|
267
|
+
const longWrite = await dev2FS().v.getByteSink(file);
|
|
268
|
+
// make concurrent upload
|
|
269
|
+
const uploadCompletion = dev2FS().v.sync.upload(file, { uploadVersion: fileStatusOnDev2.remote.latest + 1 });
|
|
270
|
+
await longWrite.sink.truncate(20);
|
|
271
|
+
// complete the write
|
|
272
|
+
await longWrite.sink.done();
|
|
273
|
+
// upload was scheduled after write completion, hence, awaiting after it
|
|
274
|
+
await uploadCompletion;
|
|
275
|
+
fileStatusOnDev2 = await dev2FS().v.sync.status(file);
|
|
276
|
+
expect(fileStatusOnDev2.state).toBe('unsynced');
|
|
277
|
+
expect(fileStatusOnDev2.synced.latest).toBe(3);
|
|
278
|
+
expect(fileStatusOnDev2.local.latest).toBe(longWrite.version);
|
|
279
|
+
await dev1FS().v.sync.status(file);
|
|
280
|
+
await dev1FS().v.sync.adoptRemote(file);
|
|
281
|
+
expect(await dev1FS().readTxtFile(file)).toBe(contentBeforeUpload);
|
|
282
|
+
await dev2FS().v.sync.upload(file);
|
|
283
|
+
fileStatusOnDev2 = await dev2FS().v.sync.status(file);
|
|
284
|
+
expect(fileStatusOnDev2.state).toBe('synced');
|
|
285
|
+
expect(fileStatusOnDev2.synced.latest).toBe(4);
|
|
286
|
+
await dev1FS().v.sync.status(file);
|
|
287
|
+
await dev1FS().v.sync.adoptRemote(file);
|
|
288
|
+
expect(await dev2FS().readTxtFile(file))
|
|
289
|
+
.toBe(await dev1FS().readTxtFile(file));
|
|
290
|
+
};
|
|
291
|
+
exports.specs.its.push(it);
|
|
292
|
+
it = {
|
|
293
|
+
expectation: 'viewing and adopting remote folder item'
|
|
294
|
+
};
|
|
295
|
+
it.func = async function ({ dev1FS, dev2FS }) {
|
|
296
|
+
const fileA = `file-a`;
|
|
297
|
+
const folder = 'folder-for-conflict';
|
|
298
|
+
const file1 = `file-1`;
|
|
299
|
+
const file1path = `${folder}/${file1}`;
|
|
300
|
+
const file2 = `file-2`;
|
|
301
|
+
const file2path = `${folder}/${file2}`;
|
|
302
|
+
// create tree on dev1 and upload
|
|
303
|
+
for (const path of [file1path, file2path, fileA]) {
|
|
304
|
+
await dev1FS().writeTxtFile(path, (0, random_node_1.stringOfB64CharsSync)(100));
|
|
305
|
+
}
|
|
306
|
+
for (const path of [file1path, file2path, folder, fileA, '']) {
|
|
307
|
+
await dev1FS().v.sync.upload(path);
|
|
308
|
+
}
|
|
309
|
+
// tree is not local on dev2
|
|
310
|
+
let syncStatus = await dev2FS().v.sync.status(''); // implicit check of server
|
|
311
|
+
expect(syncStatus.state).toBe('behind');
|
|
312
|
+
expect(await dev2FS().checkFolderPresence(folder)).toBeFalse();
|
|
313
|
+
// can read remote items
|
|
314
|
+
let lst = await dev2FS().v.sync.listRemoteFolderItem('', folder);
|
|
315
|
+
let remoteFS = await dev2FS().v.sync.getRemoteFolderItem('', folder);
|
|
316
|
+
expect((0, json_equal_1.deepEqual)(lst, await remoteFS.listFolder(''))).toBeTrue();
|
|
317
|
+
expect(remoteFS.writable).toBeFalse();
|
|
318
|
+
expect(await remoteFS.readTxtFile(file1)).toBe(await dev1FS().readTxtFile(file1path));
|
|
319
|
+
expect(await remoteFS.readTxtFile(file2)).toBe(await dev1FS().readTxtFile(file2path));
|
|
320
|
+
// making a conflicting folder on dev2
|
|
321
|
+
const fileB = `file-b`;
|
|
322
|
+
const file3 = `file-3`;
|
|
323
|
+
const file3path = `${folder}/${file3}`;
|
|
324
|
+
for (const path of [file1path, file3path, fileB]) {
|
|
325
|
+
await dev2FS().writeTxtFile(path, (0, random_node_1.stringOfB64CharsSync)(100));
|
|
326
|
+
}
|
|
327
|
+
for (const path of [file1path, file3path, folder, fileB]) {
|
|
328
|
+
await dev2FS().v.sync.upload(path);
|
|
329
|
+
}
|
|
330
|
+
syncStatus = await dev2FS().v.sync.status(''); // implicit check of server
|
|
331
|
+
expect(syncStatus.state).toBe('conflicting');
|
|
332
|
+
const diff = await dev2FS().v.sync.diffCurrentAndRemoteFolderVersions('', syncStatus.remote.latest);
|
|
333
|
+
expect(diff.added.inLocal.includes(folder)).toBeTrue();
|
|
334
|
+
expect(diff.added.inLocal.includes(fileB)).toBeTrue();
|
|
335
|
+
expect(diff.added.inRemote.includes(folder)).toBeTrue();
|
|
336
|
+
expect(diff.added.inRemote.includes(fileA)).toBeTrue();
|
|
337
|
+
expect(diff.nameOverlaps).toContain(folder);
|
|
338
|
+
await dev2FS().v.sync.absorbRemoteFolderChanges('').then(() => fail(`Overlapping names require presence of prefix`), (exc) => {
|
|
339
|
+
expect(exc.type).toBe('fs-sync');
|
|
340
|
+
expect(Array.isArray(exc.nameOverlaps)).toBeTrue();
|
|
341
|
+
});
|
|
342
|
+
const postfixForNameOverlaps = `-remote`;
|
|
343
|
+
let version = await dev2FS().v.sync.absorbRemoteFolderChanges('', { postfixForNameOverlaps });
|
|
344
|
+
expect(version).toBe(diff.currentVersion + 1);
|
|
345
|
+
expect(await dev2FS().checkFilePresence(fileA)).toBeTrue();
|
|
346
|
+
expect(await dev2FS().readTxtFile(fileA)).toBe(await dev1FS().readTxtFile(fileA));
|
|
347
|
+
expect((0, json_equal_1.deepEqual)(await dev2FS().listFolder(`${folder}${postfixForNameOverlaps}`), await dev1FS().listFolder(folder))).toBeTrue();
|
|
348
|
+
};
|
|
349
|
+
exports.specs.its.push(it);
|
|
350
|
+
it = {
|
|
351
|
+
expectation: `viewing remote folder items when it wasn't local or was removed locally`
|
|
352
|
+
};
|
|
353
|
+
it.func = async function ({ dev1FS, dev2FS }) {
|
|
354
|
+
const file = `file_to_remove`;
|
|
355
|
+
const folder = `folder_to_remove`;
|
|
356
|
+
const fileA = `file-A`;
|
|
357
|
+
const folderA = `folder-A`;
|
|
358
|
+
// make file on dev1 and upload
|
|
359
|
+
await dev1FS().writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(100));
|
|
360
|
+
await dev1FS().v.sync.upload(file);
|
|
361
|
+
await dev1FS().makeFolder(folder);
|
|
362
|
+
await dev1FS().v.sync.upload(folder);
|
|
363
|
+
await dev1FS().v.sync.upload('');
|
|
364
|
+
await dev2FS().v.sync.adoptRemote('');
|
|
365
|
+
expect(await dev2FS().checkFilePresence(file, true)).toBeTrue();
|
|
366
|
+
// change dev1 for conflict condition on folder path
|
|
367
|
+
await dev1FS().writeTxtFile(fileA, (0, random_node_1.stringOfB64CharsSync)(50));
|
|
368
|
+
await dev1FS().v.sync.upload(fileA);
|
|
369
|
+
await dev1FS().makeFolder(folderA);
|
|
370
|
+
await dev1FS().v.sync.upload(folderA);
|
|
371
|
+
await dev1FS().v.sync.upload('');
|
|
372
|
+
// remove file on dev2
|
|
373
|
+
await dev2FS().deleteFile(file);
|
|
374
|
+
await dev2FS().deleteFolder(folder);
|
|
375
|
+
const diff = await dev2FS().v.sync.diffCurrentAndRemoteFolderVersions('');
|
|
376
|
+
expect((await dev2FS().v.sync.statRemoteItem('', fileA, diff.remoteVersion)).size)
|
|
377
|
+
.toBe((await dev1FS().stat(fileA)).size);
|
|
378
|
+
expect((await dev2FS().v.sync.statRemoteItem('', file, diff.remoteVersion)).size)
|
|
379
|
+
.toBe((await dev1FS().stat(file)).size);
|
|
380
|
+
expect((await dev2FS().v.sync.statRemoteItem('', folderA, diff.remoteVersion)).mtime.valueOf())
|
|
381
|
+
.toBe((await dev1FS().stat(folderA)).mtime.valueOf());
|
|
382
|
+
expect((await dev2FS().v.sync.statRemoteItem('', folder, diff.remoteVersion)).mtime.valueOf())
|
|
383
|
+
.toBe((await dev1FS().stat(folder)).mtime.valueOf());
|
|
384
|
+
let fileObj = await dev2FS().v.sync.getRemoteFileItem('', file, diff.remoteVersion);
|
|
385
|
+
expect((await fileObj.readTxt())).toBe(await dev1FS().readTxtFile(file));
|
|
386
|
+
fileObj = await dev2FS().v.sync.getRemoteFileItem('', fileA, diff.remoteVersion);
|
|
387
|
+
expect((await fileObj.readTxt())).toBe(await dev1FS().readTxtFile(fileA));
|
|
388
|
+
let folderObj = await dev2FS().v.sync.getRemoteFolderItem('', folder, diff.remoteVersion);
|
|
389
|
+
expect((0, json_equal_1.deepEqual)(await folderObj.listFolder(''), await dev1FS().listFolder(folder))).toBeTrue();
|
|
390
|
+
expect(folderObj.v).toBeUndefined();
|
|
391
|
+
folderObj = await dev2FS().v.sync.getRemoteFolderItem('', folderA, diff.remoteVersion);
|
|
392
|
+
expect((0, json_equal_1.deepEqual)(await folderObj.listFolder(''), await dev1FS().listFolder(folderA))).toBeTrue();
|
|
393
|
+
expect(folderObj.v).toBeUndefined();
|
|
394
|
+
};
|
|
395
|
+
exports.specs.its.push(it);
|
|
396
|
+
Object.freeze(exports);
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (C) 2022, 2025 3NSoft Inc.
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
6
|
+
the terms of the GNU General Public License as published by the Free Software
|
|
7
|
+
Foundation, either version 3 of the License, or (at your option) any later
|
|
8
|
+
version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful, but
|
|
11
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
13
|
+
See the GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License along with
|
|
16
|
+
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.specs = void 0;
|
|
20
|
+
const buffer_utils_1 = require("../../../../lib-common/buffer-utils");
|
|
21
|
+
const deferred_1 = require("../../../../lib-common/processes/deferred");
|
|
22
|
+
const random_node_1 = require("../../../../lib-common/random-node");
|
|
23
|
+
const bytes_equal_1 = require("../../../libs-for-tests/bytes-equal");
|
|
24
|
+
const json_equal_1 = require("../../../libs-for-tests/json-equal");
|
|
25
|
+
const test_utils_1 = require("../test-utils");
|
|
26
|
+
exports.specs = {
|
|
27
|
+
description: '--',
|
|
28
|
+
its: []
|
|
29
|
+
};
|
|
30
|
+
let it = {
|
|
31
|
+
expectation: 'when online, other device observes events'
|
|
32
|
+
};
|
|
33
|
+
it.func = async function ({ dev1FS, dev2FS }) {
|
|
34
|
+
const file = 'file-1';
|
|
35
|
+
let fs1 = dev1FS();
|
|
36
|
+
let fs2 = dev2FS();
|
|
37
|
+
const evAtDev2 = (0, test_utils_1.observeFolderForOneEvent)(fs2);
|
|
38
|
+
let status = await fs2.v.sync.status('');
|
|
39
|
+
expect(status.state).withContext(`from setup`).toBe('synced');
|
|
40
|
+
await fs1.writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(100));
|
|
41
|
+
await fs1.v.sync.upload(file);
|
|
42
|
+
await fs1.v.sync.upload('');
|
|
43
|
+
const folderChangeEvent = await evAtDev2;
|
|
44
|
+
expect(folderChangeEvent.type).toBe('remote-change');
|
|
45
|
+
status = await fs2.v.sync.status('');
|
|
46
|
+
expect(status.state).toBe('behind');
|
|
47
|
+
await fs2.readTxtFile(file).then(() => fail(`There should be no file still in folder on dev 2`), (exc) => expect(exc.notFound).toBeTrue());
|
|
48
|
+
await fs2.v.sync.adoptRemote('');
|
|
49
|
+
status = await fs2.v.sync.status('');
|
|
50
|
+
const statusOnDev1 = await fs1.v.sync.status('');
|
|
51
|
+
expect(status.state).toBe('synced');
|
|
52
|
+
expect(status.synced.latest).toBe(statusOnDev1.synced.latest);
|
|
53
|
+
expect(await fs2.readTxtFile(file))
|
|
54
|
+
.toBe(await fs1.readTxtFile(file));
|
|
55
|
+
};
|
|
56
|
+
exports.specs.its.push(it);
|
|
57
|
+
it = {
|
|
58
|
+
expectation: 'other device should check updates when coming online'
|
|
59
|
+
};
|
|
60
|
+
it.func = async function ({ dev1FS, dev2FS, dev2 }) {
|
|
61
|
+
const file = 'file-1';
|
|
62
|
+
let fs1 = dev1FS();
|
|
63
|
+
let fs2 = dev2FS();
|
|
64
|
+
let status = await fs2.v.sync.status('');
|
|
65
|
+
expect(status.state).withContext(`from setup`).toBe('synced');
|
|
66
|
+
await dev2.stop();
|
|
67
|
+
await fs1.writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(100));
|
|
68
|
+
await fs1.v.sync.upload(file);
|
|
69
|
+
await fs1.v.sync.upload('');
|
|
70
|
+
await dev2.start();
|
|
71
|
+
fs2 = dev2FS();
|
|
72
|
+
status = await fs2.v.sync.status('', true);
|
|
73
|
+
expect(status.state).toBe('synced');
|
|
74
|
+
status = await fs2.v.sync.status('');
|
|
75
|
+
expect(status.state).toBe('behind');
|
|
76
|
+
await fs2.v.sync.adoptRemote('');
|
|
77
|
+
status = await fs2.v.sync.status('');
|
|
78
|
+
const statusOnDev1 = await fs1.v.sync.status('');
|
|
79
|
+
expect(status.state).toBe('synced');
|
|
80
|
+
expect(status.synced.latest).toBe(statusOnDev1.synced.latest);
|
|
81
|
+
expect(await fs2.readTxtFile(file))
|
|
82
|
+
.toBe(await fs1.readTxtFile(file));
|
|
83
|
+
};
|
|
84
|
+
exports.specs.its.push(it);
|
|
85
|
+
function collectFileDownloadEvents(fs, path) {
|
|
86
|
+
const startEvent = (0, deferred_1.defer)();
|
|
87
|
+
const doneEvent = (0, deferred_1.defer)();
|
|
88
|
+
const progressEvents = [];
|
|
89
|
+
const unsub = fs.watchFile(path, {
|
|
90
|
+
next: ev => {
|
|
91
|
+
switch (ev.type) {
|
|
92
|
+
case 'download-started':
|
|
93
|
+
startEvent.resolve(ev);
|
|
94
|
+
break;
|
|
95
|
+
case 'download-done':
|
|
96
|
+
doneEvent.resolve(ev);
|
|
97
|
+
unsub();
|
|
98
|
+
break;
|
|
99
|
+
case 'download-progress':
|
|
100
|
+
progressEvents.push(ev);
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
startEvent: startEvent.promise,
|
|
107
|
+
doneEvent: doneEvent.promise,
|
|
108
|
+
progressEvents
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
it = {
|
|
112
|
+
expectation: 'remote version is downloaded non-automatically'
|
|
113
|
+
};
|
|
114
|
+
it.func = async function ({ dev1FS, dev2FS }) {
|
|
115
|
+
const file = 'file-1';
|
|
116
|
+
let fs1 = dev1FS();
|
|
117
|
+
let fs2 = dev2FS();
|
|
118
|
+
// case 1: remote version of folder
|
|
119
|
+
const folderEventAt2 = (0, test_utils_1.observeFolderForOneEvent)(fs2);
|
|
120
|
+
await fs1.writeTxtFile(file, (0, random_node_1.stringOfB64CharsSync)(100));
|
|
121
|
+
await fs1.v.sync.upload(file);
|
|
122
|
+
await fs1.v.sync.upload('');
|
|
123
|
+
const folderEv = await folderEventAt2;
|
|
124
|
+
expect(folderEv.type).toBe('remote-change');
|
|
125
|
+
expect((0, json_equal_1.deepEqual)(folderEv.syncStatus, await fs2.v.sync.status(''))).toBeTrue();
|
|
126
|
+
expect(await fs2.v.sync.isRemoteVersionOnDisk('', folderEv.newRemoteVersion)).toBe('none');
|
|
127
|
+
let startedDownload = await fs2.v.sync.startDownload('', folderEv.newRemoteVersion);
|
|
128
|
+
expect(startedDownload)
|
|
129
|
+
.withContext(`long download is not started, cause small entity is completely downloaded in the first request`)
|
|
130
|
+
.toBeUndefined();
|
|
131
|
+
expect(await fs2.v.sync.isRemoteVersionOnDisk('', folderEv.newRemoteVersion))
|
|
132
|
+
.withContext(`expect to see entity completely on a disk, when download has started, as not being needed`)
|
|
133
|
+
.toBe('complete');
|
|
134
|
+
expect((await fs2.v.listFolder('', { remoteVersion: folderEv.newRemoteVersion })).lst.map(({ name }) => name)).toContain(file);
|
|
135
|
+
// case 2: remote version of file
|
|
136
|
+
expect(await fs2.checkFilePresence(file)).toBeFalse();
|
|
137
|
+
await fs2.v.sync.adoptRemote('');
|
|
138
|
+
expect(await fs2.readTxtFile(file)).toBe(await fs1.readTxtFile(file));
|
|
139
|
+
let fileEventAt2 = (0, test_utils_1.observeFileForOneEvent)(fs2, file);
|
|
140
|
+
await fs1.writeBytes(file, await (0, random_node_1.bytes)(88000000));
|
|
141
|
+
await fs1.v.sync.upload(file);
|
|
142
|
+
let fileEv = await fileEventAt2;
|
|
143
|
+
expect(fileEv.type).toBe('remote-change');
|
|
144
|
+
expect((0, json_equal_1.deepEqual)(fileEv.syncStatus, await fs2.v.sync.status(file))).toBeTrue();
|
|
145
|
+
expect(await fs2.v.sync.isRemoteVersionOnDisk(file, fileEv.newRemoteVersion)).toBe('none');
|
|
146
|
+
let events = collectFileDownloadEvents(fs2, file);
|
|
147
|
+
startedDownload = await fs2.v.sync.startDownload(file, fileEv.newRemoteVersion);
|
|
148
|
+
expect(await fs2.v.sync.isRemoteVersionOnDisk(file, fileEv.newRemoteVersion)).toBe('partial');
|
|
149
|
+
expect(startedDownload).toBeDefined();
|
|
150
|
+
expect((await events.startEvent).downloadTaskId).toBe(startedDownload.downloadTaskId);
|
|
151
|
+
expect((await events.doneEvent).downloadTaskId).toBe(startedDownload.downloadTaskId);
|
|
152
|
+
expect(events.progressEvents.length).toBeGreaterThan(0);
|
|
153
|
+
expect(await fs2.v.sync.isRemoteVersionOnDisk(file, fileEv.newRemoteVersion)).toBe('complete');
|
|
154
|
+
expect((0, bytes_equal_1.bytesEqual)((await fs2.v.readBytes(file, undefined, undefined, { remoteVersion: fileEv.newRemoteVersion })).bytes, (await fs1.readBytes(file)))).toBeTrue();
|
|
155
|
+
// case 3: reading remote version of file without adopting it and without explicit download
|
|
156
|
+
fileEventAt2 = (0, test_utils_1.observeFileForOneEvent)(fs2, file);
|
|
157
|
+
await fs1.writeBytes(file, await (0, random_node_1.bytes)(770000));
|
|
158
|
+
await fs1.v.sync.upload(file);
|
|
159
|
+
fileEv = await fileEventAt2;
|
|
160
|
+
expect(await fs2.v.sync.isRemoteVersionOnDisk(file, fileEv.newRemoteVersion)).toBe('none');
|
|
161
|
+
expect((0, bytes_equal_1.bytesEqual)((await fs2.v.readBytes(file, undefined, undefined, { remoteVersion: fileEv.newRemoteVersion })).bytes, (await fs1.readBytes(file)))).toBeTrue();
|
|
162
|
+
expect(await fs2.v.sync.isRemoteVersionOnDisk(file, fileEv.newRemoteVersion)).toBe('complete');
|
|
163
|
+
};
|
|
164
|
+
it.timeout = 30000;
|
|
165
|
+
exports.specs.its.push(it);
|
|
166
|
+
it = {
|
|
167
|
+
expectation: 'file incrementally written and uploaded, then read on a second device'
|
|
168
|
+
};
|
|
169
|
+
it.func = async function ({ dev1FS, dev2FS }) {
|
|
170
|
+
const file = 'incremental-file';
|
|
171
|
+
let fs1 = dev1FS();
|
|
172
|
+
let fs2 = dev2FS();
|
|
173
|
+
const COMMA_BYTE = buffer_utils_1.utf8.pack(',');
|
|
174
|
+
const SQ_BRACKET_BYTE = buffer_utils_1.utf8.pack(']');
|
|
175
|
+
const completeContent = [
|
|
176
|
+
{
|
|
177
|
+
type: "addition",
|
|
178
|
+
record: {
|
|
179
|
+
msgId: "F-vo1A4zInEBGq994-e3KvrE9a8QXLcr",
|
|
180
|
+
msgType: "mail",
|
|
181
|
+
deliveryTS: 1664923830399,
|
|
182
|
+
key: "0Uz7wGW4P7hdik7fuStC+avi7iXk7AZ5B1KxJCxIOQk=",
|
|
183
|
+
keyStatus: "published_intro",
|
|
184
|
+
mainObjHeaderOfs: 72,
|
|
185
|
+
removeAfter: 0
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
type: "addition",
|
|
190
|
+
record: {
|
|
191
|
+
msgId: "JYXfEEcg3-iX7UDjG3BU0Jha9DYX_Jt-",
|
|
192
|
+
msgType: "mail",
|
|
193
|
+
deliveryTS: 1664923830658,
|
|
194
|
+
key: "gjArW3Mr3ACnoFds/wKFZJm+pp9De1TlNhWIkpyd4Ow=",
|
|
195
|
+
keyStatus: "published_intro",
|
|
196
|
+
mainObjHeaderOfs: 72,
|
|
197
|
+
removeAfter: 0
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
];
|
|
201
|
+
// we do this file writing and uploading, cause this pattern hit an error,
|
|
202
|
+
// hence, we add this test with this seemingly out of the blue setup
|
|
203
|
+
await fs1.writeTxtFile(file, `[]`);
|
|
204
|
+
await fs1.v.sync.upload(file);
|
|
205
|
+
await fs1.v.sync.upload('');
|
|
206
|
+
let sink = await fs1.getByteSink(file, { truncate: false });
|
|
207
|
+
await sink.splice(1, 1);
|
|
208
|
+
let bytes = buffer_utils_1.utf8.pack(JSON.stringify(completeContent[0]));
|
|
209
|
+
await sink.splice(1, 0, bytes);
|
|
210
|
+
await sink.splice(1 + bytes.length, 0, SQ_BRACKET_BYTE);
|
|
211
|
+
await sink.done();
|
|
212
|
+
await fs1.v.sync.upload(file);
|
|
213
|
+
sink = await fs1.getByteSink(file, { truncate: false });
|
|
214
|
+
const len = await sink.getSize();
|
|
215
|
+
await sink.splice(len - 1, 1, COMMA_BYTE);
|
|
216
|
+
bytes = buffer_utils_1.utf8.pack(JSON.stringify(completeContent[1]));
|
|
217
|
+
await sink.splice(len, 0, bytes);
|
|
218
|
+
await sink.splice(len + bytes.length, 0, SQ_BRACKET_BYTE);
|
|
219
|
+
await sink.done();
|
|
220
|
+
expect((0, json_equal_1.deepEqual)(completeContent, await fs1.readJSONFile(file))).toBeTrue();
|
|
221
|
+
await fs1.v.sync.upload(file);
|
|
222
|
+
// and on the second device
|
|
223
|
+
expect((await fs2.v.sync.status('')).state).toBe('behind');
|
|
224
|
+
await fs2.v.sync.adoptRemote('');
|
|
225
|
+
expect((await fs2.v.sync.status(file)).state).toBe('synced');
|
|
226
|
+
expect((0, json_equal_1.deepEqual)(completeContent, await fs2.readJSONFile(file))).toBeTrue();
|
|
227
|
+
};
|
|
228
|
+
exports.specs.its.push(it);
|
|
229
|
+
Object.freeze(exports);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { MultiUserSetup } from "../../libs-for-tests/setups";
|
|
2
|
+
import { SpecIt as GenericSpecIt } from "../../libs-for-tests/spec-module";
|
|
3
|
+
type WritableFS = web3n.files.WritableFS;
|
|
4
|
+
type ReadonlyFS = web3n.files.ReadonlyFS;
|
|
5
|
+
export interface SetupWithTestFS {
|
|
6
|
+
isUp: boolean;
|
|
7
|
+
testFS: web3n.files.WritableFS;
|
|
8
|
+
}
|
|
9
|
+
export type SpecIt = GenericSpecIt<SetupWithTestFS>;
|
|
10
|
+
export declare function clearFS(fs: WritableFS): Promise<void>;
|
|
11
|
+
export interface SetupWithTwoFSs {
|
|
12
|
+
isUp: boolean;
|
|
13
|
+
syncedTestFS: WritableFS;
|
|
14
|
+
localTestFS: WritableFS;
|
|
15
|
+
}
|
|
16
|
+
export type SpecItWithTwoFSs = GenericSpecIt<SetupWithTwoFSs>;
|
|
17
|
+
export interface SetupWithTwoDevsFSs {
|
|
18
|
+
isUp: boolean;
|
|
19
|
+
dev1FS: () => WritableFS;
|
|
20
|
+
dev2: {
|
|
21
|
+
stop(): Promise<void>;
|
|
22
|
+
start(): Promise<void>;
|
|
23
|
+
};
|
|
24
|
+
dev2FS: () => WritableFS;
|
|
25
|
+
resetFS: () => Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
export type SpecItWithTwoDevsFSs = GenericSpecIt<SetupWithTwoDevsFSs>;
|
|
28
|
+
export declare function makeSetupWithTwoDevsFSs(testFolder: string): {
|
|
29
|
+
fsSetup: SetupWithTwoDevsFSs;
|
|
30
|
+
setupDevsAndFSs: (baseSetup: MultiUserSetup) => Promise<void>;
|
|
31
|
+
};
|
|
32
|
+
export declare function observeFolderForOneEvent<T>(fs: ReadonlyFS, path?: string): Promise<T>;
|
|
33
|
+
export declare function observeFileForOneEvent<T>(fs: ReadonlyFS, path: string): Promise<T>;
|
|
34
|
+
export {};
|