1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
import { GlobalOverrider } from "test/unit/utils";
import { PersistentCache } from "lib/PersistentCache.sys.mjs";
describe("PersistentCache", () => {
let fakeIOUtils;
let fakePathUtils;
let cache;
let filename = "cache.json";
let consoleErrorStub;
let globals;
let sandbox;
beforeEach(() => {
globals = new GlobalOverrider();
sandbox = sinon.createSandbox();
fakeIOUtils = {
writeJSON: sinon.stub().resolves(0),
readJSON: sinon.stub().resolves({}),
};
fakePathUtils = {
join: sinon.stub().returns(filename),
localProfileDir: "/",
};
consoleErrorStub = sandbox.stub();
globals.set("console", { error: consoleErrorStub });
globals.set("IOUtils", fakeIOUtils);
globals.set("PathUtils", fakePathUtils);
cache = new PersistentCache(filename);
});
afterEach(() => {
globals.restore();
sandbox.restore();
});
describe("#get", () => {
it("tries to read the file", async () => {
await cache.get("foo");
assert.calledOnce(fakeIOUtils.readJSON);
});
it("doesnt try to read the file if it was already loaded", async () => {
await cache._load();
fakeIOUtils.readJSON.resetHistory();
await cache.get("foo");
assert.notCalled(fakeIOUtils.readJSON);
});
it("should catch and report errors", async () => {
fakeIOUtils.readJSON.rejects(new SyntaxError("Failed to parse JSON"));
await cache._load();
assert.calledOnce(consoleErrorStub);
cache._cache = undefined;
consoleErrorStub.resetHistory();
fakeIOUtils.readJSON.rejects(
new DOMException("IOUtils shutting down", "AbortError")
);
await cache._load();
assert.calledOnce(consoleErrorStub);
cache._cache = undefined;
consoleErrorStub.resetHistory();
fakeIOUtils.readJSON.rejects(
new DOMException("File not found", "NotFoundError")
);
await cache._load();
assert.notCalled(consoleErrorStub);
});
it("returns data for a given cache key", async () => {
fakeIOUtils.readJSON.resolves({ foo: "bar" });
let value = await cache.get("foo");
assert.equal(value, "bar");
});
it("returns undefined for a cache key that doesn't exist", async () => {
let value = await cache.get("baz");
assert.equal(value, undefined);
});
it("returns all the data if no cache key is specified", async () => {
fakeIOUtils.readJSON.resolves({ foo: "bar" });
let value = await cache.get();
assert.deepEqual(value, { foo: "bar" });
});
});
describe("#set", () => {
it("tries to read the file on the first set", async () => {
await cache.set("foo", { x: 42 });
assert.calledOnce(fakeIOUtils.readJSON);
});
it("doesnt try to read the file if it was already loaded", async () => {
cache = new PersistentCache(filename, true);
await cache._load();
fakeIOUtils.readJSON.resetHistory();
await cache.set("foo", { x: 42 });
assert.notCalled(fakeIOUtils.readJSON);
});
it("sets a string value", async () => {
const key = "testkey";
const value = "testvalue";
await cache.set(key, value);
const cachedValue = await cache.get(key);
assert.equal(cachedValue, value);
});
it("sets an object value", async () => {
const key = "testkey";
const value = { x: 1, y: 2, z: 3 };
await cache.set(key, value);
const cachedValue = await cache.get(key);
assert.deepEqual(cachedValue, value);
});
it("writes the data to file", async () => {
const key = "testkey";
const value = { x: 1, y: 2, z: 3 };
await cache.set(key, value);
assert.calledOnce(fakeIOUtils.writeJSON);
assert.calledWith(
fakeIOUtils.writeJSON,
filename,
{ [[key]]: value },
{ tmpPath: `${filename}.tmp` }
);
});
it("throws when failing to get file path", async () => {
Object.defineProperty(fakePathUtils, "localProfileDir", {
get() {
throw new Error();
},
});
let rejected = false;
try {
await cache.set("key", "val");
} catch (error) {
rejected = true;
}
assert(rejected);
});
});
});
|