diff --git a/CliClient/tests/synchronizer.js b/CliClient/tests/synchronizer.js index 628c2e7a0..3d06fbf32 100644 --- a/CliClient/tests/synchronizer.js +++ b/CliClient/tests/synchronizer.js @@ -304,7 +304,7 @@ describe('Synchronizer', function() { expect(items.length).toBe(1); let deletedItems = await BaseItem.deletedItems(syncTargetId()); expect(deletedItems.length).toBe(0); - })); + })); it('should delete remote folder', asyncTest(async () => { let folder1 = await Folder.save({ title: "folder1" }); @@ -322,8 +322,8 @@ describe('Synchronizer', function() { await synchronizer().start(); let all = await allItems(); - localItemsSameAsRemote(all, expect); - })); + await localItemsSameAsRemote(all, expect); + })); it('should delete local folder', asyncTest(async () => { let folder1 = await Folder.save({ title: "folder1" }); @@ -345,8 +345,8 @@ describe('Synchronizer', function() { await synchronizer().start(); let items = await allItems(); - localItemsSameAsRemote(items, expect); - })); + await localItemsSameAsRemote(items, expect); + })); it('should resolve conflict if remote folder has been deleted, but note has been added to folder locally', asyncTest(async () => { let folder1 = await Folder.save({ title: "folder1" }); @@ -388,8 +388,8 @@ describe('Synchronizer', function() { expect(items.length).toBe(1); expect(items[0].title).toBe('folder'); - localItemsSameAsRemote(items, expect); - })); + await localItemsSameAsRemote(items, expect); + })); it('should cross delete all folders', asyncTest(async () => { // If client1 and 2 have two folders, client 1 deletes item 1 and client diff --git a/CliClient/tests/test-utils.js b/CliClient/tests/test-utils.js index 0b915a0e8..430bd2d40 100644 --- a/CliClient/tests/test-utils.js +++ b/CliClient/tests/test-utils.js @@ -15,6 +15,7 @@ const { Synchronizer } = require('lib/synchronizer.js'); const { FileApi } = require('lib/file-api.js'); const { FileApiDriverMemory } = require('lib/file-api-driver-memory.js'); const { FileApiDriverLocal } = require('lib/file-api-driver-local.js'); +const { FileApiDriverWebDav } = require('lib/file-api-driver-webdav.js'); const { FsDriverNode } = require('lib/fs-driver-node.js'); const { time } = require('lib/time-utils.js'); const { shimInit } = require('lib/shim-init-node.js'); @@ -22,8 +23,10 @@ const SyncTargetRegistry = require('lib/SyncTargetRegistry.js'); const SyncTargetMemory = require('lib/SyncTargetMemory.js'); const SyncTargetFilesystem = require('lib/SyncTargetFilesystem.js'); const SyncTargetOneDrive = require('lib/SyncTargetOneDrive.js'); +const SyncTargetNextcloud = require('lib/SyncTargetNextcloud.js'); const EncryptionService = require('lib/services/EncryptionService.js'); const DecryptionWorker = require('lib/services/DecryptionWorker.js'); +const WebDavApi = require('lib/WebDavApi'); let databases_ = []; let synchronizers_ = []; @@ -46,13 +49,17 @@ fs.mkdirpSync(logDir, 0o755); SyncTargetRegistry.addClass(SyncTargetMemory); SyncTargetRegistry.addClass(SyncTargetFilesystem); SyncTargetRegistry.addClass(SyncTargetOneDrive); +SyncTargetRegistry.addClass(SyncTargetNextcloud); -//const syncTargetId_ = SyncTargetRegistry.nameToId('memory'); -const syncTargetId_ = SyncTargetRegistry.nameToId('filesystem'); +//const syncTargetId_ = SyncTargetRegistry.nameToId('nextcloud'); +const syncTargetId_ = SyncTargetRegistry.nameToId('memory'); +//const syncTargetId_ = SyncTargetRegistry.nameToId('filesystem'); const syncDir = __dirname + '/../tests/sync'; const sleepTime = syncTargetId_ == SyncTargetRegistry.nameToId('filesystem') ? 1001 : 400; +console.info('Testing with sync target: ' + SyncTargetRegistry.idToName(syncTargetId_)); + const logger = new Logger(); logger.addTarget('console'); logger.addTarget('file', { path: logDir + '/log.txt' }); @@ -174,12 +181,14 @@ async function setupDatabaseAndSynchronizer(id = null) { decryptionWorkers_[id] = new DecryptionWorker(); decryptionWorkers_[id].setEncryptionService(encryptionServices_[id]); - if (syncTargetId_ == SyncTargetRegistry.nameToId('filesystem')) { - fs.removeSync(syncDir) - fs.mkdirpSync(syncDir, 0o755); - } else { - await fileApi().format(); - } + // if (syncTargetId_ == SyncTargetRegistry.nameToId('filesystem')) { + // fs.removeSync(syncDir) + // fs.mkdirpSync(syncDir, 0o755); + // } else { + await fileApi().clearRoot(); + + //await fileApi().format(); + //} } function db(id = null) { @@ -230,7 +239,17 @@ function fileApi() { fileApi_ = new FileApi(syncDir, new FileApiDriverLocal()); } else if (syncTargetId_ == SyncTargetRegistry.nameToId('memory')) { fileApi_ = new FileApi('/root', new FileApiDriverMemory()); + } else if (syncTargetId_ == SyncTargetRegistry.nameToId('nextcloud')) { + const options = { + baseUrl: () => 'http://nextcloud.local/remote.php/dav/files/admin/JoplinTest', + username: () => 'admin', + password: () => '123456', + }; + + const api = new WebDavApi(options); + fileApi_ = new FileApi('', new FileApiDriverWebDav(api)); } + // } else if (syncTargetId == Setting.SYNC_TARGET_ONEDRIVE) { // let auth = require('./onedrive-auth.json'); // if (!auth) { diff --git a/ReactNativeClient/lib/SyncTargetRegistry.js b/ReactNativeClient/lib/SyncTargetRegistry.js index a9c090e0b..c03d3b705 100644 --- a/ReactNativeClient/lib/SyncTargetRegistry.js +++ b/ReactNativeClient/lib/SyncTargetRegistry.js @@ -31,6 +31,10 @@ class SyncTargetRegistry { throw new Error('ID not found: ' + id); } + static idToName(id) { + return this.idToMetadata(id).name; + } + static idAndLabelPlainObject() { let output = {}; for (let n in this.reg_) { diff --git a/ReactNativeClient/lib/WebDavApi.js b/ReactNativeClient/lib/WebDavApi.js index cde391f7d..d8f53cb07 100644 --- a/ReactNativeClient/lib/WebDavApi.js +++ b/ReactNativeClient/lib/WebDavApi.js @@ -195,7 +195,7 @@ class WebDavApi { if (json['d:error']) { const code = json['d:error']['s:exception'] ? json['d:error']['s:exception'].join(' ') : response.status; const message = json['d:error']['s:message'] ? json['d:error']['s:message'].join("\n") : shortResponseText(); - throw new JoplinError(message + ' (' + code + ')', response.status); + throw new JoplinError(method + ' ' + path + ': ' + message + ' (' + code + ')', response.status); } throw new JoplinError(shortResponseText(), response.status); diff --git a/ReactNativeClient/lib/file-api-driver-local.js b/ReactNativeClient/lib/file-api-driver-local.js index 7d664cc4b..ae5f3c26c 100644 --- a/ReactNativeClient/lib/file-api-driver-local.js +++ b/ReactNativeClient/lib/file-api-driver-local.js @@ -224,6 +224,11 @@ class FileApiDriverLocal { throw new Error('Not supported'); } + async clearRoot(baseDir) { + await this.fsDriver().remove(baseDir); + await this.fsDriver().mkdir(baseDir); + } + } module.exports = { FileApiDriverLocal }; \ No newline at end of file diff --git a/ReactNativeClient/lib/file-api-driver-memory.js b/ReactNativeClient/lib/file-api-driver-memory.js index 29ac3fdb3..17a0b5f39 100644 --- a/ReactNativeClient/lib/file-api-driver-memory.js +++ b/ReactNativeClient/lib/file-api-driver-memory.js @@ -159,6 +159,11 @@ class FileApiDriverMemory { return output; } + clearRoot() { + this.items_ = []; + return Promise.resolve(); + } + } module.exports = { FileApiDriverMemory }; \ No newline at end of file diff --git a/ReactNativeClient/lib/file-api-driver-onedrive.js b/ReactNativeClient/lib/file-api-driver-onedrive.js index 955c7e4de..c72827e6b 100644 --- a/ReactNativeClient/lib/file-api-driver-onedrive.js +++ b/ReactNativeClient/lib/file-api-driver-onedrive.js @@ -189,6 +189,10 @@ class FileApiDriverOneDrive { return this.pathCache_[path]; } + clearRoot() { + throw new Error('Not implemented'); + } + async delta(path, options = null) { let output = { hasMore: false, diff --git a/ReactNativeClient/lib/file-api-driver-webdav.js b/ReactNativeClient/lib/file-api-driver-webdav.js index b7aa370ec..63f607d53 100644 --- a/ReactNativeClient/lib/file-api-driver-webdav.js +++ b/ReactNativeClient/lib/file-api-driver-webdav.js @@ -66,20 +66,26 @@ class FileApiDriverWebDav { async delta(path, options) { const getDirStats = async (path) => { - const result = await this.api().execPropFind(path, 1, [ - 'd:getlastmodified', - 'd:resourcetype', - ]); - - const resources = this.api().arrayFromJson(result, ['d:multistatus', 'd:response']); - return this.statsFromResources_(resources); + const result = await this.list(path); + return result.items; }; return await basicDelta(path, getDirStats, options); } async list(path, options) { - throw new Error('Not implemented'); // Not needed + const result = await this.api().execPropFind(path, 1, [ + 'd:getlastmodified', + 'd:resourcetype', + ]); + + const resources = this.api().arrayFromJson(result, ['d:multistatus', 'd:response']); + + return { + items: this.statsFromResources_(resources), + hasMore: false, + context: null, + }; } async get(path, options) { @@ -120,6 +126,11 @@ class FileApiDriverWebDav { throw new Error('Not supported'); } + async clearRoot() { + await this.delete(''); + await this.mkdir(''); + } + } module.exports = { FileApiDriverWebDav }; \ No newline at end of file diff --git a/ReactNativeClient/lib/file-api.js b/ReactNativeClient/lib/file-api.js index 71a001473..e4e349980 100644 --- a/ReactNativeClient/lib/file-api.js +++ b/ReactNativeClient/lib/file-api.js @@ -64,6 +64,7 @@ class FileApi { }); } + // Deprectated setTimestamp(path, timestampMs) { this.logger().debug('setTimestamp ' + this.fullPath_(path)); return this.driver_.setTimestamp(this.fullPath_(path), timestampMs); @@ -105,15 +106,21 @@ class FileApi { return this.driver_.delete(this.fullPath_(path)); } + // Deprectated move(oldPath, newPath) { this.logger().debug('move ' + this.fullPath_(oldPath) + ' => ' + this.fullPath_(newPath)); return this.driver_.move(this.fullPath_(oldPath), this.fullPath_(newPath)); } + // Deprectated format() { return this.driver_.format(); } + clearRoot() { + return this.driver_.clearRoot(this.baseDir_); + } + delta(path, options = null) { this.logger().debug('delta ' + this.fullPath_(path)); return this.driver_.delta(this.fullPath_(path), options); diff --git a/ReactNativeClient/lib/fs-driver-node.js b/ReactNativeClient/lib/fs-driver-node.js index 0f211b1c4..5034f5700 100644 --- a/ReactNativeClient/lib/fs-driver-node.js +++ b/ReactNativeClient/lib/fs-driver-node.js @@ -20,6 +20,11 @@ class FsDriverNode { return fs.writeFile(path, string, { encoding: encoding }); } + // same as rm -rf + async remove(path) { + return fs.remove(path); + } + async move(source, dest) { let lastError = null; diff --git a/ReactNativeClient/lib/fs-driver-rn.js b/ReactNativeClient/lib/fs-driver-rn.js index 142d1f7aa..e987d9f5f 100644 --- a/ReactNativeClient/lib/fs-driver-rn.js +++ b/ReactNativeClient/lib/fs-driver-rn.js @@ -14,6 +14,11 @@ class FsDriverRN { return RNFS.writeFile(path, string, encoding); } + // same as rm -rf + async remove(path) { + throw new Error('Not implemented'); + } + writeBinaryFile(path, content) { throw new Error('Not implemented'); }