Support multi-game betas with automatic server selection
This commit is contained in:
@@ -68,6 +68,43 @@ async function tick(seconds = 15, players = 4, healthy = true) {
|
||||
const scriptPath = (name: string) => join(fixture.t5, name, 'runtime/plutonium/storage/t5/raw/scripts/sp/test.gsc');
|
||||
|
||||
describe('durable beta test plans', () => {
|
||||
it.each([
|
||||
{ scope: 'quorum', fileScope: 'game', otherServer: 't6_zombie_town_1', otherMap: 'zm_transit' },
|
||||
{ scope: 'game', fileScope: 'map', otherServer: 't5_zombie_cosmodrome_1', otherMap: 'zombie_cosmodrome' }
|
||||
] as const)('requires a passing participant for every file with $scope coverage', async ({ scope, fileScope, otherServer, otherMap }) => {
|
||||
const otherGame = otherServer.slice(0, 2);
|
||||
await mkdir(otherGame === 't6' ? join(fixture.t6, otherServer, 'runtime/plutonium/storage/t6/scripts/zm')
|
||||
: join(fixture.t5, otherServer, 'runtime/plutonium/storage/t5/raw/scripts/sp'), { recursive: true });
|
||||
const { seedServerRegistry } = await import('../../src/lib/server/fleet/servers.js');
|
||||
await seedServerRegistry();
|
||||
const { createScript, getVersion } = await import('../../src/lib/server/fleet/scripts.js');
|
||||
const { openBeta, getBeta, listBetaServers, promoteBeta } = await import('../../src/lib/server/fleet/beta.js');
|
||||
const { testView } = await import('../../src/lib/server/fleet/beta-tests.js');
|
||||
const files = [fileScope === 'game' ? 't5' : 'zombie_theater', fileScope === 'game' ? otherGame : otherMap]
|
||||
.map(scopeValue => createScript({ name: `Test ${scopeValue}`, targetRelPath: `${scopeValue}.gsc`,
|
||||
scopeType: fileScope, scopeValue, content: Buffer.from(`${scopeValue} beta`) }));
|
||||
const { betaId } = await openBeta({
|
||||
scripts: files.map(file => ({ scriptId: file.script.id, scriptVersionId: file.version.id })),
|
||||
servers: [...names, otherServer], waitForHealth: true, waitForMatch: false,
|
||||
testPlan: { ...defaultPlan(), scope, quorum: 1, requirements: [requirement('population', 'players')] }
|
||||
});
|
||||
await tick(); await tick();
|
||||
await postTelemetry(gameEvent('game_start')); await tick();
|
||||
expect(listBetaServers(betaId).find(server => server.server === names[0])?.stage).toBe('ready');
|
||||
expect(getBeta(betaId)?.state).toBe('running');
|
||||
expect(testView(betaId)?.outcome).toBe('active');
|
||||
expect(files.map(file => getVersion(file.version.id)?.state)).toEqual(['untested', 'untested']);
|
||||
await expect(promoteBeta(betaId)).rejects.toThrow('Test plan has not passed');
|
||||
|
||||
await postTelemetry(gameEvent('game_start', 25, 4, otherServer, otherMap)); await tick();
|
||||
expect(getBeta(betaId)?.state).toBe('ready-promote');
|
||||
expect(testView(betaId)?.outcome).toBe('passed');
|
||||
expect(files.map(file => getVersion(file.version.id)?.state)).toEqual(['tested', 'tested']);
|
||||
// Coverage still permits replicas to remain unproven once each file
|
||||
// has a passing participant; it does not silently become an all gate.
|
||||
expect(listBetaServers(betaId).find(server => server.server === names[1])?.stage).toBe('observing');
|
||||
});
|
||||
|
||||
it('requires fresh gameplay after a revision and promotes exactly the replacement version', async () => {
|
||||
const id = await openPlan({ ...defaultPlan(), requirements: [requirement('matches', 'matches')] });
|
||||
await tick(); await tick();
|
||||
|
||||
Reference in New Issue
Block a user