Files
Dune/tests/public-deals-cross-turn-model.test.ts

69 lines
4.1 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
createPublicDeal, markPublicDealPaid, parsePublicDealPayment, PublicDealError,
releasePublicDeal, respondPublicDeal, validatePublicDeals,
type PublicDeal, type PublicDealSeat,
} from '../game/public-deals';
import {
outstandingPublicDealPayments, pendingPublicDealPayments, publicDealPaymentTotal,
} from '../game/public-deal-obligations';
const seats: PublicDealSeat[] = [{ id: 'a' }, { id: 'b' }, { id: 'c' }];
const payment = (dueTurn: number, amount = 3) => ({
kind: 'deferredBribe' as const, payer: 'a', recipient: 'b', amount, dueTurn,
});
const offer = (id: string, dueTurn: number, turn = 3) =>
createPublicDeal(seats, 'a', 'b', 'Pay by the agreed turn.', turn, id, payment(dueTurn));
const accept = (id: string, dueTurn: number, turn = 3) =>
respondPublicDeal(offer(id, dueTurn, turn), seats, 'b', true);
void test('a deferred term retains an exact safe deadline at or after its offer turn', () => {
const immediate = { kind: 'immediateBribe', payer: 'a', recipient: 'b', amount: 2 } as const;
assert.deepEqual(parsePublicDealPayment(immediate, ['a', 'b']), immediate);
assert.deepEqual(parsePublicDealPayment(payment(3), ['a', 'b'], 3), payment(3));
assert.deepEqual(parsePublicDealPayment(payment(7), ['a', 'b'], 3), payment(7));
assert.deepEqual(parsePublicDealPayment(payment(100), ['a', 'b'], 3), payment(100),
'A later agreed game extension may make a distant deadline reachable.');
for (const invalid of [payment(2), payment(0), payment(3.5), payment(Infinity),
payment(Number.MAX_SAFE_INTEGER + 1), { ...payment(4), extra: true }])
assert.throws(() => parsePublicDealPayment(invalid, ['a', 'b'], 3), PublicDealError);
assert.throws(() => parsePublicDealPayment(payment(4), ['a', 'b']), PublicDealError);
assert.throws(() => parsePublicDealPayment(payment(4), ['a', 'b'], 0), PublicDealError);
assert.throws(() => parsePublicDealPayment({ ...payment(4), recipient: 'c' }, ['a', 'b'], 3), PublicDealError);
});
void test('separate pending and outstanding views preserve current-turn semantics', () => {
const current = accept('current', 3);
const later = accept('later', 5);
const farther = accept('farther', 9);
const paid = markPublicDealPaid(accept('paid', 8), seats, 'a');
const declined = respondPublicDeal(offer('declined', 6), seats, 'b', false);
const offered = offer('offered', 7);
const deals: PublicDeal[] = [current, later, farther, paid, declined, offered];
assert.deepEqual(pendingPublicDealPayments(deals, 'a', 3).map(row => row.deal), ['current']);
assert.equal(publicDealPaymentTotal(deals, 'a', 3), 3);
assert.deepEqual(outstandingPublicDealPayments(deals, 'a', 3).map(row => row.deal), ['current', 'later', 'farther']);
assert.deepEqual(outstandingPublicDealPayments(deals, 'a', 4).map(row => row.deal), ['later', 'farther']);
assert.deepEqual(pendingPublicDealPayments(deals, 'a', 5).map(row => row.deal), ['later']);
assert.equal(publicDealPaymentTotal(deals, 'a', 5), 3);
assert.deepEqual(outstandingPublicDealPayments(deals, 'a', 10), []);
assert.deepEqual(outstandingPublicDealPayments(deals, 'c', 3), []);
assert.equal(deals[0].paymentStatus, 'pending', 'Reading after a deadline never mutates a receipt.');
});
void test('early settlement and mutual release preserve exact future receipt history', () => {
const pending = accept('future', 6);
const copied = JSON.parse(JSON.stringify(pending)) as PublicDeal;
validatePublicDeals([copied], seats);
const paid = markPublicDealPaid(copied, seats, 'a');
assert.equal(paid.paymentStatus, 'paid');
const stillPaid = releasePublicDeal(releasePublicDeal(paid, seats, 'a', true), seats, 'b', true);
assert.equal(stillPaid.status, 'released');
assert.equal(stillPaid.paymentStatus, 'paid');
const canceled = releasePublicDeal(releasePublicDeal(pending, seats, 'a', true), seats, 'b', true);
assert.equal(canceled.paymentStatus, 'released');
assert.deepEqual(outstandingPublicDealPayments([stillPaid, canceled], 'a', 3), []);
assert.equal(pending.paymentStatus, 'pending', 'Pure transitions preserve the original record.');
});