mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-22 22:49:45 +01:00
test: improve overall test coverage
This commit is contained in:
@@ -1,421 +0,0 @@
|
||||
import Database from "../../index";
|
||||
import { NodeStorageInterface } from "../../../__mocks__/node-storage.mock";
|
||||
import FS from "../../../__mocks__/fs.mock";
|
||||
import { CHECK_IDS, EV, EVENTS } from "../../../common";
|
||||
import EventSource from "eventsource";
|
||||
import { delay } from "../../../__tests__/utils";
|
||||
|
||||
test.skip(
|
||||
"case 1: device A & B should only download the changes from device C (no uploading)",
|
||||
async () => {
|
||||
const types = [];
|
||||
function onSyncProgress({ type }) {
|
||||
types.push(type);
|
||||
}
|
||||
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
deviceA.eventManager.subscribe(EVENTS.syncProgress, onSyncProgress);
|
||||
deviceB.eventManager.subscribe(EVENTS.syncProgress, onSyncProgress);
|
||||
|
||||
const deviceC = await initializeDevice("deviceC");
|
||||
|
||||
await deviceC.notes.add({ title: "new note 1" });
|
||||
await syncAndWait(deviceC, deviceC);
|
||||
|
||||
expect(types.every((t) => t === "download")).toBe(true);
|
||||
|
||||
await cleanup(deviceA, deviceB, deviceC);
|
||||
},
|
||||
600 * 1000
|
||||
);
|
||||
|
||||
test.skip(
|
||||
"case 3: Device A & B have unsynced changes but server has nothing",
|
||||
async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
const note1Id = await deviceA.notes.add({
|
||||
title: "Test note from device A",
|
||||
});
|
||||
const note2Id = await deviceB.notes.add({
|
||||
title: "Test note from device B",
|
||||
});
|
||||
|
||||
await syncAndWait(deviceA, deviceB);
|
||||
|
||||
expect(deviceA.notes.note(note2Id)).toBeTruthy();
|
||||
expect(deviceB.notes.note(note1Id)).toBeTruthy();
|
||||
expect(deviceA.notes.note(note1Id)).toBeTruthy();
|
||||
expect(deviceB.notes.note(note2Id)).toBeTruthy();
|
||||
|
||||
await cleanup(deviceA, deviceA);
|
||||
},
|
||||
30 * 1000
|
||||
);
|
||||
|
||||
// test.skip(
|
||||
// "case 4: Device A's sync is interrupted halfway and Device B makes some changes afterwards and syncs.",
|
||||
// async () => {
|
||||
// const deviceA = await initializeDevice("deviceA");
|
||||
// const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
// const unsyncedNoteIds = [];
|
||||
// for (let i = 0; i < 10; ++i) {
|
||||
// const id = await deviceA.notes.add({
|
||||
// title: `Test note ${i} from device A`,
|
||||
// });
|
||||
// unsyncedNoteIds.push(id);
|
||||
// }
|
||||
|
||||
// const half = unsyncedNoteIds.length / 2 + 1;
|
||||
// deviceA.eventManager.subscribe(
|
||||
// EVENTS.syncProgress,
|
||||
// async ({ type, current }) => {
|
||||
// if (type === "upload" && current === half) {
|
||||
// await deviceA.syncer.stop();
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
|
||||
// await expect(deviceA.sync(true)).rejects.toThrow();
|
||||
|
||||
// let syncedNoteIds = [];
|
||||
// for (let i = 0; i < unsyncedNoteIds.length; ++i) {
|
||||
// const expectedNoteId = unsyncedNoteIds[i];
|
||||
// if (deviceB.notes.note(expectedNoteId))
|
||||
// syncedNoteIds.push(expectedNoteId);
|
||||
// }
|
||||
// expect(
|
||||
// syncedNoteIds.length === half - 1 || syncedNoteIds.length === half
|
||||
// ).toBe(true);
|
||||
|
||||
// const deviceBNoteId = await deviceB.notes.add({
|
||||
// title: "Test note of case 4 from device B",
|
||||
// });
|
||||
|
||||
// await deviceB.sync(true);
|
||||
|
||||
// await syncAndWait(deviceA, deviceB);
|
||||
|
||||
// expect(deviceA.notes.note(deviceBNoteId)).toBeTruthy();
|
||||
// expect(
|
||||
// unsyncedNoteIds
|
||||
// .map((id) => !!deviceB.notes.note(id))
|
||||
// .every((res) => res === true)
|
||||
// ).toBe(true);
|
||||
|
||||
// await cleanup(deviceA, deviceB);
|
||||
// },
|
||||
// 60 * 1000
|
||||
// );
|
||||
|
||||
// test.only(
|
||||
// "case 5: Device A's sync is interrupted halfway and Device B makes changes on the same note's content that didn't get synced on Device A due to interruption.",
|
||||
// async () => {
|
||||
// const deviceA = await initializeDevice("deviceA");
|
||||
// const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
// const noteIds = [];
|
||||
// for (let i = 0; i < 10; ++i) {
|
||||
// const id = await deviceA.notes.add({
|
||||
// content: {
|
||||
// type: "tiptap",
|
||||
// data: `<p>deviceA=true</p>`,
|
||||
// },
|
||||
// });
|
||||
// noteIds.push(id);
|
||||
// }
|
||||
|
||||
// await deviceA.sync(true);
|
||||
// await deviceB.sync(true);
|
||||
|
||||
// const unsyncedNoteIds = [];
|
||||
// for (let id of noteIds) {
|
||||
// const noteId = await deviceA.notes.add({
|
||||
// id,
|
||||
// content: {
|
||||
// type: "tiptap",
|
||||
// data: `<p>deviceA=true+changed=true</p>`,
|
||||
// },
|
||||
// });
|
||||
// unsyncedNoteIds.push(noteId);
|
||||
// }
|
||||
|
||||
// deviceA.eventManager.subscribe(
|
||||
// EVENTS.syncProgress,
|
||||
// async ({ type, total, current }) => {
|
||||
// const half = total / 2 + 1;
|
||||
// if (type === "upload" && current === half) {
|
||||
// await deviceA.syncer.stop();
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
|
||||
// await expect(deviceA.sync(true)).rejects.toThrow();
|
||||
|
||||
// await delay(10 * 1000);
|
||||
|
||||
// for (let id of unsyncedNoteIds) {
|
||||
// await deviceB.notes.add({
|
||||
// id,
|
||||
// content: {
|
||||
// type: "tiptap",
|
||||
// data: "<p>changes from device B</p>",
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
|
||||
// const error = await withError(async () => {
|
||||
// await deviceB.sync(true);
|
||||
// await deviceA.sync(true);
|
||||
// });
|
||||
|
||||
// expect(error).not.toBeInstanceOf(NoErrorThrownError);
|
||||
// expect(error.message.includes("Merge")).toBeTruthy();
|
||||
|
||||
// await cleanup(deviceA, deviceB);
|
||||
// },
|
||||
// 60 * 1000
|
||||
// );
|
||||
|
||||
test.skip(
|
||||
"issue: running force sync from device A makes device B always download everything",
|
||||
async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
await syncAndWait(deviceA, deviceB, true);
|
||||
|
||||
const handler = jest.fn();
|
||||
deviceB.eventManager.subscribe(EVENTS.syncProgress, handler);
|
||||
|
||||
await deviceB.sync(true);
|
||||
|
||||
expect(handler).not.toHaveBeenCalled();
|
||||
|
||||
await cleanup(deviceB);
|
||||
},
|
||||
60 * 1000
|
||||
);
|
||||
|
||||
test.skip(
|
||||
"issue: colors are not properly created if multiple notes are synced together",
|
||||
async () => {
|
||||
const deviceA = await initializeDevice("deviceA", [CHECK_IDS.noteColor]);
|
||||
const deviceB = await initializeDevice("deviceB", [CHECK_IDS.noteColor]);
|
||||
|
||||
const noteIds = [];
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
const id = await deviceA.notes.add({
|
||||
content: {
|
||||
type: "tiptap",
|
||||
data: `<p>deviceA=true</p>`,
|
||||
},
|
||||
});
|
||||
noteIds.push(id);
|
||||
}
|
||||
|
||||
await syncAndWait(deviceA, deviceB);
|
||||
|
||||
for (let noteId of noteIds) {
|
||||
await deviceA.notes.note(noteId).color("purple");
|
||||
expect(deviceB.notes.note(noteId)).toBeTruthy();
|
||||
expect(deviceB.notes.note(noteId).data.color).toBeUndefined();
|
||||
}
|
||||
|
||||
await syncAndWait(deviceA, deviceB);
|
||||
|
||||
await delay(2000);
|
||||
|
||||
const purpleColor = deviceB.colors.tag("purple");
|
||||
console.log(noteIds, purpleColor.noteIds);
|
||||
expect(noteIds.every((id) => purpleColor.noteIds.indexOf(id) > -1)).toBe(
|
||||
true
|
||||
);
|
||||
|
||||
await cleanup(deviceA, deviceB);
|
||||
},
|
||||
60 * 1000
|
||||
);
|
||||
|
||||
test.skip(
|
||||
"issue: new topic on device A gets replaced by the new topic on device B",
|
||||
async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
const id = await deviceA.notebooks.add({ title: "Notebook 1" });
|
||||
|
||||
await syncAndWait(deviceA, deviceB, false);
|
||||
|
||||
expect(deviceB.notebooks.notebook(id)).toBeDefined();
|
||||
|
||||
await deviceA.notebooks.notebook(id).topics.add("Topic 1");
|
||||
|
||||
// to create a conflict
|
||||
await delay(1500);
|
||||
|
||||
await deviceB.notebooks.notebook(id).topics.add("Topic 2");
|
||||
|
||||
expect(deviceA.notebooks.notebook(id).topics.has("Topic 1")).toBeTruthy();
|
||||
|
||||
expect(deviceB.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
|
||||
|
||||
await syncAndWait(deviceA, deviceB, false);
|
||||
|
||||
await delay(1000);
|
||||
|
||||
await syncAndWait(deviceB, deviceB, false);
|
||||
|
||||
expect(deviceA.notebooks.notebook(id).topics.has("Topic 1")).toBeTruthy();
|
||||
expect(deviceB.notebooks.notebook(id).topics.has("Topic 1")).toBeTruthy();
|
||||
|
||||
expect(deviceA.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
|
||||
expect(deviceB.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
|
||||
|
||||
await cleanup(deviceA, deviceB);
|
||||
},
|
||||
60 * 1000
|
||||
);
|
||||
|
||||
test.skip(
|
||||
"issue: remove notebook reference from notes that are removed from topic during merge",
|
||||
async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
const id = await deviceA.notebooks.add({
|
||||
title: "Notebook 1",
|
||||
topics: ["Topic 1"],
|
||||
});
|
||||
|
||||
await syncAndWait(deviceA, deviceB, false);
|
||||
|
||||
expect(deviceB.notebooks.notebook(id)).toBeDefined();
|
||||
|
||||
const noteA = await deviceA.notes.add({ title: "Note 1" });
|
||||
await deviceA.notes.move({ id, topic: "Topic 1" }, noteA);
|
||||
|
||||
expect(
|
||||
deviceA.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
|
||||
await delay(2000);
|
||||
|
||||
const noteB = await deviceB.notes.add({ title: "Note 2" });
|
||||
await deviceB.notes.move({ id, topic: "Topic 1" }, noteB);
|
||||
|
||||
expect(
|
||||
deviceB.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
|
||||
await syncAndWait(deviceB, deviceA, false);
|
||||
|
||||
expect(
|
||||
deviceA.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
expect(
|
||||
deviceB.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
|
||||
expect(deviceA.notes.note(noteA).data.notebooks).toHaveLength(0);
|
||||
|
||||
await cleanup(deviceA, deviceB);
|
||||
},
|
||||
60 * 1000
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @returns {Promise<Database>}
|
||||
*/
|
||||
async function initializeDevice(id, capabilities = []) {
|
||||
EV.subscribe(EVENTS.userCheckStatus, async (type) => {
|
||||
return {
|
||||
type,
|
||||
result: capabilities.indexOf(type) > -1,
|
||||
};
|
||||
});
|
||||
|
||||
const device = new Database(new NodeStorageInterface(), EventSource, FS);
|
||||
device.host({
|
||||
API_HOST: "http://192.168.10.29:5264",
|
||||
AUTH_HOST: "http://192.168.10.29:8264",
|
||||
SSE_HOST: "http://192.168.10.29:7264",
|
||||
ISSUES_HOST: "http://192.168.10.29:2624",
|
||||
SUBSCRIPTIONS_HOST: "http://192.168.10.29:9264",
|
||||
});
|
||||
|
||||
await device.init(id);
|
||||
await device.user.login("enkaboot@gmail.com", process.env.PASSWORD);
|
||||
await device.user.resetUser(false);
|
||||
|
||||
device.eventManager.subscribe(
|
||||
EVENTS.databaseSyncRequested,
|
||||
async (full, force) => {
|
||||
await device.sync(full, force);
|
||||
}
|
||||
);
|
||||
|
||||
await syncAndWait(device, device);
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
async function cleanup(...devices) {
|
||||
for (let device of devices) {
|
||||
await device.user.logout();
|
||||
device.eventManager.unsubscribeAll();
|
||||
}
|
||||
EV.unsubscribeAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Database} device
|
||||
* @returns
|
||||
*/
|
||||
// function waitForSyncCompleted(device) {
|
||||
// return new Promise((resolve) =>
|
||||
// device.eventManager.subscribe(EVENTS.syncCompleted, () => {
|
||||
// resolve();
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Database} deviceA
|
||||
* @param {Database} deviceB
|
||||
* @returns
|
||||
*/
|
||||
function syncAndWait(deviceA, deviceB, force = false) {
|
||||
return new Promise((resolve) => {
|
||||
const ref = deviceB.eventManager.subscribe(EVENTS.syncCompleted, () => {
|
||||
ref.unsubscribe();
|
||||
resolve();
|
||||
});
|
||||
deviceA.sync(true, force);
|
||||
});
|
||||
}
|
||||
|
||||
class NoErrorThrownError extends Error {}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Function} call
|
||||
* @returns {Promise<Error>}
|
||||
*/
|
||||
async function withError(call) {
|
||||
try {
|
||||
await call();
|
||||
|
||||
throw new NoErrorThrownError();
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
375
packages/core/api/sync/__tests__/sync.test.skip.js
Normal file
375
packages/core/api/sync/__tests__/sync.test.skip.js
Normal file
@@ -0,0 +1,375 @@
|
||||
import Database from "../../index";
|
||||
import { NodeStorageInterface } from "../../../__mocks__/node-storage.mock";
|
||||
import FS from "../../../__mocks__/fs.mock";
|
||||
import { CHECK_IDS, EV, EVENTS } from "../../../common";
|
||||
import EventSource from "eventsource";
|
||||
import { delay } from "../../../__tests__/utils";
|
||||
|
||||
jest.setTimeout(100 * 1000);
|
||||
|
||||
test("case 1: device A & B should only download the changes from device C (no uploading)", async () => {
|
||||
const types = [];
|
||||
function onSyncProgress({ type }) {
|
||||
types.push(type);
|
||||
}
|
||||
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
deviceA.eventManager.subscribe(EVENTS.syncProgress, onSyncProgress);
|
||||
deviceB.eventManager.subscribe(EVENTS.syncProgress, onSyncProgress);
|
||||
|
||||
const deviceC = await initializeDevice("deviceC");
|
||||
|
||||
await deviceC.notes.add({ title: "new note 1" });
|
||||
await syncAndWait(deviceC, deviceC);
|
||||
|
||||
expect(types.every((t) => t === "download")).toBe(true);
|
||||
|
||||
await cleanup(deviceA, deviceB, deviceC);
|
||||
});
|
||||
|
||||
test("case 3: Device A & B have unsynced changes but server has nothing", async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
const note1Id = await deviceA.notes.add({
|
||||
title: "Test note from device A",
|
||||
});
|
||||
const note2Id = await deviceB.notes.add({
|
||||
title: "Test note from device B",
|
||||
});
|
||||
|
||||
await syncAndWait(deviceA, deviceB);
|
||||
|
||||
expect(deviceA.notes.note(note2Id)).toBeTruthy();
|
||||
expect(deviceB.notes.note(note1Id)).toBeTruthy();
|
||||
expect(deviceA.notes.note(note1Id)).toBeTruthy();
|
||||
expect(deviceB.notes.note(note2Id)).toBeTruthy();
|
||||
|
||||
await cleanup(deviceA, deviceA);
|
||||
});
|
||||
|
||||
// test(
|
||||
// "case 4: Device A's sync is interrupted halfway and Device B makes some changes afterwards and syncs.",
|
||||
// async () => {
|
||||
// const deviceA = await initializeDevice("deviceA");
|
||||
// const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
// const unsyncedNoteIds = [];
|
||||
// for (let i = 0; i < 10; ++i) {
|
||||
// const id = await deviceA.notes.add({
|
||||
// title: `Test note ${i} from device A`,
|
||||
// });
|
||||
// unsyncedNoteIds.push(id);
|
||||
// }
|
||||
|
||||
// const half = unsyncedNoteIds.length / 2 + 1;
|
||||
// deviceA.eventManager.subscribe(
|
||||
// EVENTS.syncProgress,
|
||||
// async ({ type, current }) => {
|
||||
// if (type === "upload" && current === half) {
|
||||
// await deviceA.syncer.stop();
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
|
||||
// await expect(deviceA.sync(true)).rejects.toThrow();
|
||||
|
||||
// let syncedNoteIds = [];
|
||||
// for (let i = 0; i < unsyncedNoteIds.length; ++i) {
|
||||
// const expectedNoteId = unsyncedNoteIds[i];
|
||||
// if (deviceB.notes.note(expectedNoteId))
|
||||
// syncedNoteIds.push(expectedNoteId);
|
||||
// }
|
||||
// expect(
|
||||
// syncedNoteIds.length === half - 1 || syncedNoteIds.length === half
|
||||
// ).toBe(true);
|
||||
|
||||
// const deviceBNoteId = await deviceB.notes.add({
|
||||
// title: "Test note of case 4 from device B",
|
||||
// });
|
||||
|
||||
// await deviceB.sync(true);
|
||||
|
||||
// await syncAndWait(deviceA, deviceB);
|
||||
|
||||
// expect(deviceA.notes.note(deviceBNoteId)).toBeTruthy();
|
||||
// expect(
|
||||
// unsyncedNoteIds
|
||||
// .map((id) => !!deviceB.notes.note(id))
|
||||
// .every((res) => res === true)
|
||||
// ).toBe(true);
|
||||
|
||||
// await cleanup(deviceA, deviceB);
|
||||
// },
|
||||
//
|
||||
// );
|
||||
|
||||
// test.only(
|
||||
// "case 5: Device A's sync is interrupted halfway and Device B makes changes on the same note's content that didn't get synced on Device A due to interruption.",
|
||||
// async () => {
|
||||
// const deviceA = await initializeDevice("deviceA");
|
||||
// const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
// const noteIds = [];
|
||||
// for (let i = 0; i < 10; ++i) {
|
||||
// const id = await deviceA.notes.add({
|
||||
// content: {
|
||||
// type: "tiptap",
|
||||
// data: `<p>deviceA=true</p>`,
|
||||
// },
|
||||
// });
|
||||
// noteIds.push(id);
|
||||
// }
|
||||
|
||||
// await deviceA.sync(true);
|
||||
// await deviceB.sync(true);
|
||||
|
||||
// const unsyncedNoteIds = [];
|
||||
// for (let id of noteIds) {
|
||||
// const noteId = await deviceA.notes.add({
|
||||
// id,
|
||||
// content: {
|
||||
// type: "tiptap",
|
||||
// data: `<p>deviceA=true+changed=true</p>`,
|
||||
// },
|
||||
// });
|
||||
// unsyncedNoteIds.push(noteId);
|
||||
// }
|
||||
|
||||
// deviceA.eventManager.subscribe(
|
||||
// EVENTS.syncProgress,
|
||||
// async ({ type, total, current }) => {
|
||||
// const half = total / 2 + 1;
|
||||
// if (type === "upload" && current === half) {
|
||||
// await deviceA.syncer.stop();
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
|
||||
// await expect(deviceA.sync(true)).rejects.toThrow();
|
||||
|
||||
// await delay(10 * 1000);
|
||||
|
||||
// for (let id of unsyncedNoteIds) {
|
||||
// await deviceB.notes.add({
|
||||
// id,
|
||||
// content: {
|
||||
// type: "tiptap",
|
||||
// data: "<p>changes from device B</p>",
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
|
||||
// const error = await withError(async () => {
|
||||
// await deviceB.sync(true);
|
||||
// await deviceA.sync(true);
|
||||
// });
|
||||
|
||||
// expect(error).not.toBeInstanceOf(NoErrorThrownError);
|
||||
// expect(error.message.includes("Merge")).toBeTruthy();
|
||||
|
||||
// await cleanup(deviceA, deviceB);
|
||||
// },
|
||||
//
|
||||
// );
|
||||
|
||||
test("issue: running force sync from device A makes device B always download everything", async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
await syncAndWait(deviceA, deviceB, true);
|
||||
|
||||
const handler = jest.fn();
|
||||
deviceB.eventManager.subscribe(EVENTS.syncProgress, handler);
|
||||
|
||||
await deviceB.sync(true);
|
||||
|
||||
expect(handler).not.toHaveBeenCalled();
|
||||
|
||||
await cleanup(deviceB);
|
||||
});
|
||||
|
||||
test("issue: colors are not properly created if multiple notes are synced together", async () => {
|
||||
const deviceA = await initializeDevice("deviceA", [CHECK_IDS.noteColor]);
|
||||
const deviceB = await initializeDevice("deviceB", [CHECK_IDS.noteColor]);
|
||||
|
||||
const noteIds = [];
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
const id = await deviceA.notes.add({
|
||||
content: {
|
||||
type: "tiptap",
|
||||
data: `<p>deviceA=true</p>`,
|
||||
},
|
||||
});
|
||||
noteIds.push(id);
|
||||
}
|
||||
|
||||
await syncAndWait(deviceA, deviceB);
|
||||
|
||||
for (let noteId of noteIds) {
|
||||
await deviceA.notes.note(noteId).color("purple");
|
||||
expect(deviceB.notes.note(noteId)).toBeTruthy();
|
||||
expect(deviceB.notes.note(noteId).data.color).toBeUndefined();
|
||||
}
|
||||
|
||||
await syncAndWait(deviceA, deviceB);
|
||||
|
||||
await delay(2000);
|
||||
|
||||
const purpleColor = deviceB.colors.tag("purple");
|
||||
console.log(noteIds, purpleColor.noteIds);
|
||||
expect(noteIds.every((id) => purpleColor.noteIds.indexOf(id) > -1)).toBe(
|
||||
true
|
||||
);
|
||||
|
||||
await cleanup(deviceA, deviceB);
|
||||
});
|
||||
|
||||
test("issue: new topic on device A gets replaced by the new topic on device B", async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
const id = await deviceA.notebooks.add({ title: "Notebook 1" });
|
||||
|
||||
await syncAndWait(deviceA, deviceB, false);
|
||||
|
||||
expect(deviceB.notebooks.notebook(id)).toBeDefined();
|
||||
|
||||
await deviceA.notebooks.notebook(id).topics.add("Topic 1");
|
||||
|
||||
// to create a conflict
|
||||
await delay(1500);
|
||||
|
||||
await deviceB.notebooks.notebook(id).topics.add("Topic 2");
|
||||
|
||||
expect(deviceA.notebooks.notebook(id).topics.has("Topic 1")).toBeTruthy();
|
||||
|
||||
expect(deviceB.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
|
||||
|
||||
await syncAndWait(deviceA, deviceB, false);
|
||||
|
||||
await delay(1000);
|
||||
|
||||
await syncAndWait(deviceB, deviceB, false);
|
||||
|
||||
expect(deviceA.notebooks.notebook(id).topics.has("Topic 1")).toBeTruthy();
|
||||
expect(deviceB.notebooks.notebook(id).topics.has("Topic 1")).toBeTruthy();
|
||||
|
||||
expect(deviceA.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
|
||||
expect(deviceB.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
|
||||
|
||||
await cleanup(deviceA, deviceB);
|
||||
});
|
||||
|
||||
test("issue: remove notebook reference from notes that are removed from topic during merge", async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
const id = await deviceA.notebooks.add({
|
||||
title: "Notebook 1",
|
||||
topics: ["Topic 1"],
|
||||
});
|
||||
|
||||
await syncAndWait(deviceA, deviceB, false);
|
||||
|
||||
expect(deviceB.notebooks.notebook(id)).toBeDefined();
|
||||
|
||||
const noteA = await deviceA.notes.add({ title: "Note 1" });
|
||||
await deviceA.notes.move({ id, topic: "Topic 1" }, noteA);
|
||||
|
||||
expect(
|
||||
deviceA.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
|
||||
await delay(2000);
|
||||
|
||||
const noteB = await deviceB.notes.add({ title: "Note 2" });
|
||||
await deviceB.notes.move({ id, topic: "Topic 1" }, noteB);
|
||||
|
||||
expect(
|
||||
deviceB.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
|
||||
await syncAndWait(deviceB, deviceA, false);
|
||||
|
||||
expect(
|
||||
deviceA.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
expect(
|
||||
deviceB.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
|
||||
expect(deviceA.notes.note(noteA).data.notebooks).toHaveLength(0);
|
||||
|
||||
await cleanup(deviceA, deviceB);
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @returns {Promise<Database>}
|
||||
*/
|
||||
async function initializeDevice(id, capabilities = []) {
|
||||
console.time("Init device");
|
||||
EV.subscribe(EVENTS.userCheckStatus, async (type) => {
|
||||
return {
|
||||
type,
|
||||
result: capabilities.indexOf(type) > -1,
|
||||
};
|
||||
});
|
||||
|
||||
const device = new Database(new NodeStorageInterface(), EventSource, FS);
|
||||
// device.host({
|
||||
// API_HOST: "http://192.168.10.29:5264",
|
||||
// AUTH_HOST: "http://192.168.10.29:8264",
|
||||
// SSE_HOST: "http://192.168.10.29:7264",
|
||||
// ISSUES_HOST: "http://192.168.10.29:2624",
|
||||
// SUBSCRIPTIONS_HOST: "http://192.168.10.29:9264",
|
||||
// });
|
||||
|
||||
await device.init(id);
|
||||
|
||||
await device.user.login(
|
||||
process.env.EMAIL,
|
||||
process.env.PASSWORD,
|
||||
process.env.HASHED_PASSWORD
|
||||
);
|
||||
|
||||
await device.user.resetUser(false);
|
||||
|
||||
device.eventManager.subscribe(
|
||||
EVENTS.databaseSyncRequested,
|
||||
async (full, force) => {
|
||||
await device.sync(full, force);
|
||||
}
|
||||
);
|
||||
|
||||
console.timeEnd("Init device");
|
||||
return device;
|
||||
}
|
||||
|
||||
async function cleanup(...devices) {
|
||||
for (let device of devices) {
|
||||
await device.user.logout();
|
||||
device.eventManager.unsubscribeAll();
|
||||
}
|
||||
EV.unsubscribeAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Database} deviceA
|
||||
* @param {Database} deviceB
|
||||
* @returns
|
||||
*/
|
||||
function syncAndWait(deviceA, deviceB, force = false) {
|
||||
return new Promise((resolve) => {
|
||||
const ref = deviceB.eventManager.subscribe(EVENTS.syncCompleted, () => {
|
||||
ref.unsubscribe();
|
||||
resolve();
|
||||
});
|
||||
deviceA.sync(true, force);
|
||||
});
|
||||
}
|
||||
@@ -100,19 +100,5 @@ class Collector {
|
||||
...(await this._serialize(item)),
|
||||
};
|
||||
}
|
||||
|
||||
filter(data, predicate) {
|
||||
const arrays = ["notes", "notebooks", "content", "attachments", "settings"];
|
||||
const newData = {};
|
||||
for (let array of arrays) {
|
||||
if (!data[array]) continue;
|
||||
this.logger.info(`Filtering from ${data[array].length} ${array}`);
|
||||
newData[array] = data[array].filter(predicate);
|
||||
this.logger.info(
|
||||
`Length after filtering ${array}: ${newData[array].length}`
|
||||
);
|
||||
}
|
||||
return newData;
|
||||
}
|
||||
}
|
||||
export default Collector;
|
||||
|
||||
Reference in New Issue
Block a user