Files
factorio-clusterio/test/controller/User.test.js
T
Cooldude2606andHornwitser 2235294c59 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>
2026-03-01 23:04:37 +00:00

157 lines
4.7 KiB
JavaScript

"use strict";
const assert = require("assert").strict;
const { Role, SubscribableDatastore } = require("@clusterio/lib");
const { User } = require("@clusterio/controller");
describe("controller/User", function () {
/** @type {SubscribableDatastore<UserRecord>} */
let users;
/** @type {SubscribableDatastore<Role>} */
let roles;
/** @type {User} */
let user;
beforeEach(function () {
users = new SubscribableDatastore();
roles = new SubscribableDatastore();
// Create a role and user used in most tests
roles.set(Role.fromJSON({ id: 1, name: "connectRole", permissions: ["core.control.connect"] }));
user = User.fromJSON({ name: "Alice", roles: [1] }, users, roles);
users.set(user);
user.updatedAtMs = 1000; // Known low value
});
describe("get roles", function () {
it("should return roles from _controllerRoles", function () {
const roleSet = user.roles;
assert.equal(roleSet.size, 1);
assert.equal([...roleSet][0].id, 1);
});
});
describe(".saveRecord() / set", function () {
it("should update updatedAtMs", function () {
const prev = user.updatedAtMs;
user.saveRecord();
assert(user.updatedAtMs > prev);
});
});
describe(".set()", function () {
it("should update updatedAtMs", function () {
const prev = user.updatedAtMs;
user.set("isAdmin", true);
assert.equal(user.isAdmin, true);
assert(user.updatedAtMs > prev);
});
});
describe(".invalidateToken()", function () {
it("should update tokenValidAfter to current time", function () {
const prevValid = user.tokenValidAfter;
const prevUpdate = user.updatedAtMs;
user.invalidateToken();
assert(user.tokenValidAfter > prevValid);
assert(user.updatedAtMs > prevUpdate);
});
});
describe(".addRole()", function () {
it("should add role if exists", function () {
roles.set(Role.fromJSON({ id: 2, name: "admin", permissions: ["core.admin"] }));
const prev = user.updatedAtMs;
user.addRole(2);
assert(user.roleIds.has(2));
assert(user.updatedAtMs > prev);
});
it("should throw if role does not exist", function () {
assert.throws(() => user.addRole(999), /role 999 does not exist/);
});
});
describe(".removeRole()", function () {
it("should remove existing role", function () {
const prev = user.updatedAtMs;
const result = user.removeRole(1);
assert(result);
assert(!user.roleIds.has(1));
assert(user.updatedAtMs > prev);
});
it("should return false if role was not assigned", function () {
const prev = user.updatedAtMs;
const result = user.removeRole(999);
assert.equal(result, false);
assert(user.updatedAtMs === prev); // unchanged
});
});
describe(".notifyJoin() / .notifyLeave()", function () {
it("should add instance on notifyJoin", function () {
const prev = user.updatedAtMs;
user.notifyJoin(10);
assert(user.instances.has(10));
assert(user.updatedAtMs > prev);
});
it("should remove instance on notifyLeave", function () {
user.notifyJoin(10);
user.updatedAtMs = 1000; // Known low value
user.notifyLeave(10);
assert(!user.instances.has(10));
assert(user.updatedAtMs > 1000);
});
});
describe(".clearInstanceStats()", function () {
it("should remove instanceStats and recalculate playerStats", function () {
const instanceId = 5;
const userWithStats = User.fromJSON({
name: "Bob",
instanceStats: [[instanceId, { join_count: 1 }]],
}, users, roles);
users.set(userWithStats);
userWithStats.updatedAtMs = 1000; // Known low value
userWithStats.clearInstanceStats(instanceId);
assert(!userWithStats.instanceStats.has(instanceId));
assert(userWithStats.updatedAtMs > 1000);
});
});
describe(".checkPermission()", function () {
it("should allow if role has core.control.connect", function () {
assert.doesNotThrow(() => user.checkPermission("core.control.connect"));
});
it("should allow if role has core.admin", function () {
roles.set(Role.fromJSON({ id: 2, name: "admin", permissions: ["core.admin"] }));
const adminUser = User.fromJSON({ name: "Charlie", roles: [2] }, users, roles);
assert.doesNotThrow(() => adminUser.checkPermission("core.admin"));
});
it("should not error if user has invalid role", function () {
user.roleIds = {
[Symbol.iterator]: function* () {
// We need the invalid id (99) to be before the valid id (1)
for (const roleId of [99, 1]) { yield roleId; }
},
};
assert.doesNotThrow(() => user.checkPermission("core.control.connect"));
});
it("should throw if permission not granted", function () {
assert.throws(() => user.checkPermission("core.admin"), /Permission denied/);
});
it("should throw if permission does not exist", function () {
assert.throws(() => user.checkPermission("nonexistent.permission"), /does not exist/);
});
});
});