69 lines
4.0 KiB
TypeScript
69 lines
4.0 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { advanceAuctionClock, applyAction, hasAuctionClockOpportunity, synchronizeAuctionClock,
|
|
viewGame } from '../game/engine';
|
|
import { truthBidInventory } from './fixture-truth-bid';
|
|
import { advanceTruthTurnBid, bindTruthTurnBid, createTruthTurnBidFixture,
|
|
finishTruthTurnBidLot, purchaseTruthTurnBidLot, truthTurnBidClean,
|
|
truthTurnBidPlayer } from './fixture-truth-turn-bid';
|
|
|
|
const epoch = 'phase-bid-test:0';
|
|
|
|
/** All three lots are original cards reached through the actual Basic auction.
|
|
* Earlier cards are purchased through native bids, never staged into a hand. */
|
|
function finalLot(answer: 'yes' | 'no') {
|
|
const fixture = createTruthTurnBidFixture();
|
|
let game = fixture.game;
|
|
for (let n = 0; n < 2; n++) game = purchaseTruthTurnBidLot(game, fixture.opponent).game;
|
|
assert.equal(game.auction?.index, 2);
|
|
game = advanceTruthTurnBid(game, state => truthTurnBidClean(state) &&
|
|
state.auction?.active === fixture.target);
|
|
game = bindTruthTurnBid(fixture, answer, 4, game).game;
|
|
assert.equal(game.auction?.active, fixture.target);
|
|
assert.equal(game.auction?.clockVersion, 1);
|
|
assert.ok(hasAuctionClockOpportunity(game));
|
|
return { fixture, game, card: game.auction!.cards[game.auction!.index].id };
|
|
}
|
|
|
|
void test('a real expiring last-lot clock submits the required phase Yes bid and keeps native payment', () => {
|
|
const { fixture: f, game, card } = finalLot('yes');
|
|
const original = structuredClone(game);
|
|
assert.throws(() => applyAction(game, f.target, { type: 'passBid' }));
|
|
assert.deepEqual(game, original);
|
|
const guide = viewGame(game, f.target).phaseBidPromiseGuide;
|
|
assert.ok(guide); assert.equal(guide.action.type, 'bid'); assert.equal(guide.action.amount, 4);
|
|
const armed = synchronizeAuctionClock(game, 1_000, epoch);
|
|
assert.equal(advanceAuctionClock(armed, 10_999, epoch), armed);
|
|
const expired = advanceAuctionClock(armed, 11_000, epoch);
|
|
assert.equal(expired.auction?.bidder, f.target);
|
|
assert.equal(expired.auction?.bid, 4);
|
|
assert.equal(truthTurnBidPlayer(expired, f.target).spice, truthTurnBidPlayer(game, f.target).spice,
|
|
'A deadline declaration does not prepay or waive the native invoice');
|
|
assert.ok(expired.phaseBidPromises?.some(row => row.player === f.target && row.minimum === 4 && row.fulfilled));
|
|
assert.match(expired.log.at(-1)!.text, /legal bid required by their binding Truthtrance answer was submitted/);
|
|
assert.deepEqual(truthBidInventory(expired), f.originalCards);
|
|
const settled = finishTruthTurnBidLot(expired);
|
|
assert.equal(truthTurnBidPlayer(settled, f.target).spice, truthTurnBidPlayer(game, f.target).spice - 4);
|
|
assert.ok(truthTurnBidPlayer(settled, f.target).hand.some(item => item.id === card));
|
|
assert.deepEqual(truthBidInventory(settled), f.originalCards);
|
|
});
|
|
|
|
void test('a real expiring last-lot clock preserves phase No and takes the legal pass', () => {
|
|
const { fixture: f, game } = finalLot('no');
|
|
assert.throws(() => applyAction(game, f.target, { type: 'bid', amount: 4, allyPayment: 0 }));
|
|
const expected = applyAction(game, f.target, { type: 'passBid' });
|
|
const armed = synchronizeAuctionClock(game, 1_000, epoch);
|
|
const expired = advanceAuctionClock(armed, 11_000, epoch);
|
|
assert.equal(expired.phase, expected.phase);
|
|
assert.deepEqual(expired.auction, expected.auction);
|
|
assert.deepEqual(expired.phaseBidPromises, expected.phaseBidPromises);
|
|
assert.ok(expired.phaseBidPromises?.some(row => row.player === f.target && !row.answer && !row.fulfilled && !row.released),
|
|
'A phase-wide No stays live while other bidders can still act');
|
|
assert.match(expired.log.at(-1)!.text, /ten-second bidding time expired; they passed/);
|
|
assert.deepEqual(truthBidInventory(expired), f.originalCards);
|
|
const closed = advanceTruthTurnBid(expired, state => state.phase !== 3);
|
|
assert.ok(closed.phaseBidPromises?.some(row => row.player === f.target && !row.answer && row.fulfilled),
|
|
'The No is fulfilled only after the actual Bidding phase closes');
|
|
assert.deepEqual(truthBidInventory(closed), f.originalCards);
|
|
});
|