Files
factorio-clusterio/plugins/statistics_exporter/test/plugin.js
T
bbassieandClaude Opus 5.5 11e067a933 Keep auto paused instances paused
Every RCON command makes an auto paused Factorio server run one tick, so
subspace_storage's 1 s inventory update and 5 s ping, plus a
statistics_exporter gather on every Prometheus scrape, kept a server with
nobody online running at about 1.3 ticks per second.

The instance now records the auto_pause server setting it was started
with and exposes autoPaused, true while that setting is on and no players
are online, along with an autoPauseChanged hook. subspace_storage skips
its timers while paused and sends a ping and inventory as soon as a player
joins. statistics_exporter gathers once after the game pauses and keeps
serving those values until it resumes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 23:40:30 +00:00

123 lines
4.0 KiB
JavaScript

import assert from "node:assert/strict";
import * as mock from "../../../test/mock.js";
import * as instance from "../dist/node/instance.js";
import { plugin as info } from "../dist/node/index.js";
describe("statistics_exporter plugin", function() {
describe("instance entrypoint", function() {
let mockInstance;
before(async function() {
({ instance: mockInstance } = await mock.loadInstancePlugin(instance.default, info));
mockInstance.mockConfigEntries.set("statistics_exporter.command_timeout", 1);
});
describe("metrics hook", function() {
it("should not send commands before running", async function() {
mockInstance.server.rconCommands.length = 0;
mockInstance.status = "starting";
await mockInstance.hooks.metrics.invoke();
assert(mockInstance.server.rconCommands.length === 0, "commands were run");
});
it("should record statistics", async function() {
mockInstance.status = "running";
mockInstance.server.rconCommandResults.set(
"/sc statistics_exporter.export()",
JSON.stringify({
game_tick: 100,
player_count: 3,
surface_statistics: {
"nauvis": {
game_flow_statistics: {
pollution_statistics: {
input: { "boiler": 2000 },
output: { "tree-proxy": 560 },
},
},
force_flow_statistics: {
player: {
item_production_statistics: {
input: { "inserter": 10 },
output: { "iron-plate": 24 },
},
},
},
},
},
platforms: {
"platform-1": {
force: "player",
surface: "nauvis",
speed: 1,
weight: 2,
},
},
})
);
await mockInstance.hooks.metrics.invoke();
assert.equal(instance._instancePlayerCount.labels("7357").get(), 3);
assert.equal(instance._instanceGameTicksTotal.labels("7357").get(), 100);
assert.equal(instance._instanceForceFlowStatistics.labels(
"7357", "nauvis", "player", "item_production_statistics", "input", "inserter").get(),
10);
assert.equal(instance._instanceForceFlowStatistics.labels(
"7357", "nauvis", "player", "item_production_statistics", "output", "iron-plate").get(),
24);
// Assert that it didn't record inserter consumption as production
assert.equal(instance._instanceForceFlowStatistics.labels(
"7357", "nauvis", "player", "item_production_statistics", "output", "inserter").get(),
0);
assert.equal(instance._instanceGameFlowStatistics.labels(
"7357", "nauvis", "pollution_statistics", "input", "boiler").get(),
2000);
assert.equal(instance._instanceGameFlowStatistics.labels(
"7357", "nauvis", "pollution_statistics", "output", "tree-proxy").get(),
560);
assert.equal(instance._instancePlatformMapping.labels(
"7357", "platform-1", "player", "nauvis").get(),
1);
assert.equal(instance._instancePlatformSpeed.labels(
"7357", "player", "nauvis").get(),
1);
assert.equal(instance._instancePlatformWeight.labels(
"7357", "player", "nauvis").get(),
2);
});
it("should gather once while auto paused", async function() {
mockInstance.server.rconCommands.length = 0;
mockInstance.autoPaused = true;
try {
await mockInstance.hooks.metrics.invoke();
await mockInstance.hooks.metrics.invoke();
assert.equal(mockInstance.server.rconCommands.length, 1);
} finally {
mockInstance.autoPaused = false;
}
await mockInstance.hooks.metrics.invoke();
assert.equal(mockInstance.server.rconCommands.length, 2);
});
it("should pass on JSON parse errors", async function() {
let string = "An error occured\n";
mockInstance.server.rconCommandResults.set(
"/sc statistics_exporter.export()", string
);
let errorMessage;
try {
JSON.parse(string);
} catch (err) {
errorMessage = err.message;
}
await assert.rejects(
instance.gatherMetrics(mockInstance, info.name),
new Error(`Error parsing statistics JSON: ${errorMessage}, content "${string}"`)
);
});
});
});
});