mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 23:04:52 +09:00
* 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>
268 lines
8.0 KiB
JavaScript
268 lines
8.0 KiB
JavaScript
"use strict";
|
|
const assert = require("assert").strict;
|
|
const { UserDetails, PlayerStats } = require("@clusterio/lib");
|
|
|
|
const { testMatrix, testRoundTripJsonSerialisable } = require("../../common");
|
|
|
|
describe("lib/data/UserDetails", function () {
|
|
it("should be round trip serialisable", function () {
|
|
testRoundTripJsonSerialisable(UserDetails, testMatrix(
|
|
["TestUser"], // name (required)
|
|
[undefined, new Set([1])], // roleIds
|
|
[undefined, new Set([2])], // instances
|
|
[undefined, true], // isAdmin
|
|
[undefined, true], // isBanned
|
|
[undefined, true], // isWhitelisted
|
|
[undefined, "reason"], // banReason
|
|
[undefined, 123], // updatedAtMs
|
|
[undefined, true], // isDeleted
|
|
[ // instanceStats
|
|
undefined,
|
|
new Map([
|
|
[1, PlayerStats.fromJSON({ join_count: 1, online_time_ms: 100 })],
|
|
]),
|
|
],
|
|
));
|
|
});
|
|
|
|
describe("constructor", function () {
|
|
it("should set defaults correctly", function () {
|
|
const user = new UserDetails("TestUser");
|
|
|
|
assert.equal(user.name, "TestUser");
|
|
assert.equal(user.roleIds.size, 0);
|
|
assert.equal(user.instances.size, 0);
|
|
assert.equal(user.isAdmin, false);
|
|
assert.equal(user.isBanned, false);
|
|
assert.equal(user.isWhitelisted, false);
|
|
assert.equal(user.banReason, "");
|
|
assert.equal(user.updatedAtMs, 0);
|
|
assert.equal(user.isDeleted, false);
|
|
assert(user.playerStats instanceof PlayerStats);
|
|
assert(user.instanceStats instanceof Map);
|
|
});
|
|
|
|
it("should accept all constructor parameters", function () {
|
|
const user = new UserDetails(
|
|
"TestUser", new Set([1]), new Set([2]),
|
|
true, true, true, "reason",
|
|
100, true,
|
|
new Map()
|
|
);
|
|
|
|
assert.equal(user.name, "TestUser");
|
|
assert.deepEqual(user.roleIds, new Set([1]));
|
|
assert.deepEqual(user.instances, new Set([2]));
|
|
assert.equal(user.isAdmin, true);
|
|
assert.equal(user.isBanned, true);
|
|
assert.equal(user.isWhitelisted, true);
|
|
assert.equal(user.banReason, "reason");
|
|
assert.equal(user.updatedAtMs, 100);
|
|
assert.equal(user.isDeleted, true);
|
|
});
|
|
});
|
|
|
|
describe("get .id", function () {
|
|
it("should return lowercase name", function () {
|
|
const user = new UserDetails("AdMiN");
|
|
assert.equal(user.id, "admin");
|
|
});
|
|
|
|
it("should reflect name changes dynamically", function () {
|
|
const user = new UserDetails("User");
|
|
user.name = "NEWNAME";
|
|
assert.equal(user.id, "newname");
|
|
});
|
|
});
|
|
|
|
describe(".toJSON()", function () {
|
|
it("should only include required fields when optional fields are not given", function () {
|
|
const user = new UserDetails("Test");
|
|
assert.deepEqual(user.toJSON(), { name: "Test" });
|
|
});
|
|
|
|
it("should include all optional fields when set", function () {
|
|
const stats = new PlayerStats({ join_count: 1 });
|
|
const user = new UserDetails(
|
|
"Test", new Set([1, 2]), new Set([10]),
|
|
true, true, true, "reason",
|
|
123, true,
|
|
new Map([[1, stats]])
|
|
);
|
|
|
|
assert.deepEqual(user.toJSON(), {
|
|
name: "Test",
|
|
roles: [1, 2],
|
|
instances: [10],
|
|
is_admin: true,
|
|
is_banned: true,
|
|
is_whitelisted: true,
|
|
ban_reason: "reason",
|
|
updated_at_ms: 123,
|
|
is_deleted: true,
|
|
instance_stats: [[1, stats.toJSON()]],
|
|
});
|
|
});
|
|
|
|
it("should omit optional fields when equal to defaults", function () {
|
|
const user = new UserDetails(
|
|
"Test", new Set(), new Set(),
|
|
false, false, false, "",
|
|
0, false,
|
|
new Map()
|
|
);
|
|
|
|
assert.deepEqual(user.toJSON(), { name: "Test" });
|
|
});
|
|
});
|
|
|
|
describe("static fromJSON()", function () {
|
|
it("should construct minimal object", function () {
|
|
const user = UserDetails.fromJSON({ name: "Test" });
|
|
|
|
assert.equal(user.name, "Test");
|
|
assert.deepEqual(user.roleIds, new Set());
|
|
assert.deepEqual(user.instances, new Set());
|
|
assert.deepEqual(user.isAdmin, false);
|
|
assert.equal(user.isBanned, false);
|
|
assert.equal(user.isWhitelisted, false);
|
|
assert.equal(user.banReason, "");
|
|
assert.equal(user.updatedAtMs, 0);
|
|
assert.equal(user.isDeleted, false);
|
|
assert.deepEqual(user.instanceStats, new Map());
|
|
});
|
|
|
|
it("should preserve optional fields exactly when provided", function () {
|
|
const stats = new PlayerStats({ join_count: 1 });
|
|
const user = UserDetails.fromJSON({
|
|
name: "Test",
|
|
roles: [1, 2],
|
|
instances: [10],
|
|
is_admin: true,
|
|
is_banned: true,
|
|
is_whitelisted: true,
|
|
ban_reason: "reason",
|
|
updated_at_ms: 123,
|
|
is_deleted: true,
|
|
instance_stats: [[1, stats.toJSON()]],
|
|
});
|
|
|
|
assert.equal(user.name, "Test");
|
|
assert.deepEqual(user.roleIds, new Set([1, 2]));
|
|
assert.deepEqual(user.instances, new Set([10]));
|
|
assert.deepEqual(user.isAdmin, true);
|
|
assert.equal(user.isBanned, true);
|
|
assert.equal(user.isWhitelisted, true);
|
|
assert.equal(user.banReason, "reason");
|
|
assert.equal(user.updatedAtMs, 123);
|
|
assert.equal(user.isDeleted, true);
|
|
assert.deepEqual(user.instanceStats, new Map([[1, stats]]));
|
|
});
|
|
|
|
it("should preserve roles exactly (including invalid ones)", function () {
|
|
const user = UserDetails.fromJSON({
|
|
name: "Test",
|
|
roles: [1, 4, 55],
|
|
});
|
|
|
|
assert.equal(user.roleIds.size, 3);
|
|
assert(user.roleIds.has(55));
|
|
});
|
|
|
|
it("should reconstruct instanceStats and calculate playerStats", function () {
|
|
const json = {
|
|
name: "Test",
|
|
instance_stats: [
|
|
[1, { join_count: 1, online_time_ms: 100 }],
|
|
[2, { join_count: 2, online_time_ms: 200 }],
|
|
],
|
|
};
|
|
|
|
const user = UserDetails.fromJSON(json);
|
|
|
|
assert.equal(user.instanceStats.size, 2);
|
|
assert.equal(user.playerStats.joinCount, 3);
|
|
assert.equal(user.playerStats.onlineTimeMs, 300);
|
|
});
|
|
});
|
|
|
|
describe(".recalculatePlayerStats()", function () {
|
|
it("should aggregate stats correctly across multiple instances", function () {
|
|
const user = UserDetails.fromJSON({
|
|
name: "test",
|
|
instance_stats: [
|
|
[1, {
|
|
join_count: 1,
|
|
online_time_ms: 60e3,
|
|
last_join_at_ms: new Date("2020-05-05T12:00Z").getTime(),
|
|
last_leave_at_ms: new Date("2020-05-05T12:01Z").getTime(),
|
|
last_leave_reason: "quit",
|
|
}],
|
|
[2, {
|
|
join_count: 1,
|
|
online_time_ms: 0,
|
|
first_join_at_ms: new Date("2020-05-05T12:00Z").getTime(),
|
|
last_join_at_ms: new Date("2020-05-05T12:02Z").getTime(),
|
|
}],
|
|
],
|
|
});
|
|
|
|
assert.equal(user.playerStats.joinCount, 2);
|
|
assert.equal(user.playerStats.onlineTimeMs, 60e3);
|
|
|
|
user.instanceStats.set(3, PlayerStats.fromJSON({
|
|
join_count: 2,
|
|
online_time_ms: 120e3,
|
|
first_join_at_ms: new Date("2020-05-05T11:00Z").getTime(),
|
|
last_join_at_ms: new Date("2020-05-05T13:00Z").getTime(),
|
|
last_leave_at_ms: new Date("2020-05-05T13:02Z").getTime(),
|
|
last_leave_reason: "afk",
|
|
}));
|
|
|
|
user.recalculatePlayerStats();
|
|
|
|
assert.equal(user.playerStats.joinCount, 4);
|
|
assert.equal(user.playerStats.onlineTimeMs, 180e3);
|
|
});
|
|
});
|
|
|
|
describe("static _calculatePlayerStats()", function () {
|
|
it("should return empty stats for empty map", function () {
|
|
const stats = UserDetails._calculatePlayerStats(new Map());
|
|
assert.equal(stats.joinCount, 0);
|
|
assert.equal(stats.onlineTimeMs, 0);
|
|
});
|
|
|
|
it("should pick earliest firstJoin and latest joins/leaves", function () {
|
|
const a = PlayerStats.fromJSON({
|
|
first_join_at_ms: new Date("2020-01-01").getTime(),
|
|
last_join_at_ms: new Date("2020-01-02").getTime(),
|
|
last_leave_at_ms: new Date("2020-01-02").getTime(),
|
|
last_leave_reason: "quit",
|
|
join_count: 1,
|
|
online_time_ms: 100,
|
|
});
|
|
|
|
const b = PlayerStats.fromJSON({
|
|
first_join_at_ms: new Date("2019-01-01").getTime(),
|
|
last_join_at_ms: new Date("2021-01-01").getTime(),
|
|
last_leave_at_ms: new Date("2021-01-02").getTime(),
|
|
last_leave_reason: "afk",
|
|
join_count: 2,
|
|
online_time_ms: 200,
|
|
});
|
|
|
|
const stats = UserDetails._calculatePlayerStats(
|
|
new Map([[1, a], [2, b]])
|
|
);
|
|
|
|
assert.equal(stats.joinCount, 3);
|
|
assert.equal(stats.onlineTimeMs, 300);
|
|
assert.deepEqual(stats.firstJoinAt, new Date("2019-01-01"));
|
|
assert.deepEqual(stats.lastJoinAt, new Date("2021-01-01"));
|
|
assert.deepEqual(stats.lastLeaveAt, new Date("2021-01-02"));
|
|
assert.equal(stats.lastLeaveReason, "afk");
|
|
});
|
|
});
|
|
});
|