Add Prettier and ESLint tooling (Refs #77)

This commit is contained in:
2026-10-09 15:56:13 -04:00
parent 8ed059b8d3
commit 16469e2d43
711 changed files with 99737 additions and 28286 deletions
+112 -53
View File
@@ -4,85 +4,144 @@ import { prepareNativeHost } from '../helpers/native-host.js';
import { zombieMaps } from '../../src/lib/map-aliases.js';
let fixture: Fixture;
beforeEach(async () => { vi.resetModules(); fixture = await newFixture([]); });
afterEach(async () => { vi.useRealTimers(); await fixture.cleanup(); });
beforeEach(async () => {
vi.resetModules();
fixture = await newFixture([]);
});
afterEach(async () => {
vi.useRealTimers();
await fixture.cleanup();
});
const server = 't6_zombie_town_1';
async function setup(mapSlug: string) {
const maps = zombieMaps('t6').filter(map => mapSlug === '*' || map.slug === mapSlug);
const names = maps.map(map => `t6_zombie_${map.slug}_1`);
const maps = zombieMaps('t6').filter((map) => mapSlug === '*' || map.slug === mapSlug);
const names = maps.map((map) => `t6_zombie_${map.slug}_1`);
const { host } = await prepareNativeHost(names);
const { db } = await import('../../src/lib/server/db/index.js');
const scaling = await import('../../src/lib/server/autoscaling/index.js');
const { autoscalingRemovalCapability } = await import('../../src/lib/server/autoscaling/authorization.js');
db().prepare("UPDATE ec_hosts SET inventory=json_set(inventory,'$.capabilities',json(?)) WHERE id=?")
const { autoscalingRemovalCapability } =
await import('../../src/lib/server/autoscaling/authorization.js');
db()
.prepare(
"UPDATE ec_hosts SET inventory=json_set(inventory,'$.capabilities',json(?)) WHERE id=?"
)
.run(JSON.stringify([autoscalingRemovalCapability]), host.id);
const at = new Date().toISOString();
for (const map of maps) {
const name = `t6_zombie_${map.slug}_1`;
db().prepare(`INSERT INTO servers(name,game,map,map_slug,container_state,container_checked_at,last_seen_at,last_checked_at,last_players)
VALUES(?,'t6',?,?,'running',?,?,?,0)`).run(name, map.canonical, map.slug, at, at, at);
db().prepare("INSERT INTO server_keys(id,game,pluto_key) VALUES(?,'t6',?)").run(name, `key-${name}`);
db()
.prepare(
`INSERT INTO servers(name,game,map,map_slug,container_state,container_checked_at,last_seen_at,last_checked_at,last_players)
VALUES(?,'t6',?,?,'running',?,?,?,0)`
)
.run(name, map.canonical, map.slug, at, at, at);
db()
.prepare("INSERT INTO server_keys(id,game,pluto_key) VALUES(?,'t6',?)")
.run(name, `key-${name}`);
db().prepare('INSERT INTO server_key_leases(server_id,key_id) VALUES(?,?)').run(name, name);
}
// A free key ensures a lack of capacity cannot hide unwanted scale-up.
db().prepare("INSERT INTO server_keys(id,game,pluto_key) VALUES('spare','t6','spare-key')").run();
db()
.prepare("INSERT INTO server_keys(id,game,pluto_key) VALUES('spare','t6','spare-key')")
.run();
const policy = scaling.saveScaling({ game: 't6', mapSlug, maximum: 2, mode: 'create-destroy' });
const { requestLifecycle } = await import('../../src/lib/server/provisioning/requests.js');
return { db, scaling, policy, requestLifecycle };
}
it.each(['town', '*'])('keeps lineup-changing actions blocked for %s ownership', async scope => {
it.each(['town', '*'])('keeps lineup-changing actions blocked for %s ownership', async (scope) => {
const { db, requestLifecycle } = await setup(scope);
const { destroyServer } = await import('../../src/lib/server/provisioning/instances.js');
for (const action of ['start', 'stop'] as const) {
expect(requestLifecycle(server, action)).toMatchObject({ ok: false, status: 409, error: expect.stringContaining('Autoscaling') });
expect(requestLifecycle(server, action)).toMatchObject({
ok: false,
status: 409,
error: expect.stringContaining('Autoscaling')
});
}
expect(destroyServer(server)).toMatchObject({ ok: false, message: expect.stringContaining('Autoscaling') });
expect(destroyServer(server)).toMatchObject({
ok: false,
message: expect.stringContaining('Autoscaling')
});
expect(db().prepare('SELECT id FROM server_deployments').all()).toEqual([]);
});
it.each([
['town', 'restart'], ['town', 'rebuild'], ['*', 'restart'], ['*', 'rebuild']
] as const)('allows %s-owned %s without scaling up during work or recovery', async (scope, action) => {
vi.useFakeTimers({ toFake: ['Date'] });
const { db, scaling, policy, requestLifecycle } = await setup(scope);
const { scalingSnapshot } = await import('../../src/lib/server/autoscaling/snapshot.js');
const result = requestLifecycle(server, action);
expect(result.ok).toBe(true);
if (!result.ok) throw new Error(result.error);
expect(requestLifecycle(server, action)).toEqual(result);
['town', 'restart'],
['town', 'rebuild'],
['*', 'restart'],
['*', 'rebuild']
] as const)(
'allows %s-owned %s without scaling up during work or recovery',
async (scope, action) => {
vi.useFakeTimers({ toFake: ['Date'] });
const { db, scaling, policy, requestLifecycle } = await setup(scope);
const { scalingSnapshot } = await import('../../src/lib/server/autoscaling/snapshot.js');
const result = requestLifecycle(server, action);
expect(result.ok).toBe(true);
if (!result.ok) throw new Error(result.error);
expect(requestLifecycle(server, action)).toEqual(result);
for (const state of ['waiting', 'queued', 'running', 'awaiting-health', 'interrupted']) {
db().prepare('UPDATE server_deployments SET state=? WHERE id=?').run(state, result.deploymentId);
db().prepare("UPDATE servers SET container_state='stopped',last_players=NULL WHERE name=?").run(server);
const snapshot = scalingSnapshot(policy);
expect(snapshot.members.find(member => member.name === server)).toMatchObject({ state: 'protected', operationId: result.deploymentId });
expect(snapshot.reservedNames).toContain(server);
for (const state of ['waiting', 'queued', 'running', 'awaiting-health', 'interrupted']) {
db()
.prepare('UPDATE server_deployments SET state=? WHERE id=?')
.run(state, result.deploymentId);
db()
.prepare(
"UPDATE servers SET container_state='stopped',last_players=NULL WHERE name=?"
)
.run(server);
const snapshot = scalingSnapshot(policy);
expect(snapshot.members.find((member) => member.name === server)).toMatchObject({
state: 'protected',
operationId: result.deploymentId
});
expect(snapshot.reservedNames).toContain(server);
expect(await scaling.runAutoscaling()).toBe(false);
}
// A terminal operation with unresolved recovery must still hold capacity.
db()
.prepare("UPDATE server_deployments SET state='failed' WHERE id=?")
.run(result.deploymentId);
const requestedAt = new Date().toISOString();
db()
.prepare('INSERT INTO restart_rollout(server,requested_at,operation_id) VALUES(?,?,?)')
.run(server, requestedAt, result.deploymentId);
expect(
scalingSnapshot(policy).members.find((member) => member.name === server)?.blocker
).toBe('Unresolved recovery');
expect(await scaling.runAutoscaling()).toBe(false);
expect(
db()
.prepare("SELECT id FROM server_deployments WHERE action IN ('create','destroy')")
.all()
).toEqual([]);
// Recovery status must be strictly newer than the restart request.
const at = new Date(Date.parse(requestedAt) + 1_000).toISOString();
vi.setSystemTime(new Date(at));
db()
.prepare("UPDATE server_deployments SET state='done',finished_at=? WHERE id=?")
.run(at, result.deploymentId);
db().prepare('UPDATE restart_rollout SET healthy_at=? WHERE server=?').run(at, server);
db()
.prepare(
"UPDATE servers SET container_state='running',last_players=0,container_checked_at=?,last_seen_at=?,last_checked_at=?"
)
.run(at, at, at);
expect(await scaling.runAutoscaling()).toBe(false);
expect(scaling.getScalingPolicy(policy.id)).toMatchObject({
mode: 'create-destroy',
pausedReason: null
});
// Once recovered, actual occupancy can request a new spare normally.
db().prepare('UPDATE servers SET last_players=1 WHERE name=?').run(server);
expect(await scaling.runAutoscaling()).toBe(true);
expect(
db().prepare("SELECT server FROM server_deployments WHERE action='create'").all()
).toEqual([{ server: 't6_zombie_town_2' }]);
}
// A terminal operation with unresolved recovery must still hold capacity.
db().prepare("UPDATE server_deployments SET state='failed' WHERE id=?").run(result.deploymentId);
const requestedAt = new Date().toISOString();
db().prepare('INSERT INTO restart_rollout(server,requested_at,operation_id) VALUES(?,?,?)')
.run(server, requestedAt, result.deploymentId);
expect(scalingSnapshot(policy).members.find(member => member.name === server)?.blocker).toBe('Unresolved recovery');
expect(await scaling.runAutoscaling()).toBe(false);
expect(db().prepare("SELECT id FROM server_deployments WHERE action IN ('create','destroy')").all()).toEqual([]);
// Recovery status must be strictly newer than the restart request.
const at = new Date(Date.parse(requestedAt) + 1_000).toISOString();
vi.setSystemTime(new Date(at));
db().prepare("UPDATE server_deployments SET state='done',finished_at=? WHERE id=?").run(at, result.deploymentId);
db().prepare('UPDATE restart_rollout SET healthy_at=? WHERE server=?').run(at, server);
db().prepare("UPDATE servers SET container_state='running',last_players=0,container_checked_at=?,last_seen_at=?,last_checked_at=?")
.run(at, at, at);
expect(await scaling.runAutoscaling()).toBe(false);
expect(scaling.getScalingPolicy(policy.id)).toMatchObject({ mode: 'create-destroy', pausedReason: null });
// Once recovered, actual occupancy can request a new spare normally.
db().prepare('UPDATE servers SET last_players=1 WHERE name=?').run(server);
expect(await scaling.runAutoscaling()).toBe(true);
expect(db().prepare("SELECT server FROM server_deployments WHERE action='create'").all()).toEqual([{ server: 't6_zombie_town_2' }]);
});
);