Files
Dune/tests/two-player-controllers.test.ts
T

99 lines
4.8 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
TwoPlayerControllersError,
addTwoPlayerControlledSeat as add,
controllerStrongholdTarget as target,
createTwoPlayerControllers as create,
physicalControllerCount as count,
validateTwoPlayerControllersComplete as complete,
validateTwoPlayerControllersLobby as lobby,
type TwoPlayerControllers,
} from '../game/two-player-controllers';
const seats = ['atreides', 'harkonnen', 'fremen', 'guild'];
function assembled(): TwoPlayerControllers {
let state = create();
state = add(state, seats, 'atreides', 'atreides');
state = add(state, seats, 'atreides', 'fremen');
state = add(state, seats, 'harkonnen', 'harkonnen');
return add(state, seats, 'harkonnen', 'guild');
}
void test('lobby additions create two distinct primary controllers without mutating inputs', () => {
const empty = create(), first = add(empty, seats, 'atreides', 'atreides');
const prior = structuredClone(first);
const second = add(first, seats, 'atreides', 'fremen');
assert.deepEqual(empty, { version: 1, controllers: [] });
assert.deepEqual(first, prior);
assert.notEqual(second.controllers, first.controllers);
assert.notEqual(second.controllers[0].seats, first.controllers[0].seats);
lobby(second, seats);
assert.throws(() => complete(second, seats), TwoPlayerControllersError);
const ready = assembled();
complete(ready, seats);
assert.deepEqual(ready.controllers, [
{ id: 'atreides', seats: ['atreides', 'fremen'] },
{ id: 'harkonnen', seats: ['harkonnen', 'guild'] },
]);
});
void test('full mapping counts two people and keeps faction alliances separate', () => {
const state = assembled();
assert.equal(count(seats, state), 2);
assert.equal(target(seats, state, false), 4);
assert.equal(target(seats, state, true), 4);
assert.deepEqual(state.controllers[0].seats, ['atreides', 'fremen']);
// Without the explicit mode, four faction seats remain four players.
assert.equal(count(seats), 4);
assert.equal(target(seats, undefined, false), 3);
assert.equal(target(seats, undefined, true), 4);
assert.equal(target(seats.slice(0, 2), null, false), 4);
assert.equal(count(['a', 'b', 'c', 'd', 'e', 'f']), 6);
});
void test('lobby mapping rejects duplicate, foreign, missing and forged primary assignments', () => {
for (const value of [
{ version: 1, controllers: [{ id: 'atreides', seats: ['atreides', 'atreides'] }] },
{ version: 1, controllers: [{ id: 'atreides', seats: ['fremen'] }] },
{ version: 1, controllers: [{ id: 'atreides', seats: ['atreides', 'foreign'] }] },
{ version: 1, controllers: [
{ id: 'atreides', seats: ['atreides', 'fremen'] },
{ id: 'harkonnen', seats: ['harkonnen', 'fremen'] },
] },
{ version: 1, controllers: [{ id: 'atreides', seats: [] }] },
{ version: 1, controllers: [{ id: 'atreides', seats: ['atreides'], secret: 'x' }] },
{ version: 2, controllers: [] },
{ version: 1, controllers: [], extra: true },
]) assert.throws(() => lobby(value, seats), TwoPlayerControllersError);
const state = assembled(), snapshot = structuredClone(state);
assert.throws(() => lobby(state, seats.slice(0, 3)), TwoPlayerControllersError);
assert.throws(() => lobby(state, ['atreides', 'atreides']), TwoPlayerControllersError);
assert.deepEqual(state, snapshot);
});
void test('incremental additions cannot steal, duplicate or exceed a controller seat', () => {
let state = add(create(), seats, 'atreides', 'atreides');
const before = structuredClone(state);
assert.throws(() => add(state, seats, 'atreides', 'atreides'), TwoPlayerControllersError);
assert.throws(() => add(state, seats, 'harkonnen', 'guild'), TwoPlayerControllersError);
assert.throws(() => add(state, seats, 'atreides', 'unknown'), TwoPlayerControllersError);
assert.deepEqual(state, before);
state = add(state, seats, 'atreides', 'fremen');
assert.throws(() => add(state, seats, 'atreides', 'guild'), TwoPlayerControllersError);
state = add(state, seats, 'harkonnen', 'harkonnen');
assert.throws(() => add(state, seats, 'guild', 'guild'), TwoPlayerControllersError);
});
void test('a partial or malformed mapping cannot alter victory calculations', () => {
const partial = add(create(), seats, 'atreides', 'atreides');
assert.throws(() => count(seats, partial), TwoPlayerControllersError);
assert.throws(() => target(seats, partial, false), TwoPlayerControllersError);
assert.throws(() => target(seats, partial, true), TwoPlayerControllersError);
assert.throws(() => count([], undefined), TwoPlayerControllersError);
assert.throws(() => count(['a', 'a'], undefined), TwoPlayerControllersError);
const restored = JSON.parse(JSON.stringify(assembled())) as TwoPlayerControllers;
complete(restored, seats);
assert.equal(target(seats, restored, false), 4);
});