Allow travelers to pay in two-party travel deals
Verify and publish checkpoint container / container (push) Canceled after 0s
Verify and publish checkpoint container / container (push) Canceled after 0s
This commit is contained in:
1 parent
c1ffa1c28a
commit
3987c5c16b
9 files changed
+90
-21
No files matched your search
@@ -117,11 +117,50 @@ void test('unanimous release ends the pending travel obligation without refundin
|
||||
assert.equal(player(game, f.target).bribes, escrow);
|
||||
});
|
||||
|
||||
void test('paid travel excludes reversed, deferred and multiparty payment variants', () => {
|
||||
void test('paid travel excludes deferred and multiparty payment variants', () => {
|
||||
const f = fixture(), travel = { kind: 'ship', territory: f.shipmentTerritory, minimum: 1, answer: true };
|
||||
const base = { type: 'dealOffer', target: f.target, terms: 'Travel.', travel } satisfies Action;
|
||||
reject(f.game, f.asker, { ...base, payment: { kind: 'immediateBribe', payer: f.target, recipient: f.asker, amount: 1 } });
|
||||
reject(f.game, f.asker, { ...base, payment: { kind: 'deferredBribe', payer: f.asker, recipient: f.target, amount: 1, dueTurn: f.game.turn } });
|
||||
reject(f.game, f.asker, { type: 'dealOffer', targets: [f.target, f.asker], terms: 'Travel.', travel,
|
||||
payment: { kind: 'immediateBribe', payer: f.asker, recipient: f.target, amount: 1 } });
|
||||
});
|
||||
|
||||
void test('the travelling recipient can pay the proposer at final consent and then complete shipment', () => {
|
||||
const f = fixture(), payerBefore = player(f.game, f.target).spice,
|
||||
recipientBefore = player(f.game, f.asker).bribes;
|
||||
assert.ok(payerBefore >= f.shipmentCost + 1);
|
||||
const payment = { kind: 'immediateBribe', payer: f.target, recipient: f.asker, amount: 1 };
|
||||
const travel = { kind: 'ship', territory: f.shipmentTerritory, minimum: 1, answer: true };
|
||||
const offered = act(f.game, f.asker, { type: 'dealOffer', target: f.target,
|
||||
terms: 'Payment for agreed travel.', payment, travel });
|
||||
const deal = offered.deals!.at(-1)!;
|
||||
assert.equal(player(offered, f.target).spice, payerBefore);
|
||||
const accepted = accept(offered, f.target, deal.id);
|
||||
assert.equal(accepted.deals!.find(row => row.id === deal.id)!.status, 'active');
|
||||
assert.equal(player(accepted, f.target).spice, payerBefore - 1);
|
||||
assert.equal(player(accepted, f.asker).bribes, recipientBefore + 1);
|
||||
assert.equal(accepted.travelPromises!.filter(row => row.sourceDeal === deal.id).length, 1);
|
||||
assert.equal(status(accepted, f.target, deal.id), 'pending');
|
||||
const shipped = settle(act(accepted, f.target, f.shipmentAction));
|
||||
assert.equal(status(shipped, f.target, deal.id), 'fulfilled');
|
||||
assert.equal(player(shipped, f.target).spice, payerBefore - 1 - f.shipmentCost);
|
||||
assert.equal(player(shipped, f.asker).bribes, recipientBefore + 1);
|
||||
assert.deepEqual(inventory(shipped), f.originalCards);
|
||||
});
|
||||
|
||||
void test('reverse payment that consumes the travel funds rolls back the final consent atomically', () => {
|
||||
const f = fixture(), cash = player(f.game, f.target).spice,
|
||||
recipientEscrow = player(f.game, f.asker).bribes;
|
||||
assert.ok(cash >= f.shipmentCost);
|
||||
const payment = { kind: 'immediateBribe', payer: f.target, recipient: f.asker, amount: cash };
|
||||
const travel = { kind: 'ship', territory: f.shipmentTerritory, minimum: 1, answer: true };
|
||||
const offered = act(f.game, f.asker, { type: 'dealOffer', target: f.target,
|
||||
terms: 'Payment for agreed travel.', payment, travel });
|
||||
const deal = offered.deals!.at(-1)!;
|
||||
reject(offered, f.target, { type: 'dealRespond', deal: deal.id, accept: true });
|
||||
assert.equal(offered.deals!.find(row => row.id === deal.id)!.status, 'offered');
|
||||
assert.equal(player(offered, f.target).spice, cash);
|
||||
assert.equal(player(offered, f.asker).bribes, recipientEscrow);
|
||||
assert.equal(offered.travelPromises?.some(row => row.sourceDeal === deal.id) ?? false, false);
|
||||
assert.deepEqual(inventory(offered), f.originalCards);
|
||||
});
|
||||
@@ -53,7 +53,9 @@ void test('the offer packet retains legacy two-party terms and validates an expl
|
||||
assert.equal(publicDealOfferAction(['b','c'], 'Terms', payment, 'a', 2, {...travel, actor:'c'}), null);
|
||||
assert.deepEqual(publicDealOfferAction(['b'], 'Terms', payment, 'a', 2, travel),
|
||||
{type:'dealOffer', target:'b', terms:'Terms', payment, travel});
|
||||
assert.equal(publicDealOfferAction(['b'], 'Terms', {...payment,payer:'b',recipient:'a'}, 'a', 2, travel), null);
|
||||
const reverse = {...payment,payer:'b',recipient:'a'};
|
||||
assert.deepEqual(publicDealOfferAction(['b'], 'Terms', reverse, 'a', 2, travel),
|
||||
{type:'dealOffer', target:'b', terms:'Terms', payment:reverse, travel});
|
||||
assert.equal(publicDealOfferAction(['b'], 'Terms', {kind:'deferredBribe',payer:'a',recipient:'b',amount:2,dueTurn:2}, 'a', 2, travel), null);
|
||||
assert.equal(publicDealOfferAction(['b','c'], 'Terms', payment, 'a', 2, travel), null);
|
||||
assert.equal(publicDealOfferAction(['a'], 'Terms', undefined, 'a', 2, travel), null);
|
||||
@@ -142,7 +144,12 @@ void test('the authoring controls compose a proposer payment with recipient trav
|
||||
travel:{kind:'ship',territory:'carthag',minimum:1,answer:true}});
|
||||
ui.select(0,'deferredBribe'); assert.equal(button()?.props.disabled,true);
|
||||
ui.select(0,'immediateBribe'); ui.select(1,'b'); ui.select(2,'a');
|
||||
assert.equal(button()?.props.disabled,true);
|
||||
assert.equal(button()?.props.disabled,false);
|
||||
const reverseForm = ui.render().find(node => node.type === 'form'); assert.ok(reverseForm);
|
||||
(reverseForm.props.onSubmit as (event:{preventDefault():void}) => void)({preventDefault() {}});
|
||||
assert.deepEqual(JSON.parse(JSON.stringify(ui.sent[1])), {type:'dealOffer',target:'b',terms:'Travel for spice',
|
||||
payment:{kind:'immediateBribe',payer:'b',recipient:'a',amount:1},
|
||||
travel:{kind:'ship',territory:'carthag',minimum:1,answer:true}});
|
||||
});
|
||||
|
||||
void test('a payment can also be added after selecting the travel term', () => {
|
||||
|
||||
@@ -172,8 +172,8 @@ void test('multiparty travel rejects forged actors, early votes, payments and no
|
||||
assert.equal(respondPublicDeal(acceptedOther, seats, 'b', true).status, 'active');
|
||||
});
|
||||
|
||||
void test('a proposer-to-recipient immediate payment can accompany travel through the complete deal lifecycle', () => {
|
||||
const payment = { kind: 'immediateBribe', payer: 'a', recipient: 'b', amount: 3 } as const;
|
||||
for (const [payer, recipient] of [['a', 'b'], ['b', 'a']] as const) void test(`a ${payer}-to-${recipient} immediate payment can accompany travel through the complete deal lifecycle`, () => {
|
||||
const payment = { kind: 'immediateBribe', payer, recipient, amount: 3 } as const;
|
||||
const original = createPublicDeal(seats, 'a', 'b', 'Payment for travel.', 3, 'paid-travel', payment);
|
||||
const offered = setPublicDealTravel(original, term, seats);
|
||||
assert.equal(original.travel, undefined);
|
||||
@@ -193,11 +193,10 @@ void test('a proposer-to-recipient immediate payment can accompany travel throug
|
||||
assert.equal(released.status, 'released');
|
||||
});
|
||||
|
||||
void test('paid travel rejects reverse, deferred, multiparty and forged persisted payment shapes', () => {
|
||||
const reverse = { kind: 'immediateBribe', payer: 'b', recipient: 'a', amount: 1 } as const;
|
||||
void test('paid travel rejects deferred, multiparty and forged persisted payment shapes', () => {
|
||||
const deferred = { kind: 'deferredBribe', payer: 'a', recipient: 'b', amount: 1, dueTurn: 3 } as const;
|
||||
const paid = { kind: 'immediateBribe', payer: 'a', recipient: 'b', amount: 1 } as const;
|
||||
for (const payment of [reverse, deferred]) {
|
||||
for (const payment of [deferred]) {
|
||||
const offer = createPublicDeal(seats, 'a', 'b', 'Terms', 3, `invalid-${payment.kind}`, payment);
|
||||
assert.throws(() => setPublicDealTravel(offer, term, seats), PublicDealError);
|
||||
assert.throws(() => validatePublicDeals([{ ...offer, travel: term }], seats), PublicDealError);
|
||||
|
||||
Reference in new issue
Block a user