From afc1d35668447edc3c9522ee8582caae1ae3b48b Mon Sep 17 00:00:00 2001 From: bbassie <17990055+bbassie@users.noreply.github.com> Date: Wed, 28 Oct 2026 23:40:15 +0000 Subject: [PATCH] Load datastores with SubscribableDatastore.fromProvider clusterio#1129 replaced DatastoreProvider.bootstrap with async factories and Instance now takes its FactorioServer in the constructor, so the instance tests pass their server stub there. Co-Authored-By: Claude Opus 5.5 --- exp_discord_auth/controller.ts | 10 +++---- exp_groups/controller.ts | 30 +++++++++------------ exp_player_statistics/controller.ts | 10 +++---- exp_player_statistics/test/instance.test.js | 9 ++++--- exp_reports/controller.ts | 22 +++++++-------- exp_reports/test/instance.test.js | 10 +++---- exp_roles/controller.ts | 10 +++---- exp_roles/test/instance.test.js | 16 +++++------ exp_scenario/controller.ts | 20 ++++++-------- exp_scenario/test/instance.test.js | 10 +++---- exp_server_list/test/instance.test.js | 9 ++++--- 11 files changed, 69 insertions(+), 87 deletions(-) diff --git a/exp_discord_auth/controller.ts b/exp_discord_auth/controller.ts index 0d575cb5..d7921775 100644 --- a/exp_discord_auth/controller.ts +++ b/exp_discord_auth/controller.ts @@ -95,12 +95,10 @@ export class ControllerPlugin { const controller = context.controller; const databaseDirectory = controller.config.get("controller.database_directory"); - const accounts = new lib.SubscribableDatastore( - ...await new lib.JsonIdDatastoreProvider( - path.join(databaseDirectory, "exp_discord_auth", "accounts.json"), - messages.DiscordAccountRecord.fromJSON.bind(messages.DiscordAccountRecord), - ).bootstrap() - ); + const accounts = await lib.SubscribableDatastore.fromProvider(new lib.JsonIdDatastoreProvider( + path.join(databaseDirectory, "exp_discord_auth", "accounts.json"), + messages.DiscordAccountRecord.fromJSON.bind(messages.DiscordAccountRecord), + )); return new ControllerPlugin(controller, context.logger, context.plugin.name, accounts); } diff --git a/exp_groups/controller.ts b/exp_groups/controller.ts index 8e143dbd..ba5eb822 100644 --- a/exp_groups/controller.ts +++ b/exp_groups/controller.ts @@ -26,26 +26,20 @@ export class ControllerPlugin { const controller = context.controller; const databaseDirectory = controller.config.get("controller.database_directory"); - const groups = new lib.SubscribableDatastore( - ...await new lib.JsonIdDatastoreProvider( - path.join(databaseDirectory, "exp_groups", "groups.json"), - messages.GroupRecord.fromJSON.bind(messages.GroupRecord), - ).bootstrap() - ); + const groups = await lib.SubscribableDatastore.fromProvider(new lib.JsonIdDatastoreProvider( + path.join(databaseDirectory, "exp_groups", "groups.json"), + messages.GroupRecord.fromJSON.bind(messages.GroupRecord), + )); - const roleMappings = new lib.SubscribableDatastore( - ...await new lib.JsonIdDatastoreProvider( - path.join(databaseDirectory, "exp_groups", "role_mappings.json"), - messages.RoleMappingRecord.fromJSON.bind(messages.RoleMappingRecord), - ).bootstrap() - ); + const roleMappings = await lib.SubscribableDatastore.fromProvider(new lib.JsonIdDatastoreProvider( + path.join(databaseDirectory, "exp_groups", "role_mappings.json"), + messages.RoleMappingRecord.fromJSON.bind(messages.RoleMappingRecord), + )); - const manualAssignments = new lib.SubscribableDatastore( - ...await new lib.JsonIdDatastoreProvider( - path.join(databaseDirectory, "exp_groups", "assignments.json"), - messages.AssignmentRecord.fromJSON.bind(messages.AssignmentRecord), - ).bootstrap() - ); + const manualAssignments = await lib.SubscribableDatastore.fromProvider(new lib.JsonIdDatastoreProvider( + path.join(databaseDirectory, "exp_groups", "assignments.json"), + messages.AssignmentRecord.fromJSON.bind(messages.AssignmentRecord), + )); const plugin = new ControllerPlugin( controller, context.logger, context.plugin.name, groups, roleMappings, manualAssignments, diff --git a/exp_player_statistics/controller.ts b/exp_player_statistics/controller.ts index 815eb124..5460897e 100644 --- a/exp_player_statistics/controller.ts +++ b/exp_player_statistics/controller.ts @@ -7,12 +7,10 @@ import * as path from "node:path"; export default async function (context: ControllerPluginContext) { const { controller, plugin } = context; const databaseDirectory = controller.config.get("controller.database_directory"); - const players = new lib.SubscribableDatastore( - ...await new lib.JsonIdDatastoreProvider( - path.join(databaseDirectory, "exp_player_statistics", "players.json"), - messages.PlayerStatistics.fromJSON.bind(messages.PlayerStatistics), - ).bootstrap() - ); + const players = await lib.SubscribableDatastore.fromProvider(new lib.JsonIdDatastoreProvider( + path.join(databaseDirectory, "exp_player_statistics", "players.json"), + messages.PlayerStatistics.fromJSON.bind(messages.PlayerStatistics), + )); function addDeltas(deltas: messages.StatisticsDelta[]) { const changed = new Map(); diff --git a/exp_player_statistics/test/instance.test.js b/exp_player_statistics/test/instance.test.js index e7742994..fd6894f1 100644 --- a/exp_player_statistics/test/instance.test.js +++ b/exp_player_statistics/test/instance.test.js @@ -29,15 +29,16 @@ async function startPlugin(t2, { status = "running", stats = { Kills: 3 } } = {} instanceConfig.set("instance.id", 1); instanceConfig.set("instance.name", "test"); - const instance = new Instance( - { assignGamePort: () => 1 }, new TestConnector(), t2.testdir(), "factorioDir", instanceConfig - ); const state = { sent: [], rcons: [], warnings: [] }; const ipc = new Map(); - instance.server = { + const server = { + on: () => {}, handle: (name, handler) => ipc.set(name, handler), }; + const instance = new Instance( + { assignGamePort: () => 1 }, new TestConnector(), t2.testdir(), instanceConfig, server + ); instance.sendRcon = async command => { state.rcons.push(command); return ""; diff --git a/exp_reports/controller.ts b/exp_reports/controller.ts index 8d7e2a47..c33d57c6 100644 --- a/exp_reports/controller.ts +++ b/exp_reports/controller.ts @@ -11,19 +11,15 @@ function migrateReports(rawJson: unknown[]) { export default async function (context: ControllerPluginContext) { const { controller, logger, plugin } = context; const databaseDirectory = controller.config.get("controller.database_directory"); - const reports = new lib.SubscribableDatastore( - ...await new lib.JsonIdDatastoreProvider( - path.join(databaseDirectory, "exp_reports", "reports.json"), - messages.ReportRecord.fromJSON.bind(messages.ReportRecord), - migrateReports, - ).bootstrap() - ); - const watchList = new lib.SubscribableDatastore( - ...await new lib.JsonIdDatastoreProvider( - path.join(databaseDirectory, "exp_reports", "watch_list.json"), - messages.WatchListRecord.fromJSON.bind(messages.WatchListRecord), - ).bootstrap() - ); + const reports = await lib.SubscribableDatastore.fromProvider(new lib.JsonIdDatastoreProvider( + path.join(databaseDirectory, "exp_reports", "reports.json"), + messages.ReportRecord.fromJSON.bind(messages.ReportRecord), + migrateReports, + )); + const watchList = await lib.SubscribableDatastore.fromProvider(new lib.JsonIdDatastoreProvider( + path.join(databaseDirectory, "exp_reports", "watch_list.json"), + messages.WatchListRecord.fromJSON.bind(messages.WatchListRecord), + )); /** Every report, or only those against one player. */ function listReports(playerName?: string) { diff --git a/exp_reports/test/instance.test.js b/exp_reports/test/instance.test.js index 92ce2733..576e2e9a 100644 --- a/exp_reports/test/instance.test.js +++ b/exp_reports/test/instance.test.js @@ -31,20 +31,20 @@ async function startPlugin(t2, { reports = [report(1, "bob", "alice"), report(2, instanceConfig.set("instance.id", 1); instanceConfig.set("instance.name", "test"); - const instance = new Instance( - { assignGamePort: () => 1 }, new TestConnector(), t2.testdir(), "factorioDir", instanceConfig - ); - // Spies which record the messages and commands leaving the instance const state = { sent: [], rcons: [], failNext: null }; const ipc = new Map(); - instance.server = { + const server = { + on: () => {}, handle: (name, handler) => ipc.set(name, handler), sendRcon: async command => { state.rcons.push(command); return ""; }, }; + const instance = new Instance( + { assignGamePort: () => 1 }, new TestConnector(), t2.testdir(), instanceConfig, server + ); instance.sendTo = async (dst, request) => { state.sent.push(request); if (state.failNext) { diff --git a/exp_roles/controller.ts b/exp_roles/controller.ts index fb317696..bb14fc38 100644 --- a/exp_roles/controller.ts +++ b/exp_roles/controller.ts @@ -22,12 +22,10 @@ export class ControllerPlugin { const controller = context.controller; const databaseDirectory = controller.config.get("controller.database_directory"); - const roleMeta = new lib.SubscribableDatastore( - ...await new lib.JsonIdDatastoreProvider( - path.join(databaseDirectory, "exp_roles", "role_meta.json"), - messages.RoleMetaRecord.fromJSON.bind(messages.RoleMetaRecord), - ).bootstrap() - ); + const roleMeta = await lib.SubscribableDatastore.fromProvider(new lib.JsonIdDatastoreProvider( + path.join(databaseDirectory, "exp_roles", "role_meta.json"), + messages.RoleMetaRecord.fromJSON.bind(messages.RoleMetaRecord), + )); const plugin = new ControllerPlugin(controller, context.logger, context.plugin.name, roleMeta); loaded.set(controller, plugin); diff --git a/exp_roles/test/instance.test.js b/exp_roles/test/instance.test.js index 6ed398b7..81fdf520 100644 --- a/exp_roles/test/instance.test.js +++ b/exp_roles/test/instance.test.js @@ -40,21 +40,21 @@ async function startPlugin(t2, { syncMode = "bidirectional", roles = sampleRoles instanceConfig.set("instance.name", "test"); instanceConfig.set("exp_roles.sync_mode", syncMode); - const instance = new Instance( - { assignGamePort: () => 1 }, new TestConnector(), t2.testdir(), "factorioDir", instanceConfig - ); - // Spies which record the messages and commands leaving the instance const state = { sent: [], rcons: [], warnings: [], failNext: null }; const handlers = new Map(); - instance.handle = (Event, handler) => handlers.set(Event, handler); - instance.server = { + const server = { + on: () => {}, handle: (name, handler) => handlers.set(name, handler), sendRcon: async command => { state.rcons.push(command); return ""; }, }; + const instance = new Instance( + { assignGamePort: () => 1 }, new TestConnector(), t2.testdir(), instanceConfig, server + ); + instance.handle = (Event, handler) => handlers.set(Event, handler); instance.sendTo = async (dst, request) => { state.sent.push(request); if (state.failNext) { @@ -96,10 +96,10 @@ t.test("entrypoint", async t2 => { const instanceConfig = new lib.InstanceConfig("host"); instanceConfig.set("instance.id", 1); instanceConfig.set("instance.name", "test"); + const server = { on: () => {}, handle: () => {} }; const instance = new Instance( - { assignGamePort: () => 1 }, new TestConnector(), t2.testdir(), "factorioDir", instanceConfig + { assignGamePort: () => 1 }, new TestConnector(), t2.testdir(), instanceConfig, server ); - instance.server = { handle: () => {} }; await entrypoint({ instance, host: {}, logger, plugin: { name: "exp_roles" } }); for (const hook of ["instanceConfigFieldChanged", "start"]) { t2.ok([...instance.hooks[hook].attached].includes("exp_roles"), `attached to ${hook}`); diff --git a/exp_scenario/controller.ts b/exp_scenario/controller.ts index 6c391805..f31dea35 100644 --- a/exp_scenario/controller.ts +++ b/exp_scenario/controller.ts @@ -12,18 +12,14 @@ import { SeedRole, SeedGroup, seedRoles, seedGroups, flattenSeedPermissions } fr export default async function (context: ControllerPluginContext) { const { controller, logger, plugin } = context; const databaseDirectory = controller.config.get("controller.database_directory"); - const featureStore = new lib.SubscribableDatastore( - ...await new lib.JsonIdDatastoreProvider( - path.join(databaseDirectory, "exp_scenario", "features.json"), - messages.FeatureRecord.fromJSON.bind(messages.FeatureRecord), - ).bootstrap() - ); - const playerStore = new lib.SubscribableDatastore( - ...await new lib.JsonIdDatastoreProvider( - path.join(databaseDirectory, "exp_scenario", "players.json"), - messages.PlayerRecord.fromJSON.bind(messages.PlayerRecord), - ).bootstrap() - ); + const featureStore = await lib.SubscribableDatastore.fromProvider(new lib.JsonIdDatastoreProvider( + path.join(databaseDirectory, "exp_scenario", "features.json"), + messages.FeatureRecord.fromJSON.bind(messages.FeatureRecord), + )); + const playerStore = await lib.SubscribableDatastore.fromProvider(new lib.JsonIdDatastoreProvider( + path.join(databaseDirectory, "exp_scenario", "players.json"), + messages.PlayerRecord.fromJSON.bind(messages.PlayerRecord), + )); type PlayerSettings = Pick; diff --git a/exp_scenario/test/instance.test.js b/exp_scenario/test/instance.test.js index 1b201bcb..692eff91 100644 --- a/exp_scenario/test/instance.test.js +++ b/exp_scenario/test/instance.test.js @@ -32,19 +32,19 @@ async function startPlugin( instanceConfig.set("instance.id", 1); instanceConfig.set("instance.name", "test"); - const instance = new Instance( - { assignGamePort: () => 1 }, new TestConnector(), t2.testdir(), "factorioDir", instanceConfig - ); - const state = { sent: [], rcons: [] }; const ipcHandlers = new Map(); - instance.server = { + const server = { + on: () => {}, handle: (channel, handler) => ipcHandlers.set(channel, handler), sendRcon: async command => { state.rcons.push(command); return ""; }, }; + const instance = new Instance( + { assignGamePort: () => 1 }, new TestConnector(), t2.testdir(), instanceConfig, server + ); instance.sendTo = async (dst, request) => { state.sent.push(request); if (request instanceof messages.PlayerGetRequest) { diff --git a/exp_server_list/test/instance.test.js b/exp_server_list/test/instance.test.js index 758ede46..58ffbf52 100644 --- a/exp_server_list/test/instance.test.js +++ b/exp_server_list/test/instance.test.js @@ -35,18 +35,19 @@ async function startPlugin(t2, servers = [server(1), server(2)]) { instanceConfig.set("instance.id", 1); instanceConfig.set("instance.name", "test"); - const instance = new Instance( - { assignGamePort: () => 1 }, new TestConnector(), t2.testdir(), "factorioDir", instanceConfig - ); const state = { sent: [], rcons: [] }; - instance.server = { + const server = { + on: () => {}, handle: () => {}, sendRcon: async command => { state.rcons.push(command); return ""; }, }; + const instance = new Instance( + { assignGamePort: () => 1 }, new TestConnector(), t2.testdir(), instanceConfig, server + ); instance.sendTo = async (dst, request) => { if (request instanceof lib.InstanceStatusChangedEvent) { return undefined;