import assert from 'node:assert/strict'; import test from 'node:test'; import { AuctionPaymentRoutingError, routeAuctionPayment, type AuctionPaymentInput } from '../game/auction-payment-routing'; const leg = (payer: string, amount: number, recipient: 'bank' | 'emperor' | 'seller') => ({payer, amount, recipient}); function invoice(patch: Partial = {}): AuctionPaymentInput { return {advanced:true, free:false, price:9, buyer:'buyer', emperor:'emperor', seller:null, contributions:[{payer:'buyer',amount:3}, {payer:'emperor',amount:4}, {payer:'ally',amount:2}], ...patch}; } void test('Advanced ordinary auction routes Emperor aid to Bank and other actual payer legs to Emperor', () => { const source = invoice(), before = structuredClone(source); assert.deepEqual(routeAuctionPayment(source), [ leg('buyer',3,'emperor'), leg('emperor',4,'bank'), leg('ally',2,'emperor'), ]); assert.deepEqual(source, before); assert.deepEqual(routeAuctionPayment(invoice({advanced:false})), [ leg('buyer',3,'emperor'), leg('emperor',4,'emperor'), leg('ally',2,'emperor'), ], 'Basic retains the preexisting aggregate income routing.'); }); void test('a direct gift is buyer-owned funding and does not become Emperor aid', () => { assert.deepEqual(routeAuctionPayment(invoice({price:7, contributions:[ {payer:'buyer',amount:5}, {payer:'ally',amount:2}, ]})), [leg('buyer',5,'emperor'), leg('ally',2,'emperor')]); }); void test('an Emperor buyer or an absent Emperor sends a normal invoice to Bank', () => { assert.deepEqual(routeAuctionPayment(invoice({buyer:'emperor',price:6,contributions:[ {payer:'emperor',amount:4}, {payer:'ally',amount:2}, ]})), [leg('emperor',4,'bank'), leg('ally',2,'bank')]); assert.deepEqual(routeAuctionPayment(invoice({emperor:null,price:5,contributions:[ {payer:'buyer',amount:3}, {payer:'ally',amount:2}, ]})), [leg('buyer',3,'bank'), leg('ally',2,'bank')]); }); void test('Richese seller, Emperor and other donors retain distinct Advanced special-auction destinations', () => { const contributions = [{payer:'buyer',amount:3}, {payer:'emperor',amount:2}, {payer:'richese',amount:2}, {payer:'ally',amount:1}]; assert.deepEqual(routeAuctionPayment(invoice({seller:'richese',price:8,contributions})), [ leg('buyer',3,'seller'), leg('emperor',2,'bank'), leg('richese',2,'emperor'), leg('ally',1,'seller'), ]); assert.deepEqual(routeAuctionPayment(invoice({advanced:false,seller:'richese',price:8,contributions})), [ leg('buyer',3,'seller'), leg('emperor',2,'seller'), leg('richese',2,'seller'), leg('ally',1,'seller'), ]); assert.deepEqual(routeAuctionPayment(invoice({seller:'richese',emperor:null,price:5,contributions:[ {payer:'buyer',amount:3}, {payer:'richese',amount:2}, ]})), [leg('buyer',3,'seller'), leg('richese',2,'bank')]); }); void test('Emperor buying a Richese card receives Richese aid, while buyer payment goes to the seller', () => { assert.deepEqual(routeAuctionPayment(invoice({buyer:'emperor',seller:'richese',price:5,contributions:[ {payer:'emperor',amount:3}, {payer:'richese',amount:2}, ]})), [leg('emperor',3,'seller'), leg('richese',2,'emperor')]); }); void test('Richese paid self-retention routes its own price normally and Emperor aid to Bank', () => { assert.deepEqual(routeAuctionPayment(invoice({buyer:'richese',seller:'richese',price:7,contributions:[ {payer:'richese',amount:3}, {payer:'emperor',amount:4}, ]})), [leg('richese',3,'emperor'), leg('emperor',4,'bank')]); assert.deepEqual(routeAuctionPayment(invoice({buyer:'richese',seller:'richese',emperor:null,price:3, contributions:[{payer:'richese',amount:3}]})), [leg('richese',3,'bank')]); }); void test('a Bank-only override reroutes every leg and a free purchase carries no paid legs', () => { assert.deepEqual(routeAuctionPayment(invoice({seller:'richese',bankOnly:true})), [ leg('buyer',3,'bank'), leg('emperor',4,'bank'), leg('ally',2,'bank'), ]); assert.deepEqual(routeAuctionPayment(invoice({free:true,contributions:[]})), []); assert.deepEqual(routeAuctionPayment(invoice({free:false,price:0,contributions:[]})), []); assert.throws(() => routeAuctionPayment(invoice({free:true})), AuctionPaymentRoutingError); }); void test('the physical invoice rejects duplicates, phantom legs, unfunded costs and unsafe sums', () => { for (const patch of [ {contributions:[{payer:'buyer',amount:0}, {payer:'ally',amount:9}]}, {contributions:[{payer:'buyer',amount:4}, {payer:'buyer',amount:5}]}, {contributions:[{payer:'buyer',amount:8}]}, {contributions:[{payer:'buyer',amount:10}]}, {contributions:[{payer:'',amount:9}]}, {price:-1}, {price:1.5}, {price:Number.MAX_SAFE_INTEGER + 1}, {seller:'emperor'}, {bankOnly:'yes'}, {price:Number.MAX_SAFE_INTEGER, contributions:[{payer:'buyer',amount:Number.MAX_SAFE_INTEGER}, {payer:'ally',amount:1}]}, ]) assert.throws(() => routeAuctionPayment(invoice(patch as Partial)), AuctionPaymentRoutingError); });