mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-12 15:04:52 +09:00
Refactor user and instance manager (#837)
* Refactor UserManager to use Datastore * Rename lib.User to UserDetails also adds to/fromUserDetails * Fix existing tests * Add user class tests * Add tests for finaliseUsers * Move HostDetails to own file with tests * Move InstanceDetails to its own file with tests * Rename HostInfo and add tests * Rename InstanceInfo and add tests - Also changes the names of the host properties related to instances - Also removes the need to specify the location of InstanceInfo * Make json round trip test validate schema * Add InstanceManager * Fix lint errors * Extra tests * Add final test coverage * Add one last test to InstanceManager * Response to comments * Use generator rather than return anonumous generator Co-authored-by: Hornwitser <github@hornwitser.no> * Fix lint issue with generator --------- Co-authored-by: Hornwitser <github@hornwitser.no>
This commit is contained in:
1 parent
5bea930d05
commit
2235294c59
60 files changed
+3371
-1227
No files matched your search
@@ -0,0 +1,358 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const lib = require("@clusterio/lib");
|
||||
|
||||
const { Controller, InstanceRecord, InstanceManager } = require("@clusterio/controller");
|
||||
|
||||
describe("controller/InstanceManager", function () {
|
||||
/** @type {Controller} */
|
||||
let controller;
|
||||
/** @type {InstanceManager} */
|
||||
let instances;
|
||||
/** @type {InstanceRecord} */
|
||||
let instance;
|
||||
|
||||
beforeEach(function () {
|
||||
const controllerConfig = new lib.ControllerConfig("controller", {
|
||||
"controller.name": "TestController",
|
||||
});
|
||||
const instanceConfig = new lib.InstanceConfig("controller", {
|
||||
"instance.id": 1,
|
||||
"instance.name": "Test",
|
||||
});
|
||||
|
||||
const datastoreInstances = new lib.SubscribableDatastore();
|
||||
datastoreInstances.set(new InstanceRecord(instanceConfig, "unassigned"));
|
||||
|
||||
controller = new Controller(lib.logger, [], controllerConfig,
|
||||
undefined, undefined, undefined, undefined, datastoreInstances);
|
||||
instances = controller.instances;
|
||||
instance = instances.getMutable(1);
|
||||
});
|
||||
|
||||
describe("constructor()", function () {
|
||||
it("should call _notifyConfigFieldChanged when config changes", async function () {
|
||||
let called = false;
|
||||
|
||||
instances._notifyConfigFieldChanged = async (
|
||||
inst, field, curr, prev
|
||||
) => {
|
||||
called = true;
|
||||
assert.equal(inst.id, 1);
|
||||
assert.equal(field, "instance.name");
|
||||
assert.equal(prev, "Test");
|
||||
assert.equal(curr, "Updated");
|
||||
};
|
||||
|
||||
instance.config.set("instance.name", "Updated");
|
||||
|
||||
assert.equal(called, true);
|
||||
});
|
||||
|
||||
it("should update datastore when config changes", function () {
|
||||
let setCalled = false;
|
||||
|
||||
const originalSet = instances.records.set.bind(instances.records);
|
||||
instances.records.set = function (value) {
|
||||
setCalled = true;
|
||||
return originalSet(value);
|
||||
};
|
||||
|
||||
instance.config.set("instance.name", "Changed");
|
||||
|
||||
assert.equal(setCalled, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe(".has() / .get() / .getMutable()", function () {
|
||||
it("should return instance when exists", async function () {
|
||||
assert.equal(instances.has(instance.id), true);
|
||||
assert.equal(instances.get(instance.id), instance);
|
||||
assert.equal(instances.getMutable(instance.id), instance);
|
||||
});
|
||||
|
||||
it("should return undefined when missing", function () {
|
||||
assert.equal(instances.has(999), false);
|
||||
assert.equal(instances.get(999), undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe(".values() / .valuesMutable()", function () {
|
||||
it("should iterate all instances", async function () {
|
||||
const config1 = new lib.InstanceConfig("controller", {
|
||||
"instance.id": 2,
|
||||
"instance.name": "A",
|
||||
});
|
||||
const config2 = new lib.InstanceConfig("controller", {
|
||||
"instance.id": 3,
|
||||
"instance.name": "B",
|
||||
});
|
||||
|
||||
const i1 = await instances.createInstance(config1);
|
||||
const i2 = await instances.createInstance(config2);
|
||||
|
||||
assert.deepEqual(Array.from(instances.values()), [instance, i1, i2]);
|
||||
assert.deepEqual(Array.from(instances.valuesMutable()), [instance, i1, i2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe(".getForRequest()", function () {
|
||||
it("should return instance when exists", async function () {
|
||||
assert.equal(instances.getForRequest(1), instance);
|
||||
});
|
||||
|
||||
it("should throw when missing", function () {
|
||||
assert.throws(() => instances.getForRequest(999), lib.RequestError);
|
||||
});
|
||||
});
|
||||
|
||||
describe(".createInstance()", function () {
|
||||
it("should create new instance with id 2", async function () {
|
||||
let notified = false;
|
||||
instances._notifyStatusChanged = async () => { notified = true; };
|
||||
|
||||
const config = new lib.InstanceConfig("controller", {
|
||||
"instance.id": 2,
|
||||
"instance.name": "Second",
|
||||
});
|
||||
|
||||
const created = await instances.createInstance(config);
|
||||
|
||||
assert.equal(created.id, 2);
|
||||
assert(instances.get(2), created);
|
||||
assert.equal(notified, true);
|
||||
});
|
||||
|
||||
it("should call notify on config change", async function () {
|
||||
let notified = false;
|
||||
instances._notifyConfigFieldChanged = async () => { notified = true; };
|
||||
|
||||
const config = new lib.InstanceConfig("controller", {
|
||||
"instance.id": 2,
|
||||
"instance.name": "Second",
|
||||
});
|
||||
|
||||
const created = await instances.createInstance(config);
|
||||
|
||||
created.config.set("instance.name", "Updated");
|
||||
|
||||
assert.equal(created.id, 2);
|
||||
assert.equal(instances.get(2), created);
|
||||
assert.equal(notified, true);
|
||||
});
|
||||
|
||||
it("should throw if id already exists", async function () {
|
||||
const config = new lib.InstanceConfig("controller", {
|
||||
"instance.id": 1,
|
||||
"instance.name": "Duplicate",
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
() => instances.createInstance(config),
|
||||
lib.RequestError
|
||||
);
|
||||
});
|
||||
|
||||
it("should suppress changes when flag set", async function () {
|
||||
let notified = false;
|
||||
instances._notifyStatusChanged = async () => { notified = true; };
|
||||
|
||||
const config = new lib.InstanceConfig("controller", {
|
||||
"instance.id": 2,
|
||||
"instance.name": "Suppressed",
|
||||
});
|
||||
|
||||
const created = await instances.createInstance(config, true);
|
||||
|
||||
assert.equal(instances.get(2), undefined);
|
||||
assert.equal(notified, false);
|
||||
});
|
||||
});
|
||||
|
||||
describe(".assignInstance()", function () {
|
||||
it("should throw if host not connected", async function () {
|
||||
await assert.rejects(
|
||||
() => instances.assignInstance(1, 5),
|
||||
lib.RequestError
|
||||
);
|
||||
});
|
||||
|
||||
it("should do nothing if the host would not change", async function () {
|
||||
let assignRequest;
|
||||
controller.wsServer.hostConnections.set(5, {
|
||||
send: async (request) => {
|
||||
assignRequest = request;
|
||||
},
|
||||
connector: { closing: false },
|
||||
});
|
||||
|
||||
instance.config.set("instance.assigned_host", 5);
|
||||
|
||||
await instances.assignInstance(1, 5);
|
||||
assert.equal(assignRequest, undefined);
|
||||
});
|
||||
|
||||
it("should not unassign if new host is not connected", async function () {
|
||||
let unassignRequest;
|
||||
controller.wsServer.hostConnections.set(5, {
|
||||
send: async (request) => {
|
||||
unassignRequest = request;
|
||||
},
|
||||
connector: { closing: false },
|
||||
});
|
||||
|
||||
instance.config.set("instance.assigned_host", 5);
|
||||
|
||||
await assert.rejects(
|
||||
() => instances.assignInstance(1, 6),
|
||||
lib.RequestError
|
||||
);
|
||||
|
||||
assert.equal(unassignRequest, undefined);
|
||||
});
|
||||
|
||||
it("should assign to connected host", async function () {
|
||||
let assignRequest;
|
||||
controller.wsServer.hostConnections.set(5, {
|
||||
send: async (request) => {
|
||||
assignRequest = request;
|
||||
},
|
||||
connector: { closing: false },
|
||||
});
|
||||
|
||||
await instances.assignInstance(1, 5);
|
||||
|
||||
assert.equal(instance.config.get("instance.assigned_host"), 5);
|
||||
assert(assignRequest instanceof lib.InstanceAssignInternalRequest);
|
||||
assert.equal(assignRequest.instanceId, 1);
|
||||
});
|
||||
|
||||
it("should not fail when host has id of 0", async function () {
|
||||
let lastEvent;
|
||||
controller.wsServer.hostConnections.set(0, {
|
||||
send: async (request) => {
|
||||
lastEvent = request;
|
||||
},
|
||||
connector: { closing: false },
|
||||
});
|
||||
|
||||
await instances.assignInstance(1, 0);
|
||||
|
||||
assert.equal(instance.config.get("instance.assigned_host"), 0);
|
||||
assert(lastEvent instanceof lib.InstanceAssignInternalRequest);
|
||||
assert.equal(lastEvent.instanceId, 1);
|
||||
|
||||
await instances.assignInstance(1, null);
|
||||
|
||||
assert.equal(instance.config.get("instance.assigned_host"), null);
|
||||
assert(lastEvent instanceof lib.InstanceUnassignInternalRequest);
|
||||
assert.equal(lastEvent.instanceId, 1);
|
||||
});
|
||||
|
||||
it("should unassign old host if connected", async function () {
|
||||
let unassignRequest;
|
||||
controller.wsServer.hostConnections.set(5, {
|
||||
send: async (request) => {
|
||||
unassignRequest = request;
|
||||
},
|
||||
connector: { closing: false },
|
||||
});
|
||||
|
||||
let assignRequest;
|
||||
controller.wsServer.hostConnections.set(6, {
|
||||
send: async (request) => {
|
||||
assignRequest = request;
|
||||
},
|
||||
connector: { closing: false },
|
||||
});
|
||||
|
||||
instance.config.set("instance.assigned_host", 5);
|
||||
await instances.assignInstance(1, 6);
|
||||
|
||||
assert(unassignRequest instanceof lib.InstanceUnassignInternalRequest);
|
||||
assert.equal(unassignRequest.instanceId, 1);
|
||||
assert(assignRequest instanceof lib.InstanceAssignInternalRequest);
|
||||
assert.equal(assignRequest.instanceId, 1);
|
||||
});
|
||||
|
||||
it("should not unassign if old host closing", async function () {
|
||||
let unassignRequest;
|
||||
controller.wsServer.hostConnections.set(5, {
|
||||
send: async (request) => {
|
||||
unassignRequest = request;
|
||||
},
|
||||
connector: { closing: true },
|
||||
});
|
||||
|
||||
let assignRequest;
|
||||
controller.wsServer.hostConnections.set(6, {
|
||||
send: async (request) => {
|
||||
assignRequest = request;
|
||||
},
|
||||
connector: { closing: false },
|
||||
});
|
||||
|
||||
instance.config.set("instance.assigned_host", 5);
|
||||
await instances.assignInstance(1, 6);
|
||||
|
||||
assert.equal(unassignRequest, undefined);
|
||||
assert(assignRequest instanceof lib.InstanceAssignInternalRequest);
|
||||
assert.equal(assignRequest.instanceId, 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe(".unassignInstance()", function () {
|
||||
it("should unassign an instance", async function () {
|
||||
controller.wsServer.hostConnections.set(5, {
|
||||
send: async () => {},
|
||||
connector: { closing: true },
|
||||
});
|
||||
|
||||
await instances.assignInstance(instance.id, 5);
|
||||
await instances.unassignInstance(instance.id);
|
||||
assert.equal(instance.config.get("instance.assigned_host"), null);
|
||||
assert.equal(instance.status, "unassigned");
|
||||
});
|
||||
it("should do nothing if the host would not change", async function () {
|
||||
instance.config.set("instance.assigned_host", null);
|
||||
|
||||
let fieldChanged;
|
||||
instance.config.on("fieldChanged", name => {
|
||||
fieldChanged = name;
|
||||
});
|
||||
|
||||
await instances.unassignInstance(1);
|
||||
assert.equal(fieldChanged, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe(".deleteInstance()", function () {
|
||||
it("should delete and notify", async function () {
|
||||
let notified = false;
|
||||
instances._notifyStatusChanged = async () => { notified = true; };
|
||||
|
||||
await instances.deleteInstance(1);
|
||||
|
||||
assert.equal(instances.get(1), undefined);
|
||||
assert.equal(notified, true);
|
||||
});
|
||||
|
||||
it("should send delete to host if assigned", async function () {
|
||||
let sentRequest;
|
||||
controller.sendTo = async (_target, request) => {
|
||||
sentRequest = request;
|
||||
};
|
||||
|
||||
controller.wsServer.hostConnections.set(5, {
|
||||
send: async () => {},
|
||||
connector: { closing: false },
|
||||
});
|
||||
|
||||
await instances.assignInstance(1, 5);
|
||||
await instances.deleteInstance(1);
|
||||
|
||||
assert(sentRequest instanceof lib.InstanceDeleteInternalRequest);
|
||||
assert.equal(sentRequest.instanceId, 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user