Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/backend/conat/test/sync/open-files.test.ts
1451 views
1
/*
2
Unit test basic functionality of the openFiles distributed key:value
3
store. Projects and compute servers use this to know what files
4
to open so they can fulfill their backend responsibilities:
5
- computation
6
- save to disk
7
- load from disk when file changes
8
9
DEVELOPMENT:
10
11
pnpm test ./open-files.test.ts
12
13
*/
14
15
import { openFiles as createOpenFiles } from "@cocalc/backend/conat/sync";
16
import { once } from "@cocalc/util/async-utils";
17
import { delay } from "awaiting";
18
import { before, after, wait } from "@cocalc/backend/conat/test/setup";
19
20
beforeAll(before);
21
22
const project_id = "00000000-0000-4000-8000-000000000000";
23
async function create() {
24
return await createOpenFiles(project_id, { noAutosave: true, noCache: true });
25
}
26
27
describe("create open file tracker and do some basic operations", () => {
28
let o1, o2;
29
let file1 = `${Math.random()}.txt`;
30
let file2 = `${Math.random()}.txt`;
31
32
it("creates two open files trackers (tracking same project) and clear them", async () => {
33
o1 = await create();
34
o2 = await create();
35
// ensure caching disabled so our sync tests are real
36
expect(o1.getKv() === o2.getKv()).toBe(false);
37
o1.clear();
38
await o1.save();
39
expect(o1.hasUnsavedChanges()).toBe(false);
40
o2.clear();
41
while (o2.hasUnsavedChanges() || o1.hasUnsavedChanges()) {
42
try {
43
// expected due to merge conflict and autosave being disabled.
44
await o2.save();
45
} catch {
46
await delay(10);
47
}
48
}
49
});
50
51
it("confirm they are cleared", async () => {
52
expect(o1.getAll()).toEqual([]);
53
expect(o2.getAll()).toEqual([]);
54
});
55
56
it("touch file in one and observe change and timestamp getting assigned by server", async () => {
57
o1.touch(file1);
58
expect(o1.get(file1).time).toBeCloseTo(Date.now(), -3);
59
});
60
61
it("touches file in one and observes change by OTHER", async () => {
62
o1.touch(file2);
63
expect(o1.get(file2)?.path).toBe(file2);
64
expect(o2.get(file2)).toBe(undefined);
65
await o1.save();
66
if (o2.get(file2) == null) {
67
await once(o2, "change", 250);
68
expect(o2.get(file2).path).toBe(file2);
69
expect(o2.get(file2).time == null).toBe(false);
70
}
71
});
72
73
it("get all in o2 sees both file1 and file2", async () => {
74
const v = o2.getAll();
75
expect(v[0].path).toBe(file1);
76
expect(v[1].path).toBe(file2);
77
expect(v.length).toBe(2);
78
});
79
80
it("delete file1 and verify fact that it is deleted is sync'd", async () => {
81
o1.delete(file1);
82
expect(o1.get(file1)).toBe(undefined);
83
expect(o1.getAll().length).toBe(1);
84
await o1.save();
85
86
// verify file is gone in o2, at least after waiting (if necessary)
87
await wait({
88
until: () => {
89
return o2.getAll().length == 1;
90
},
91
});
92
expect(o2.get(file1)).toBe(undefined);
93
// should be 1 due to file2 still being there:
94
expect(o2.getAll().length).toBe(1);
95
96
// Also confirm file1 is gone in a newly opened one:
97
const o3 = await create();
98
expect(o3.get(file1)).toBe(undefined);
99
// should be 1 due to file2 still being there, but not file1.
100
expect(o3.getAll().length).toBe(1);
101
o3.close();
102
});
103
104
it("sets an error", async () => {
105
o2.setError(file2, Error("test error"));
106
expect(o2.get(file2).error.error).toBe("Error: test error");
107
expect(typeof o2.get(file2).error.time == "number").toBe(true);
108
expect(Math.abs(Date.now() - o2.get(file2).error.time)).toBeLessThan(10000);
109
try {
110
// get a conflict due to above so resolve it...
111
await o2.save();
112
} catch {
113
await o2.save();
114
}
115
if (!o1.get(file2).error) {
116
await once(o1, "change", 250);
117
}
118
expect(o1.get(file2).error.error).toBe("Error: test error");
119
});
120
121
it("clears the error", async () => {
122
o1.setError(file2);
123
expect(o1.get(file2).error).toBe(undefined);
124
await o1.save();
125
if (o2.get(file2).error) {
126
await once(o2, "change", 250);
127
}
128
expect(o2.get(file2).error).toBe(undefined);
129
});
130
});
131
132
afterAll(after);
133
134