Path: blob/master/src/packages/backend/conat/test/sync/open-files.test.ts
1451 views
/*1Unit test basic functionality of the openFiles distributed key:value2store. Projects and compute servers use this to know what files3to open so they can fulfill their backend responsibilities:4- computation5- save to disk6- load from disk when file changes78DEVELOPMENT:910pnpm test ./open-files.test.ts1112*/1314import { openFiles as createOpenFiles } from "@cocalc/backend/conat/sync";15import { once } from "@cocalc/util/async-utils";16import { delay } from "awaiting";17import { before, after, wait } from "@cocalc/backend/conat/test/setup";1819beforeAll(before);2021const project_id = "00000000-0000-4000-8000-000000000000";22async function create() {23return await createOpenFiles(project_id, { noAutosave: true, noCache: true });24}2526describe("create open file tracker and do some basic operations", () => {27let o1, o2;28let file1 = `${Math.random()}.txt`;29let file2 = `${Math.random()}.txt`;3031it("creates two open files trackers (tracking same project) and clear them", async () => {32o1 = await create();33o2 = await create();34// ensure caching disabled so our sync tests are real35expect(o1.getKv() === o2.getKv()).toBe(false);36o1.clear();37await o1.save();38expect(o1.hasUnsavedChanges()).toBe(false);39o2.clear();40while (o2.hasUnsavedChanges() || o1.hasUnsavedChanges()) {41try {42// expected due to merge conflict and autosave being disabled.43await o2.save();44} catch {45await delay(10);46}47}48});4950it("confirm they are cleared", async () => {51expect(o1.getAll()).toEqual([]);52expect(o2.getAll()).toEqual([]);53});5455it("touch file in one and observe change and timestamp getting assigned by server", async () => {56o1.touch(file1);57expect(o1.get(file1).time).toBeCloseTo(Date.now(), -3);58});5960it("touches file in one and observes change by OTHER", async () => {61o1.touch(file2);62expect(o1.get(file2)?.path).toBe(file2);63expect(o2.get(file2)).toBe(undefined);64await o1.save();65if (o2.get(file2) == null) {66await once(o2, "change", 250);67expect(o2.get(file2).path).toBe(file2);68expect(o2.get(file2).time == null).toBe(false);69}70});7172it("get all in o2 sees both file1 and file2", async () => {73const v = o2.getAll();74expect(v[0].path).toBe(file1);75expect(v[1].path).toBe(file2);76expect(v.length).toBe(2);77});7879it("delete file1 and verify fact that it is deleted is sync'd", async () => {80o1.delete(file1);81expect(o1.get(file1)).toBe(undefined);82expect(o1.getAll().length).toBe(1);83await o1.save();8485// verify file is gone in o2, at least after waiting (if necessary)86await wait({87until: () => {88return o2.getAll().length == 1;89},90});91expect(o2.get(file1)).toBe(undefined);92// should be 1 due to file2 still being there:93expect(o2.getAll().length).toBe(1);9495// Also confirm file1 is gone in a newly opened one:96const o3 = await create();97expect(o3.get(file1)).toBe(undefined);98// should be 1 due to file2 still being there, but not file1.99expect(o3.getAll().length).toBe(1);100o3.close();101});102103it("sets an error", async () => {104o2.setError(file2, Error("test error"));105expect(o2.get(file2).error.error).toBe("Error: test error");106expect(typeof o2.get(file2).error.time == "number").toBe(true);107expect(Math.abs(Date.now() - o2.get(file2).error.time)).toBeLessThan(10000);108try {109// get a conflict due to above so resolve it...110await o2.save();111} catch {112await o2.save();113}114if (!o1.get(file2).error) {115await once(o1, "change", 250);116}117expect(o1.get(file2).error.error).toBe("Error: test error");118});119120it("clears the error", async () => {121o1.setError(file2);122expect(o1.get(file2).error).toBe(undefined);123await o1.save();124if (o2.get(file2).error) {125await once(o2, "change", 250);126}127expect(o2.get(file2).error).toBe(undefined);128});129});130131afterAll(after);132133134