mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-12 07:04:52 +09:00
Migrate the usage of CommonJS specific code and imports to ESM. Roughly summarised this: - Changes all require() calls to import statement or expressions. - Changes usage of module.exports to export statements. - Replaces require.resolve with import.meta.resolve. - Replaces conditional imports with conditional subpath imports. - Replaces module === require.main with import.meta.main. - Adds the full filename with extention to all relative imports. - Changes all usage of __dirname and __filename to their import.meta equivalents. - Sets "type": "module" in package.json files. - Removes usage of "use strict". - Adds .ts an an alias of .js for the webpack build. Due to differences in how require and import resolves files the plugin entrypoints will have to include the filename and extension after this migration in order to work.
777 lines
27 KiB
JavaScript
777 lines
27 KiB
JavaScript
import events from "node:events";
|
|
import assert from "node:assert/strict";
|
|
import * as lib from "@clusterio/lib";
|
|
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
|
|
class MockDatastoreProvider extends lib.DatastoreProvider {
|
|
constructor() {
|
|
super();
|
|
this.methodCalls = [];
|
|
this.value = new Map();
|
|
this.methodCalls.push(["constructor"]);
|
|
}
|
|
|
|
async save(data) {
|
|
this.methodCalls.push(["save", data]);
|
|
}
|
|
|
|
async load() {
|
|
this.methodCalls.push(["load"]);
|
|
return this.value;
|
|
}
|
|
}
|
|
|
|
class MockDatastore extends lib.Datastore {
|
|
getData() {
|
|
return this.data; // Protected member
|
|
}
|
|
|
|
callTouch(updates = [0]) {
|
|
this.touch(updates); // Protected method
|
|
}
|
|
}
|
|
|
|
describe("lib/datastore", function() {
|
|
const baseDir = path.join("temp", "test", "datastore");
|
|
const testFiles = path.join("test", "file");
|
|
before(async function() {
|
|
await fs.rm(baseDir, { force: true, recursive: true, maxRetries: 10 });
|
|
await fs.mkdir(baseDir, { recursive: true });
|
|
});
|
|
|
|
describe("class DatastoreProvider", function() {
|
|
let datastoreProvider;
|
|
beforeEach(function() {
|
|
datastoreProvider = new MockDatastoreProvider();
|
|
});
|
|
|
|
it("can be constructed", function() {
|
|
assert.deepEqual(datastoreProvider.methodCalls[0], ["constructor"]);
|
|
});
|
|
|
|
it("can be bootstrapped", async function() {
|
|
const rtn = await datastoreProvider.bootstrap();
|
|
assert.equal(rtn[0], datastoreProvider);
|
|
assert.equal(rtn[1], datastoreProvider.value);
|
|
assert.deepEqual(datastoreProvider.methodCalls[1], ["load"]);
|
|
});
|
|
});
|
|
|
|
describe("class MemoryDatastoreProvider", function() {
|
|
let datastoreProvider, initialData;
|
|
beforeEach(function() {
|
|
initialData = new Map([
|
|
["foo", "fooValue"], ["bar", "barValue"], ["baz", "bazValue"],
|
|
]);
|
|
datastoreProvider = new lib.MemoryDatastoreProvider(initialData);
|
|
});
|
|
|
|
describe("constructor", function() {
|
|
it("has a default value", async function() {
|
|
const [, value] = await new lib.MemoryDatastoreProvider().bootstrap();
|
|
assert.notEqual(value, undefined);
|
|
assert.notEqual(value, null);
|
|
});
|
|
it("can have a value given", async function() {
|
|
const expectedValue = new Map([["foo", "bar"]]);
|
|
const [, value] = await new lib.MemoryDatastoreProvider(expectedValue).bootstrap();
|
|
assert.deepEqual(value, expectedValue);
|
|
});
|
|
});
|
|
|
|
describe("save", function() {
|
|
it("can save data", async function() {
|
|
const newData = new Map([["foo2", "fooValue2"]]);
|
|
await datastoreProvider.save(newData);
|
|
const loadedData = await datastoreProvider.load();
|
|
assert.deepEqual(loadedData, newData);
|
|
});
|
|
});
|
|
|
|
describe("load", function() {
|
|
it("can load data", async function() {
|
|
const loadedData = await datastoreProvider.load();
|
|
assert.deepEqual(loadedData, initialData);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("class JsonDatastoreProvider", function() {
|
|
let datastoreProvider, initialData, dataPath;
|
|
before(async function() {
|
|
dataPath = path.join(baseDir, "data.json");
|
|
initialData = Object.entries(JSON.parse(await fs.readFile(
|
|
path.join(testFiles, "json", "load_map.json"), { encoding: "utf8" }
|
|
)));
|
|
});
|
|
|
|
beforeEach(async function() {
|
|
await fs.copyFile(path.join(testFiles, "json", "load_map.json"), dataPath);
|
|
datastoreProvider = new lib.JsonDatastoreProvider(dataPath);
|
|
});
|
|
|
|
describe("constructor", function() {
|
|
it("has default values", async function() {
|
|
datastoreProvider = new lib.JsonDatastoreProvider(dataPath);
|
|
|
|
const [, value] = await datastoreProvider.bootstrap();
|
|
assert.deepEqual(value, new Map(initialData));
|
|
});
|
|
it("can accept a fromJson method", async function() {
|
|
datastoreProvider = new lib.JsonDatastoreProvider(
|
|
dataPath,
|
|
json => [json]
|
|
);
|
|
|
|
const [, value] = await datastoreProvider.bootstrap();
|
|
assert.deepEqual(value, new Map(initialData.map(([k, v]) => [k, [v]])));
|
|
});
|
|
it("can accept a migration method", async function() {
|
|
datastoreProvider = new lib.JsonDatastoreProvider(
|
|
dataPath,
|
|
json => json, // Nop
|
|
rawJson => ({ c: "foo", ...rawJson })
|
|
);
|
|
|
|
const [, value] = await datastoreProvider.bootstrap();
|
|
assert.deepEqual(value, new Map([...initialData, ["c", "foo"]]));
|
|
});
|
|
it("can accept a finalise method", async function() {
|
|
datastoreProvider = new lib.JsonDatastoreProvider(
|
|
dataPath,
|
|
json => json, // Nop
|
|
rawJson => rawJson, // Nop
|
|
obj => [obj]
|
|
);
|
|
|
|
const [, value] = await datastoreProvider.bootstrap();
|
|
assert.deepEqual(value, new Map(initialData.map(([k, v]) => [k, [v]])));
|
|
});
|
|
});
|
|
|
|
describe("save", function() {
|
|
it("can save data", async function() {
|
|
const newData = new Map([["foo", "bar"]]);
|
|
await datastoreProvider.save(newData);
|
|
const loadedData = await datastoreProvider.load();
|
|
assert.deepEqual(loadedData, newData);
|
|
});
|
|
});
|
|
|
|
describe("load", function() {
|
|
it("can load data", async function() {
|
|
const loadedData = await datastoreProvider.load();
|
|
assert.deepEqual(loadedData, new Map(initialData));
|
|
});
|
|
it("has a default value", async function() {
|
|
datastoreProvider = new lib.JsonDatastoreProvider(path.join(baseDir, "not_exists.json"));
|
|
const loadedData = await datastoreProvider.load();
|
|
assert.equal(loadedData.size, 0);
|
|
});
|
|
it("throws for invalid file contents", async function() {
|
|
await fs.copyFile(path.join(testFiles, "json", "invalid.json"), dataPath);
|
|
await assert.rejects(() => datastoreProvider.load(dataPath));
|
|
});
|
|
it("writes a backup if size changes after load", async function() {
|
|
datastoreProvider = new lib.JsonDatastoreProvider(
|
|
dataPath,
|
|
json => json, // Nop
|
|
rawJson => rawJson, // Nop
|
|
() => undefined, // Throw away all data
|
|
);
|
|
|
|
const entriesBefore = await fs.readdir(baseDir);
|
|
const loadedData = await datastoreProvider.load();
|
|
const entriesAfter = await fs.readdir(baseDir);
|
|
|
|
assert.equal(entriesAfter.length, entriesBefore.length + 1, "Backup was not created");
|
|
assert.equal(loadedData.size, 0);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("class JsonIdDatastoreProvider", function() {
|
|
let datastoreProvider, initialData, dataPath;
|
|
before(async function() {
|
|
dataPath = path.join(baseDir, "data.json");
|
|
initialData = JSON.parse(await fs.readFile(
|
|
path.join(testFiles, "json", "load_array_map.json"), { encoding: "utf8" }
|
|
)).map(v => [v.id, v]);
|
|
});
|
|
|
|
beforeEach(async function() {
|
|
await fs.copyFile(path.join(testFiles, "json", "load_array_map.json"), dataPath);
|
|
datastoreProvider = new lib.JsonIdDatastoreProvider(dataPath);
|
|
});
|
|
|
|
describe("constructor", function() {
|
|
it("has default values", async function() {
|
|
datastoreProvider = new lib.JsonIdDatastoreProvider(dataPath);
|
|
|
|
const [, value] = await datastoreProvider.bootstrap();
|
|
assert.deepEqual(value, new Map(initialData));
|
|
});
|
|
it("can accept a fromJson method", async function() {
|
|
datastoreProvider = new lib.JsonIdDatastoreProvider(
|
|
dataPath,
|
|
json => ({...json, foo: true})
|
|
);
|
|
|
|
const [, value] = await datastoreProvider.bootstrap();
|
|
assert.deepEqual(value, new Map(initialData.map(v => ([v[0], {...v[1], foo: true}]))));
|
|
});
|
|
it("can accept a migration method", async function() {
|
|
datastoreProvider = new lib.JsonIdDatastoreProvider(
|
|
dataPath,
|
|
json => json, // Nop
|
|
rawJson => ([{ id: "c" }, ...rawJson])
|
|
);
|
|
|
|
const [, value] = await datastoreProvider.bootstrap();
|
|
assert.deepEqual(value, new Map([...initialData, ["c", { id: "c" }]]));
|
|
});
|
|
it("can accept a finalise method", async function() {
|
|
datastoreProvider = new lib.JsonIdDatastoreProvider(
|
|
dataPath,
|
|
json => json, // Nop
|
|
rawJson => rawJson, // Nop
|
|
obj => ({...obj, foo: true})
|
|
);
|
|
|
|
const [, value] = await datastoreProvider.bootstrap();
|
|
assert.deepEqual(value, new Map(initialData.map(v => ([v[0], {...v[1], foo: true}]))));
|
|
});
|
|
});
|
|
|
|
describe("save", function() {
|
|
it("can save data", async function() {
|
|
const newData = new Map([["foo", { id: "foo" }]]);
|
|
await datastoreProvider.save(newData);
|
|
const loadedData = await datastoreProvider.load();
|
|
assert.deepEqual(loadedData, newData);
|
|
});
|
|
});
|
|
|
|
describe("load", function() {
|
|
it("can load data", async function() {
|
|
const loadedData = await datastoreProvider.load();
|
|
assert.equal(loadedData.size, initialData.length);
|
|
assert.deepEqual(loadedData, new Map(initialData));
|
|
});
|
|
it("has a default value", async function() {
|
|
datastoreProvider = new lib.JsonIdDatastoreProvider(path.join(baseDir, "not_exists.json"));
|
|
const loadedData = await datastoreProvider.load();
|
|
assert.equal(loadedData.size, 0);
|
|
});
|
|
it("throws for invalid file contents", async function() {
|
|
await fs.copyFile(path.join(testFiles, "json", "invalid.json"), dataPath);
|
|
await assert.rejects(() => datastoreProvider.load(dataPath));
|
|
});
|
|
it("writes a backup if size changes after load", async function() {
|
|
datastoreProvider = new lib.JsonIdDatastoreProvider(
|
|
dataPath,
|
|
json => json, // Nop
|
|
rawJson => rawJson, // Nop
|
|
() => undefined, // Throw away all data
|
|
);
|
|
|
|
const entriesBefore = await fs.readdir(baseDir);
|
|
const loadedData = await datastoreProvider.load();
|
|
const entriesAfter = await fs.readdir(baseDir);
|
|
|
|
assert.equal(entriesAfter.length, entriesBefore.length + 1, "Backup was not created");
|
|
assert.equal(loadedData.size, 0);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("class Datastore", function() {
|
|
let datastoreProvider, datastore;
|
|
beforeEach(function() {
|
|
datastoreProvider = new MockDatastoreProvider();
|
|
datastoreProvider.value = new Map([
|
|
["foo", "fooValue"], ["bar", "barValue"], ["baz", "bazValue"],
|
|
]);
|
|
datastore = new MockDatastore(datastoreProvider, datastoreProvider.value);
|
|
});
|
|
|
|
describe("constructor", function() {
|
|
it("has a default value", function() {
|
|
datastore = new MockDatastore(datastoreProvider);
|
|
assert.deepEqual(datastore.getData(), new Map());
|
|
});
|
|
it("accepts a starting value", function() {
|
|
datastore = new MockDatastore(datastoreProvider, datastoreProvider.value);
|
|
assert.deepEqual(datastore.getData(), datastoreProvider.value);
|
|
});
|
|
it("accepts the return of bootstrap", async function() {
|
|
datastore = new MockDatastore(...await datastoreProvider.bootstrap());
|
|
assert.deepEqual(datastore.getData(), datastoreProvider.value);
|
|
});
|
|
});
|
|
|
|
describe("getFilePath", function() {
|
|
it("returns the correct filepath", function() {
|
|
const mockConfig = {
|
|
get(field) {
|
|
if (field === "controller.database_directory") {
|
|
return "databaseDir";
|
|
}
|
|
throw new Error(`field ${field} not implemented`);
|
|
},
|
|
};
|
|
|
|
assert.equal(
|
|
lib.Datastore.getFilePath(mockConfig, "db1.json"),
|
|
path.resolve("databaseDir", "db1.json")
|
|
);
|
|
assert.equal(
|
|
lib.Datastore.getFilePath(mockConfig, "db2.txt"),
|
|
path.resolve("databaseDir", "db2.txt")
|
|
);
|
|
assert.equal(
|
|
lib.Datastore.getFilePath(mockConfig, path.join("foo", "db3.json")),
|
|
path.resolve("databaseDir", "foo", "db3.json")
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("save", function() {
|
|
it("should not save when clean", async function() {
|
|
await datastore.save();
|
|
assert.equal(datastoreProvider.methodCalls.length, 1);
|
|
});
|
|
it("should save when dirty", async function() {
|
|
datastore.callTouch(); // Make the data dirty
|
|
await datastore.save();
|
|
assert.equal(datastoreProvider.methodCalls.length, 2);
|
|
assert.deepEqual(datastoreProvider.methodCalls[1], ["save", datastore.getData()]);
|
|
});
|
|
it("should not save multiple times", async function() {
|
|
datastore.callTouch(); // Make the data dirty
|
|
await datastore.save();
|
|
assert.equal(datastoreProvider.methodCalls.length, 2);
|
|
assert.deepEqual(datastoreProvider.methodCalls[1], ["save", datastore.getData()]);
|
|
await datastore.save();
|
|
assert.equal(datastoreProvider.methodCalls.length, 2);
|
|
});
|
|
});
|
|
|
|
describe("load", function() {
|
|
it("should load new data", async function() {
|
|
datastoreProvider.value = new Map();
|
|
assert.notEqual(datastore.getData(), datastoreProvider.value);
|
|
await datastore.load();
|
|
assert.equal(datastoreProvider.methodCalls.length, 2);
|
|
assert.deepEqual(datastoreProvider.methodCalls[1], ["load"]);
|
|
assert.deepEqual(datastore.getData(), datastoreProvider.value);
|
|
});
|
|
it("should not save after a load", async function() {
|
|
datastore.callTouch(); // Make the data dirty
|
|
await datastore.load();
|
|
assert.equal(datastoreProvider.methodCalls.length, 2);
|
|
assert.deepEqual(datastoreProvider.methodCalls[1], ["load"]);
|
|
await datastore.save();
|
|
assert.equal(datastoreProvider.methodCalls.length, 2);
|
|
});
|
|
});
|
|
|
|
describe("touch", function() {
|
|
it("should emit update events", function() {
|
|
const eventsRaised = [];
|
|
datastore.on("update", updates => eventsRaised.push(updates));
|
|
|
|
let updates = ["foo"];
|
|
datastore.callTouch(updates);
|
|
assert.equal(eventsRaised.length, 1);
|
|
assert.deepEqual(eventsRaised[0], updates);
|
|
|
|
updates = ["foo", "bar"];
|
|
datastore.callTouch(updates);
|
|
assert.equal(eventsRaised.length, 2);
|
|
assert.deepEqual(eventsRaised[1], updates);
|
|
});
|
|
});
|
|
|
|
describe("has", function() {
|
|
it("returns true correctly", function() {
|
|
assert.equal(datastore.has("foo"), true);
|
|
assert.equal(datastore.has("bar"), true);
|
|
assert.equal(datastore.has("baz"), true);
|
|
});
|
|
it("returns false correctly", function() {
|
|
assert.equal(datastore.has("missing1"), false);
|
|
assert.equal(datastore.has("missing2"), false);
|
|
assert.equal(datastore.has(1), false);
|
|
assert.equal(datastore.has(2), false);
|
|
});
|
|
});
|
|
|
|
describe("get", function() {
|
|
it("returns the correct value", function() {
|
|
assert.equal(datastore.get("foo"), "fooValue");
|
|
assert.equal(datastore.get("bar"), "barValue");
|
|
assert.equal(datastore.get("baz"), "bazValue");
|
|
});
|
|
it("returns undefined correctly", function() {
|
|
assert.equal(datastore.get("missing1"), undefined);
|
|
assert.equal(datastore.get("missing2"), undefined);
|
|
assert.equal(datastore.get(1), undefined);
|
|
assert.equal(datastore.get(2), undefined);
|
|
});
|
|
});
|
|
|
|
describe("getMutable", function() {
|
|
it("returns the correct value", function() {
|
|
assert.equal(datastore.getMutable("foo"), "fooValue");
|
|
assert.equal(datastore.getMutable("bar"), "barValue");
|
|
assert.equal(datastore.getMutable("baz"), "bazValue");
|
|
});
|
|
it("returns undefined correctly", function() {
|
|
assert.equal(datastore.getMutable("missing1"), undefined);
|
|
assert.equal(datastore.getMutable("missing2"), undefined);
|
|
assert.equal(datastore.getMutable(1), undefined);
|
|
assert.equal(datastore.getMutable(2), undefined);
|
|
});
|
|
});
|
|
|
|
describe("size", function() {
|
|
it("returns the correct size", function() {
|
|
assert.equal(datastore.size, 3);
|
|
});
|
|
});
|
|
|
|
describe("iterator", function() {
|
|
it("iterates all values", function() {
|
|
const entries = [];
|
|
for (const entry of datastore) {
|
|
entries.push(entry);
|
|
}
|
|
assert.deepEqual(entries, [...datastoreProvider.value.entries()]);
|
|
});
|
|
});
|
|
|
|
describe("values", function() {
|
|
it("returns all values", function() {
|
|
assert.deepEqual([...datastore.values()], [...datastoreProvider.value.values()]);
|
|
});
|
|
});
|
|
|
|
describe("valuesMutable", function() {
|
|
it("returns all values", function() {
|
|
assert.deepEqual([...datastore.valuesMutable()], [...datastoreProvider.value.values()]);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("class KeyValueDatastore", function() {
|
|
let datastoreProvider, datastore, eventsRaised;
|
|
beforeEach(function() {
|
|
eventsRaised = [];
|
|
datastoreProvider = new MockDatastoreProvider();
|
|
datastoreProvider.value = new Map([
|
|
["foo", "fooValue"], ["bar", "barValue"], ["baz", "bazValue"],
|
|
]);
|
|
datastore = new lib.KeyValueDatastore(datastoreProvider, datastoreProvider.value);
|
|
datastore.on("update", updates => eventsRaised.push(updates));
|
|
});
|
|
|
|
describe("set", function() {
|
|
it("can set a value", function() {
|
|
datastore.set("test", "testValue");
|
|
assert.equal(datastore.get("test"), "testValue");
|
|
assert.equal(eventsRaised.length, 1);
|
|
assert.deepEqual(eventsRaised[0], [["test", "testValue"]]);
|
|
});
|
|
it("allows saving after a value is set", async function() {
|
|
datastore.set("test", "testValue");
|
|
await datastore.save();
|
|
assert.equal(datastoreProvider.methodCalls.length, 2);
|
|
assert.deepEqual(datastoreProvider.methodCalls[1],
|
|
["save", new Map([...datastoreProvider.value.entries(), ["test", "testValue"]])]
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("setMany", function() {
|
|
it("does not save with zero changes", async function() {
|
|
datastore.setMany([]);
|
|
await datastore.save();
|
|
assert.equal(datastoreProvider.methodCalls.length, 1);
|
|
});
|
|
it("can set many values at once", async function() {
|
|
const updates = [["test1", "testValue1"], ["test2", "testValue2"]];
|
|
datastore.setMany(updates);
|
|
assert.equal(datastore.get("test1"), "testValue1");
|
|
assert.equal(datastore.get("test2"), "testValue2");
|
|
await datastore.save();
|
|
assert.equal(eventsRaised.length, 1);
|
|
assert.deepEqual(eventsRaised[0], updates);
|
|
assert.equal(datastoreProvider.methodCalls.length, 2);
|
|
assert.deepEqual(datastoreProvider.methodCalls[1],
|
|
["save", new Map([...datastoreProvider.value.entries(), ...updates])]
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("delete", function() {
|
|
it("can delete a value", function() {
|
|
datastore.delete("foo");
|
|
assert.equal(datastore.get("foo"), undefined);
|
|
assert.equal(eventsRaised.length, 1);
|
|
assert.deepEqual(eventsRaised[0], [["foo", "fooValue", true]]);
|
|
});
|
|
it("allows saving after a value is deleted", async function() {
|
|
datastore.delete("foo");
|
|
await datastore.save();
|
|
assert.equal(datastoreProvider.methodCalls.length, 2);
|
|
assert.deepEqual(datastoreProvider.methodCalls[1],
|
|
["save", new Map([...datastoreProvider.value.entries()].filter((v, k) => k !== "foo"))]
|
|
);
|
|
});
|
|
it("does nothing when the key does not exist", function() {
|
|
datastore.delete("test");
|
|
assert.equal(eventsRaised.length, 0);
|
|
});
|
|
});
|
|
|
|
describe("deleteMany", function() {
|
|
it("does not save with zero changes", async function() {
|
|
datastore.deleteMany([]);
|
|
await datastore.save();
|
|
assert.equal(datastoreProvider.methodCalls.length, 1);
|
|
});
|
|
it("can delete many values at once", async function() {
|
|
const keys = ["foo", "bar"];
|
|
datastore.deleteMany(keys);
|
|
assert.equal(datastore.get("foo"), undefined);
|
|
assert.equal(datastore.get("bar"), undefined);
|
|
await datastore.save();
|
|
assert.equal(eventsRaised.length, 1);
|
|
assert.deepEqual(eventsRaised[0], [["foo", "fooValue", true], ["bar", "barValue", true]]);
|
|
assert.equal(datastoreProvider.methodCalls.length, 2);
|
|
assert.deepEqual(datastoreProvider.methodCalls[1],
|
|
["save", new Map([...datastoreProvider.value.entries()].filter((v, k) => !keys.includes(k)))]
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("class SubscribableDatastore", function() {
|
|
let datastoreProvider, datastore, eventsRaised;
|
|
beforeEach(function() {
|
|
eventsRaised = [];
|
|
datastoreProvider = new MockDatastoreProvider();
|
|
datastoreProvider.value = new Map([
|
|
["foo", {id: "foo", updatedAtMs: 10}], ["bar", {id: "bar"}], ["baz", {id: "baz"}],
|
|
]);
|
|
datastore = new lib.SubscribableDatastore(datastoreProvider, datastoreProvider.value);
|
|
datastore.on("update", updates => eventsRaised.push(updates));
|
|
});
|
|
|
|
function forceFuture() {
|
|
const future = {id: "future"};
|
|
for (let i = 1; i < 100; i++) {
|
|
datastore.set(future);
|
|
}
|
|
return future.updatedAtMs;
|
|
}
|
|
|
|
describe("lastUpdateAtMs", function() {
|
|
it("is set to the highest value after constructing a new instance", function() {
|
|
assert(datastore.lastUpdatedAtMs === 10, `${datastore.lastUpdatedAtMs} !== 10`);
|
|
});
|
|
it("is set to the highest value after loading from a provider", async function() {
|
|
const future = Date.now() + 2000;
|
|
datastore.provider.value = ["future", {id: "future", updatedAtMs: future}];
|
|
await datastore.load();
|
|
assert(datastore.lastUpdatedAtMs === future, `${datastore.lastUpdatedAtMs} !== ${future}`);
|
|
});
|
|
});
|
|
describe("set", function() {
|
|
it("can set a value", function() {
|
|
const value = { id: "test" };
|
|
datastore.set(value);
|
|
assert.equal(datastore.get(value.id), value);
|
|
assert.equal(eventsRaised.length, 1);
|
|
assert.deepEqual(eventsRaised[0], [value]);
|
|
});
|
|
it("allows saving after a value is set", async function() {
|
|
const value = { id: "test" };
|
|
datastore.set(value);
|
|
await datastore.save();
|
|
assert.equal(datastoreProvider.methodCalls.length, 2);
|
|
assert.deepEqual(datastoreProvider.methodCalls[1],
|
|
["save", new Map([...datastoreProvider.value.entries(), [value.id, value]])]
|
|
);
|
|
});
|
|
it("sets the updatedAtMs property", async function() {
|
|
const value = { id: "test" };
|
|
datastore.set(value);
|
|
assert.notEqual(value.updatedAtMs, undefined);
|
|
assert(value.updatedAtMs > 0, "updatedAtMs not greater than zero");
|
|
});
|
|
it("increments the updatedAtMs property", async function() {
|
|
const value = { id: "test" };
|
|
datastore.set(value);
|
|
const prev = value.updatedAtMs;
|
|
datastore.set(value);
|
|
assert(value.updatedAtMs > prev, "updatedAtMs must increase");
|
|
});
|
|
it("increments the updatedAtMs property past existing values", async function() {
|
|
const future = forceFuture();
|
|
const value = { id: "test" };
|
|
datastore.set(value);
|
|
assert(value.updatedAtMs > future, "updatedAtMs must increase");
|
|
});
|
|
});
|
|
|
|
describe("setMany", function() {
|
|
it("does not save with zero changes", async function() {
|
|
datastore.setMany([]);
|
|
await datastore.save();
|
|
assert.equal(datastoreProvider.methodCalls.length, 1);
|
|
});
|
|
it("can set many values at once", async function() {
|
|
const updates = [{ id: "test1" }, { id: "test2" }];
|
|
datastore.setMany(updates);
|
|
assert.equal(datastore.get(updates[0].id), updates[0]);
|
|
assert.equal(datastore.get(updates[1].id), updates[1]);
|
|
await datastore.save();
|
|
assert.equal(eventsRaised.length, 1);
|
|
assert.deepEqual(eventsRaised[0], updates);
|
|
assert.equal(datastoreProvider.methodCalls.length, 2);
|
|
assert.deepEqual(datastoreProvider.methodCalls[1],
|
|
["save", new Map([...datastoreProvider.value.entries(), ...updates.map(v => [v.id, v])])]
|
|
);
|
|
});
|
|
it("sets the updatedAtMs property", async function() {
|
|
const updates = [{ id: "test1" }, { id: "test2" }];
|
|
datastore.setMany(updates);
|
|
assert.notEqual(updates[0].updatedAtMs, undefined);
|
|
assert.notEqual(updates[1].updatedAtMs, undefined);
|
|
assert(updates[0].updatedAtMs > 0, "updatedAtMs greater than zero");
|
|
assert(updates[1].updatedAtMs > 0, "updatedAtMs greater than zero");
|
|
});
|
|
it("increments the updatedAtMs property", async function() {
|
|
const value = { id: "test1" };
|
|
datastore.set(value);
|
|
const prev = value.updatedAtMs;
|
|
const updates = [{ id: "test1" }, { id: "test2" }];
|
|
datastore.setMany(updates);
|
|
assert(updates[0].updatedAtMs > prev, "updatedAtMs must increase");
|
|
assert(updates[1].updatedAtMs > prev, "updatedAtMs must increase");
|
|
});
|
|
it("increments the updatedAtMs property past existing values", async function() {
|
|
const future = forceFuture();
|
|
const updates = [{ id: "test1" }, { id: "test2" }];
|
|
datastore.setMany(updates);
|
|
assert(updates[0].updatedAtMs > future, "updatedAtMs must increase");
|
|
assert(updates[1].updatedAtMs > future, "updatedAtMs must increase");
|
|
});
|
|
});
|
|
|
|
describe("delete", function() {
|
|
it("can delete a value", function() {
|
|
const value = datastore.get("foo");
|
|
datastore.delete(value);
|
|
assert.equal(datastore.get(value.id), undefined);
|
|
assert.equal(eventsRaised.length, 1);
|
|
assert.deepEqual(eventsRaised[0], [value]);
|
|
});
|
|
it("allows saving after a value is deleted", async function() {
|
|
const value = datastore.get("foo");
|
|
datastore.delete(value);
|
|
await datastore.save();
|
|
assert.equal(datastoreProvider.methodCalls.length, 2);
|
|
assert.deepEqual(datastoreProvider.methodCalls[1],
|
|
["save", new Map([...datastoreProvider.value.entries()].filter((v, k) => k !== value.id))]
|
|
);
|
|
});
|
|
it("does not break when the key does not exist", function() {
|
|
const value = { id: "test" };
|
|
datastore.delete(value);
|
|
assert.equal(eventsRaised.length, 1);
|
|
assert.deepEqual(eventsRaised[0], [value]);
|
|
});
|
|
it("sets the updatedAtMs property", function() {
|
|
const value = { id: "test" };
|
|
datastore.delete(value);
|
|
assert.notEqual(value.updatedAtMs, undefined);
|
|
assert(value.updatedAtMs > 0, "updatedAtMs not greater than zero");
|
|
});
|
|
it("increments the updatedAtMs property", function() {
|
|
const value = { id: "test" };
|
|
datastore.set(value);
|
|
const prev = value.updatedAtMs;
|
|
datastore.delete(value);
|
|
assert(value.updatedAtMs > prev, "updatedAtMs must increase");
|
|
});
|
|
it("increments the updatedAtMs property past existing values", function() {
|
|
const future = forceFuture();
|
|
const value = { id: "test" };
|
|
datastore.delete(value);
|
|
assert(value.updatedAtMs > future, "updatedAtMs must increase");
|
|
});
|
|
it("sets the isDeleted property", function() {
|
|
const value = { id: "test" };
|
|
datastore.delete(value);
|
|
assert.equal(value.isDeleted, true);
|
|
});
|
|
});
|
|
|
|
describe("deleteMany", function() {
|
|
it("does not save with zero changes", async function() {
|
|
datastore.deleteMany([]);
|
|
await datastore.save();
|
|
assert.equal(datastoreProvider.methodCalls.length, 1);
|
|
});
|
|
it("can delete many values at once", async function() {
|
|
const values = [{id: "foo"}, {id: "bar"}];
|
|
datastore.deleteMany(values);
|
|
assert.equal(datastore.get(values[0].id), undefined);
|
|
assert.equal(datastore.get(values[1].id), undefined);
|
|
await datastore.save();
|
|
assert.equal(eventsRaised.length, 1);
|
|
assert.deepEqual(eventsRaised[0], values);
|
|
assert.equal(datastoreProvider.methodCalls.length, 2);
|
|
assert.deepEqual(datastoreProvider.methodCalls[1],
|
|
["save", new Map([...datastoreProvider.value.entries()]
|
|
.filter((_, k) => !values.some(v => v.id === k)))]
|
|
);
|
|
});
|
|
it("sets the updatedAtMs property", function() {
|
|
const values = [{id: "foo"}, {id: "bar"}];
|
|
datastore.deleteMany(values);
|
|
assert.notEqual(values[0].updatedAtMs, undefined);
|
|
assert.notEqual(values[1].updatedAtMs, undefined);
|
|
assert(values[0].updatedAtMs > 0, "updatedAtMs greater than zero");
|
|
assert(values[1].updatedAtMs > 0, "updatedAtMs greater than zero");
|
|
});
|
|
it("increments the updatedAtMs property", function() {
|
|
const value = { id: "foo" };
|
|
datastore.set(value);
|
|
const prev = value.updatedAtMs;
|
|
const values = [{id: "foo", updatedAtMs: prev}, {id: "bar", updatedAtMs: prev}];
|
|
datastore.deleteMany(values);
|
|
assert(values[0].updatedAtMs > prev, "updatedAtMs must increase");
|
|
assert(values[1].updatedAtMs > prev, "updatedAtMs must increase");
|
|
});
|
|
it("increments the updatedAtMs property past existing values", function() {
|
|
const future = forceFuture();
|
|
const values = [{id: "foo"}, {id: "bar"}];
|
|
datastore.deleteMany(values);
|
|
assert(values[0].updatedAtMs > future, "updatedAtMs must increase");
|
|
assert(values[1].updatedAtMs > future, "updatedAtMs must increase");
|
|
});
|
|
it("sets the isDeleted property", function() {
|
|
const values = [{id: "foo"}, {id: "bar"}];
|
|
datastore.deleteMany(values);
|
|
assert.equal(values[0].isDeleted, true);
|
|
assert.equal(values[1].isDeleted, true);
|
|
});
|
|
});
|
|
});
|
|
});
|