68 lines
3.6 KiB
TypeScript
68 lines
3.6 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import {
|
|
answerFactionSwap, cancelFactionSwap, createFactionSwap, FactionSwapError,
|
|
validateFactionSwap, type FactionSwapSeat,
|
|
} from '../game/faction-swap';
|
|
|
|
const roster: FactionSwapSeat[] = [
|
|
{ id: 'one', faction: 'atreides' },
|
|
{ id: 'two', faction: 'harkonnen' },
|
|
{ id: 'three', faction: 'guild' },
|
|
];
|
|
|
|
void test('an offer snapshots the public identities and current faction assignments', () => {
|
|
const players = structuredClone(roster);
|
|
const offer = createFactionSwap(players, 'one', 'two', 'swap:1');
|
|
assert.deepEqual(offer, {
|
|
event: 'swap:1', from: 'one', to: 'two', fromFaction: 'atreides', toFaction: 'harkonnen',
|
|
});
|
|
assert.deepEqual(players, roster);
|
|
validateFactionSwap(JSON.parse(JSON.stringify(offer)), players);
|
|
});
|
|
|
|
void test('only the recipient may accept or reject and acceptance swaps precisely two factions', () => {
|
|
const players = structuredClone(roster);
|
|
const offer = createFactionSwap(players, 'one', 'two', 'swap:1');
|
|
const original = structuredClone(offer);
|
|
assert.deepEqual(answerFactionSwap(players, offer, 'two', true), {
|
|
accepted: true, assignments: [
|
|
{ player: 'one', faction: 'harkonnen' },
|
|
{ player: 'two', faction: 'atreides' },
|
|
],
|
|
});
|
|
assert.deepEqual(answerFactionSwap(players, offer, 'two', false), { accepted: false, assignments: [] });
|
|
assert.throws(() => answerFactionSwap(players, offer, 'one', true), FactionSwapError);
|
|
assert.throws(() => answerFactionSwap(players, offer, 'three', false), FactionSwapError);
|
|
assert.throws(() => answerFactionSwap(players, offer, 'two', 'yes' as unknown as boolean), FactionSwapError);
|
|
assert.deepEqual(players, roster);
|
|
assert.deepEqual(offer, original);
|
|
});
|
|
|
|
void test('only the proposer can cancel an offer', () => {
|
|
const offer = createFactionSwap(roster, 'one', 'two', 'swap:1');
|
|
assert.doesNotThrow(() => cancelFactionSwap(roster, offer, 'one'));
|
|
assert.throws(() => cancelFactionSwap(roster, offer, 'two'), FactionSwapError);
|
|
assert.throws(() => cancelFactionSwap(roster, offer, 'three'), FactionSwapError);
|
|
});
|
|
|
|
void test('invalid rosters and participants cannot create an offer', () => {
|
|
assert.throws(() => createFactionSwap(roster, 'one', 'one', 'swap:1'), FactionSwapError);
|
|
assert.throws(() => createFactionSwap(roster, 'one', 'missing', 'swap:1'), FactionSwapError);
|
|
assert.throws(() => createFactionSwap(roster, 'one', 'two', ' '), FactionSwapError);
|
|
assert.throws(() => createFactionSwap([roster[0], roster[0]], 'one', 'two', 'swap:1'), FactionSwapError);
|
|
assert.throws(() => createFactionSwap([roster[0], { ...roster[1], faction: 'atreides' }], 'one', 'two', 'swap:1'), FactionSwapError);
|
|
assert.throws(() => createFactionSwap([roster[0]], 'one', 'two', 'swap:1'), FactionSwapError);
|
|
});
|
|
|
|
void test('saved offers cannot authorize stale or malformed faction assignments', () => {
|
|
const offer = createFactionSwap(roster, 'one', 'two', 'swap:1');
|
|
const changed = [roster[0], { ...roster[1], faction: 'fremen' as const }, roster[2]];
|
|
assert.throws(() => answerFactionSwap(changed, offer, 'two', true), FactionSwapError);
|
|
assert.throws(() => validateFactionSwap(offer, [roster[0], roster[2]]), FactionSwapError);
|
|
assert.throws(() => validateFactionSwap({ ...offer, event: '' }, roster), FactionSwapError);
|
|
assert.throws(() => validateFactionSwap({ ...offer, to: 'one' }, roster), FactionSwapError);
|
|
assert.throws(() => validateFactionSwap({ ...offer, toFaction: 'invalid' }, roster), FactionSwapError);
|
|
assert.throws(() => validateFactionSwap({ ...offer, secret: 'extra' }, roster), FactionSwapError);
|
|
});
|