split-relay.test.js 2.68 KB
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/)
  })
})