1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Fixed tags and added unit tests

This commit is contained in:
Laurent Cozic 2017-07-03 18:29:19 +00:00
parent ab312d70f5
commit dde3ea2008
6 changed files with 69 additions and 24 deletions

View File

@ -5,7 +5,7 @@ rm -f "$CLIENT_DIR/tests-build/lib"
mkdir -p "$CLIENT_DIR/tests-build/data" mkdir -p "$CLIENT_DIR/tests-build/data"
ln -s "$CLIENT_DIR/build/lib" "$CLIENT_DIR/tests-build" ln -s "$CLIENT_DIR/build/lib" "$CLIENT_DIR/tests-build"
npm run build && NODE_PATH="$CLIENT_DIR/tests-build/" npm test tests-build/synchronizer.js tests-build/base-model.js npm run build && NODE_PATH="$CLIENT_DIR/tests-build/" npm test tests-build/synchronizer.js
#npm run build && NODE_PATH="$CLIENT_DIR/tests-build/" npm test tests-build/synchronizer.js #npm run build && NODE_PATH="$CLIENT_DIR/tests-build/" npm test tests-build/synchronizer.js
#npm run build && NODE_PATH="$CLIENT_DIR/tests-build/" npm test tests-build/base-model.js #npm run build && NODE_PATH="$CLIENT_DIR/tests-build/" npm test tests-build/base-model.js

View File

@ -3,6 +3,7 @@ import { setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi,
import { createFoldersAndNotes } from 'test-data.js'; import { createFoldersAndNotes } from 'test-data.js';
import { Folder } from 'lib/models/folder.js'; import { Folder } from 'lib/models/folder.js';
import { Note } from 'lib/models/note.js'; import { Note } from 'lib/models/note.js';
import { Tag } from 'lib/models/tag.js';
import { Setting } from 'lib/models/setting.js'; import { Setting } from 'lib/models/setting.js';
import { BaseItem } from 'lib/models/base-item.js'; import { BaseItem } from 'lib/models/base-item.js';
import { BaseModel } from 'lib/base-model.js'; import { BaseModel } from 'lib/base-model.js';
@ -461,4 +462,43 @@ describe('Synchronizer', function() {
done(); done();
}); });
it('should sync tags', async (done) => {
let f1 = await Folder.save({ title: "folder" });
let n1 = await Note.save({ title: "mynote" });
let n2 = await Note.save({ title: "mynote2" });
let tag = await Tag.save({ title: 'mytag' });
await synchronizer().start();
await switchClient(2);
await synchronizer().start();
let remoteTag = await Tag.loadByTitle(tag.title);
expect(!!remoteTag).toBe(true);
expect(remoteTag.id).toBe(tag.id);
await Tag.addNote(remoteTag.id, n1.id);
await Tag.addNote(remoteTag.id, n2.id);
let noteIds = await Tag.tagNoteIds(tag.id);
expect(noteIds.length).toBe(2);
await synchronizer().start();
await switchClient(1);
await synchronizer().start();
let remoteNoteIds = await Tag.tagNoteIds(tag.id);
expect(remoteNoteIds.length).toBe(2);
Tag.removeNote(tag.id, n1.id);
remoteNoteIds = await Tag.tagNoteIds(tag.id);
expect(remoteNoteIds.length).toBe(1);
await synchronizer().start();
await switchClient(2);
await synchronizer().start();
noteIds = await Tag.tagNoteIds(tag.id);
expect(noteIds.length).toBe(1);
expect(remoteNoteIds[0]).toBe(noteIds[0]);
done();
});
}); });

View File

@ -10,6 +10,7 @@ import { BaseItem } from 'lib/models/base-item.js';
import { Synchronizer } from 'lib/synchronizer.js'; import { Synchronizer } from 'lib/synchronizer.js';
import { FileApi } from 'lib/file-api.js'; import { FileApi } from 'lib/file-api.js';
import { FileApiDriverMemory } from 'lib/file-api-driver-memory.js'; import { FileApiDriverMemory } from 'lib/file-api-driver-memory.js';
import { time } from 'lib/time-utils.js';
let databases_ = []; let databases_ = [];
let synchronizers_ = []; let synchronizers_ = [];
@ -29,8 +30,9 @@ function sleep(n) {
}); });
} }
function switchClient(id) { async function switchClient(id) {
Setting.saveAll(); await time.msleep(200);
await Setting.saveAll();
currentClient_ = id; currentClient_ = id;
BaseModel.db_ = databases_[id]; BaseModel.db_ = databases_[id];

View File

@ -15,6 +15,7 @@
"CliClient/app/src", "CliClient/app/src",
"CliClient/app/lib", "CliClient/app/lib",
"CliClient/tests/src", "CliClient/tests/src",
"CliClient/tests/fuzzing",
"ReactNativeClient/node_modules", "ReactNativeClient/node_modules",
"ReactNativeClient/android/app/build", "ReactNativeClient/android/app/build",
"ReactNativeClient/android/build", "ReactNativeClient/android/build",

View File

@ -1,6 +1,7 @@
import { BaseModel } from 'lib/base-model.js'; import { BaseModel } from 'lib/base-model.js';
import { Database } from 'lib/database.js'; import { Database } from 'lib/database.js';
import { BaseItem } from 'lib/models/base-item.js'; import { BaseItem } from 'lib/models/base-item.js';
import { time } from 'lib/time-utils.js';
import lodash from 'lodash'; import lodash from 'lodash';
class Tag extends BaseItem { class Tag extends BaseItem {
@ -18,7 +19,6 @@ class Tag extends BaseItem {
fieldNames.push('type_'); fieldNames.push('type_');
fieldNames.push(async () => { fieldNames.push(async () => {
let noteIds = await this.tagNoteIds(item.id); let noteIds = await this.tagNoteIds(item.id);
console.info('NOTE IDS', noteIds);
return { return {
key: 'notes_', key: 'notes_',
value: noteIds.join(','), value: noteIds.join(','),
@ -37,10 +37,6 @@ class Tag extends BaseItem {
return output; return output;
} }
// TODO: in order for a sync to happen, the updated_time property should somehow be changed
// whenever an tag is applied or removed from an item. Either the updated_time property
// is changed here or by the caller?
static async addNote(tagId, noteId) { static async addNote(tagId, noteId) {
let hasIt = await this.hasNote(tagId, noteId); let hasIt = await this.hasNote(tagId, noteId);
if (hasIt) return; if (hasIt) return;
@ -51,18 +47,17 @@ class Tag extends BaseItem {
}); });
await this.db().exec(query); await this.db().exec(query);
//await this.save({ id: tagId, updated_time: time.unixMs() }); //type_: BaseModel.MODEL_TYPE_TAG await this.save({ id: tagId, updated_time: time.unixMs() });
} }
static async addNotes(tagId, noteIds) { static async removeNote(tagId, noteId) {
for (let i = 0; i < noteIds.length; i++) { await this.db().exec('DELETE FROM note_tags WHERE tag_id = ? AND note_id = ?', [tagId, noteId]);
await this.addNote(tagId, noteIds[i]); await this.save({ id: tagId, updated_time: time.unixMs() });
}
} }
// Note: updated_time must not change since this is only called from // Note: updated_time must not change here since this is only called from
// the synchronizer, which manages and sets the correct updated_time // save(), which already handles how the updated_time property is set.
static async setAssociatedNotes(tagId, noteIds) { static async setAssociatedNotes_(tagId, noteIds) {
let queries = [{ let queries = [{
sql: 'DELETE FROM note_tags WHERE tag_id = ?', sql: 'DELETE FROM note_tags WHERE tag_id = ?',
params: [tagId], params: [tagId],
@ -80,8 +75,17 @@ class Tag extends BaseItem {
return !!r; return !!r;
} }
static removeNote(tagId, noteId) { static async save(o, options = null) {
return this.db().exec('DELETE FROM note_tags WHERE tag_id = ? AND note_id = ?', [tagId, noteId]); let result = await super.save(o, options);
if (options && options.applyMetadataChanges === true) {
if (o.notes_) {
let noteIds = o.notes_.split(',');
await this.setAssociatedNotes_(o.id, noteIds);
}
}
return result;
} }
} }

View File

@ -309,7 +309,10 @@ class Synchronizer {
let newContent = Object.assign({}, content); let newContent = Object.assign({}, content);
newContent.sync_time = time.unixMs(); newContent.sync_time = time.unixMs();
let options = { autoTimestamp: false }; let options = {
autoTimestamp: false,
applyMetadataChanges: true,
};
if (action == 'createLocal') options.isNew = true; if (action == 'createLocal') options.isNew = true;
if (newContent.type_ == BaseModel.MODEL_TYPE_RESOURCE && action == 'createLocal') { if (newContent.type_ == BaseModel.MODEL_TYPE_RESOURCE && action == 'createLocal') {
@ -321,11 +324,6 @@ class Synchronizer {
await ItemClass.save(newContent, options); await ItemClass.save(newContent, options);
if (newContent.type_ == BaseModel.MODEL_TYPE_TAG) {
let noteIds = newContent.notes_.split(',');
await ItemClass.setAssociatedNotes(newContent.id, noteIds);
}
this.logSyncOperation(action, local, content, reason); this.logSyncOperation(action, local, content, reason);
} else { } else {
this.logSyncOperation(action, local, remote, reason); this.logSyncOperation(action, local, remote, reason);