Files
Dune/tests/richese-reveal-interruption-privacy.test.ts
T
Quintine 0d55333d5d
Verify and publish checkpoint container / container (push) Canceled after 0s
Allow Richese No-Field reveal during Basic openings and markets
2026-10-29 07:27:08 +11:00

78 lines
3.8 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { applyAction, createGame, newPlayer, viewGame, type Game } from '../game/engine';
import { richeseRevealInterruptSupported } from '../game/richese-reveal-interruption';
import { createRicheseNoField, deployRicheseNoField } from '../game/richese-no-field';
import {
bindTruthMixedBribe,
createTruthMixedBribeFixture,
truthMixedBribeFact,
truthMixedBribeGroup,
truthMixedBribePayment,
} from './fixture-truth-mixed-bribe';
// The public question and residual are produced by the native mixed-question
// fixture. The pending auction parent, transfer of its records to a Richese
// table, marker, and alternate no-residual ledger are explicitly staged for
// this availability comparison; they are not a replay of the original game.
function stagedPair() {
const fixture = createTruthMixedBribeFixture();
const mixed = truthMixedBribeGroup('or', truthMixedBribeFact(fixture.hajrFact),
truthMixedBribePayment(fixture.asker));
const answered = bindTruthMixedBribe(fixture, 'yes', mixed).game;
const record = answered.truthHistory?.at(-1);
const residuals = answered.bribeExpressionPromises;
assert.ok(record && residuals?.length);
assert.equal(record.answer, 'yes');
assert.ok(residuals.some(row => !row.fulfilled && !row.released));
const base = createGame('REVEALPRIVACY', newPlayer('r', 'Richese', 'richese'), false, ['choam']);
base.players.push(...answered.players.map(player => newPlayer(player.id, player.name, player.faction)));
const richese = base.players[0];
richese.noField = deployRicheseNoField(createRicheseNoField(['zero', 'three', 'five']), {
tokenId: 'three', controller: 'r', location: { territory: 'polar_sink', sector: 0 },
});
richese.noFieldEvent = 'marker-event';
base.status = 'playing';
base.phase = 3;
base.turn = record.turn;
base.response = null;
base.phaseOpening = null;
base.decision = { kind: 'auctionPayment', player: fixture.target };
base.auction = { cards: [], index: 0, bid: 3, bidder: fixture.target, active: fixture.target, passed: [], opener: 0 };
base.truthHistory = [structuredClone(record)];
const withResidual = structuredClone(base);
withResidual.bribeExpressionPromises = structuredClone(residuals);
const withoutResidual = structuredClone(base);
withoutResidual.bribeExpressionPromises = [];
return { withResidual, withoutResidual, record };
}
void test('a public current-turn mixed answer uniformly hides private residual-dependent interruption availability', () => {
const { withResidual, withoutResidual, record } = stagedPair();
const baseline = structuredClone(withoutResidual);
baseline.truthHistory = [];
assert.equal(richeseRevealInterruptSupported(baseline), true, 'the staged economic parent is otherwise admitted');
for (const game of [withResidual, withoutResidual]) {
const before = structuredClone(game);
assert.deepEqual(viewGame(game, 'r').truthHistory, [record]);
assert.equal(richeseRevealInterruptSupported(game), false);
const info = viewGame(game, 'r').richeseNoField;
assert.ok(info?.private?.deployed);
assert.equal(info.canReveal, false);
assert.throws(() => applyAction(game, 'r', { type: 'revealNoField',
event: info.event, token: info.private!.deployed!.tokenId }));
assert.deepEqual(game, before);
}
assert.equal(viewGame(withResidual, 'r').richeseNoField!.revealBlock,
viewGame(withoutResidual, 'r').richeseNoField!.revealBlock);
});
void test('the public history fence applies only to the current turn', () => {
const { withoutResidual } = stagedPair();
const game: Game = structuredClone(withoutResidual);
game.turn++;
assert.equal(richeseRevealInterruptSupported(game), true);
assert.equal(viewGame(game, 'r').richeseNoField?.canReveal, true);
});