import { expect, test } from 'vitest'; import type { ProfileMembership, ProfileRechargeProduct, ProfileWalletLedgerEntry, } from '../../../packages/shared/src/contracts/runtime'; import { buildMembershipCycleLabel, buildMembershipLabel, buildRechargeProductValueLabel, buildWalletLedgerPresentation, formatRechargePrice, formatWalletLedgerAmount, getWalletLedgerSourceLabel, } from './rpgEntryProfileFundsViewModel'; function buildLedgerEntry( overrides: Partial = {}, ): ProfileWalletLedgerEntry { return { id: 'ledger-1', amountDelta: 30, balanceAfter: 80, sourceType: 'invite_invitee_reward', createdAt: '2026-06-03T00:00:00.000Z', ...overrides, }; } function buildRechargeProduct( overrides: Partial = {}, ): ProfileRechargeProduct { return { productId: 'points_60', title: '60泥点', priceCents: 600, kind: 'points', pointsAmount: 60, bonusPoints: 0, durationDays: 0, badgeLabel: '', description: '60泥点', tier: 'normal', membershipPeriodPoints: 0, membershipPeriodDays: 0, membershipQueueLimit: 0, membershipDiscountBps: 0, ...overrides, }; } function buildMembership( overrides: Partial = {}, ): ProfileMembership { return { status: 'normal', tier: 'normal', startedAt: null, expiresAt: null, updatedAt: null, cycleStartedAt: null, cycleResetsAt: null, cycleGrantedPoints: 0, cycleRemainingPoints: 0, cyclePeriodDays: 30, ...overrides, }; } test('profile funds ViewModel formats ledger amount labels', () => { expect(formatWalletLedgerAmount(-1)).toBe('-1'); expect(formatWalletLedgerAmount(0)).toBe('0'); expect(formatWalletLedgerAmount(30)).toBe('+30'); }); test('profile funds ViewModel resolves ledger source labels with raw fallback', () => { expect(getWalletLedgerSourceLabel('asset_operation_consume')).toBe( '资产操作消耗', ); expect(getWalletLedgerSourceLabel('puzzle_author_incentive_claim')).toBe( '拼图作者奖励', ); expect(getWalletLedgerSourceLabel('recharge_refund_recovery')).toBe( '充值退款追回', ); expect(getWalletLedgerSourceLabel('future_source')).toBe('future_source'); expect(getWalletLedgerSourceLabel('')).toBe('未知来源'); }); test('profile funds ViewModel builds wallet ledger presentation', () => { const incomeEntry = buildLedgerEntry({ id: 'ledger-income', amountDelta: 30, balanceAfter: 80, sourceType: 'puzzle_author_incentive_claim', }); const outcomeEntry = buildLedgerEntry({ id: 'ledger-outcome', amountDelta: -1, balanceAfter: 79, sourceType: 'asset_operation_consume', }); const recoveryEntry = buildLedgerEntry({ id: 'ledger-recovery', amountDelta: -30, balanceAfter: 49, sourceType: 'recharge_refund_recovery', }); expect( buildWalletLedgerPresentation( { entries: [incomeEntry, outcomeEntry, recoveryEntry] }, 12, ), ).toEqual({ balance: 80, balanceLabel: '80泥点', entries: [ { amountLabel: '+30', balanceLabel: '余额 80', createdAt: '2026-06-03T00:00:00.000Z', id: 'ledger-income', isIncome: true, sourceLabel: '拼图作者奖励', }, { amountLabel: '-1', balanceLabel: '余额 79', createdAt: '2026-06-03T00:00:00.000Z', id: 'ledger-outcome', isIncome: false, sourceLabel: '资产操作消耗', }, { amountLabel: '-30', balanceLabel: '余额 49', createdAt: '2026-06-03T00:00:00.000Z', id: 'ledger-recovery', isIncome: false, sourceLabel: '充值退款追回', }, ], }); expect(buildWalletLedgerPresentation({ entries: [] }, 12)).toEqual({ balance: 12, balanceLabel: '12泥点', entries: [], }); }); test('profile funds ViewModel formats recharge product and membership labels', () => { expect(formatRechargePrice(600)).toBe('¥6'); expect(formatRechargePrice(650)).toBe('¥6.50'); expect(buildRechargeProductValueLabel(buildRechargeProduct())).toBe('60泥点'); expect( buildRechargeProductValueLabel( buildRechargeProduct({ productId: 'points_180', title: '180泥点', priceCents: 1800, pointsAmount: 180, bonusPoints: 90, badgeLabel: '首充加赠', description: '首充加赠90泥点', }), ), ).toBe('180+90泥点'); expect( buildRechargeProductValueLabel( buildRechargeProduct({ kind: 'membership', pointsAmount: 0, bonusPoints: 0, durationDays: 30, membershipPeriodPoints: 200, membershipPeriodDays: 30, }), ), ).toBe('每月200泥点'); expect( buildRechargeProductValueLabel( buildRechargeProduct({ kind: 'membership', pointsAmount: 0, bonusPoints: 0, durationDays: 7, membershipPeriodPoints: 80, membershipPeriodDays: 7, }), ), ).toBe('每周80泥点'); expect( buildRechargeProductValueLabel( buildRechargeProduct({ kind: 'membership', pointsAmount: 0, bonusPoints: 0, durationDays: 14, membershipPeriodPoints: 300, membershipPeriodDays: 14, }), ), ).toBe('每14天300泥点'); expect( buildRechargeProductValueLabel( buildRechargeProduct({ kind: 'membership', pointsAmount: 0, bonusPoints: 0, durationDays: 90, membershipPeriodPoints: 1200, membershipPeriodDays: 90, }), ), ).toBe('每90天1200泥点'); expect(buildMembershipLabel(buildMembership(), (value) => value)).toBe( '普通用户', ); expect( buildMembershipLabel( buildMembership({ status: 'active', expiresAt: null }), (value) => value, ), ).toBe('会员已生效'); expect( buildMembershipLabel( buildMembership({ status: 'active', expiresAt: '2026-06-03T00:00:00.000Z', cycleResetsAt: '2026-06-02T00:00:00.000Z', }), (value) => value === '2026-06-03T00:00:00.000Z' ? '06/03 08:00' : '06/02 08:00', ), ).toBe('会员至 06/03 08:00 · 06/02 08:00重置'); expect( buildMembershipCycleLabel( buildMembership({ status: 'active', tier: 'basic', cycleRemainingPoints: 120, cycleResetsAt: '2026-06-02T00:00:00.000Z', }), () => '06/02 08:00', ), ).toBe('本周期剩余 120 泥点 · 06/02 08:00重置'); });