import { describe, expect, it } from 'vitest' import { splitRelayPayout } from '../src/split-relay.js' const hold = (agent_id, end_reason, has_valid_checkpoint) => ({ agent_id, end_reason, has_valid_checkpoint }) describe('SM-02 splitRelayPayout', () => { it('1 人:全额归提交者', () => { expect(splitRelayPayout(100, [hold('b', 'submitted', true)], 'b')).toEqual([{ agent_id: 'b', amount: 100, kind: 'winner' }]) }) it('2 人平分', () => { expect(splitRelayPayout(100, [hold('a', 'released', true), hold('b', 'submitted', false)], 'b')).toEqual([ { agent_id: 'b', amount: 50, kind: 'winner' }, { agent_id: 'a', amount: 50, kind: 'relay_share' }, ]) }) it('3 人除不尽:余数归最终提交者', () => { const payouts = splitRelayPayout(100, [hold('a', 'timeout', true), hold('c', 'transferred', true), hold('b', 'submitted', true)], 'b') expect(payouts).toEqual([ { agent_id: 'b', amount: 34, kind: 'winner' }, { agent_id: 'a', amount: 33, kind: 'relay_share' }, { agent_id: 'c', amount: 33, kind: 'relay_share' }, ]) }) it('同一 Agent 多次持有只算一份;没留有效检查点的前任不参与', () => { const holds = [hold('a', 'released', true), hold('a', 'timeout', true), hold('d', 'released', false), hold('b', 'released', true), hold('b', 'submitted', true)] expect(splitRelayPayout(90, holds, 'b')).toEqual([ { agent_id: 'b', amount: 45, kind: 'winner' }, { agent_id: 'a', amount: 45, kind: 'relay_share' }, ]) }) it('各笔之和恒等于 reward(含赏金小于人数)', () => { const holds = [hold('a', 'released', true), hold('c', 'released', true), hold('b', 'submitted', true)] for (const reward of [1, 2, 3, 4, 7, 100, 101]) { const payouts = splitRelayPayout(reward, holds, 'b') expect(payouts.reduce((sum, p) => sum + p.amount, 0)).toBe(reward) expect(payouts.every((p) => p.amount > 0)).toBe(true) } expect(splitRelayPayout(2, holds, 'b')).toEqual([{ agent_id: 'b', amount: 2, kind: 'winner' }]) }) it('提交后又放弃的前任也参与平分', () => { expect(splitRelayPayout(100, [hold('a', 'submitted', true), hold('b', 'submitted', true)], 'b')).toEqual([ { agent_id: 'b', amount: 50, kind: 'winner' }, { agent_id: 'a', amount: 50, kind: 'relay_share' }, ]) }) it('全部结束原因都被处理;未知原因直接 throw', () => { for (const reason of ['submitted', 'released', 'timeout', 'transferred', null]) expect(() => splitRelayPayout(10, [hold('a', reason, true)], 'b')).not.toThrow() expect(() => splitRelayPayout(10, [hold('a', 'vanished', true)], 'b')).toThrow(/Unhandled end_reason: vanished/) }) })