Files
Genarrative/src/components/rpg-entry/rpgEntryProfileFundsViewModel.test.ts
T

162 lines
4.1 KiB
TypeScript

import { expect, test } from 'vitest';
import type {
ProfileMembership,
ProfileRechargeProduct,
ProfileWalletLedgerEntry,
} from '../../../packages/shared/src/contracts/runtime';
import {
buildMembershipLabel,
buildRechargeProductValueLabel,
buildWalletLedgerPresentation,
formatRechargePrice,
formatWalletLedgerAmount,
getWalletLedgerSourceLabel,
} from './rpgEntryProfileFundsViewModel';
function buildLedgerEntry(
overrides: Partial<ProfileWalletLedgerEntry> = {},
): 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> = {},
): ProfileRechargeProduct {
return {
productId: 'points_60',
title: '60泥点',
priceCents: 600,
kind: 'points',
pointsAmount: 60,
bonusPoints: 60,
durationDays: 0,
badgeLabel: '首充双倍',
description: '首充送60泥点',
tier: 'normal',
...overrides,
};
}
function buildMembership(
overrides: Partial<ProfileMembership> = {},
): ProfileMembership {
return {
status: 'normal',
tier: 'normal',
startedAt: null,
expiresAt: null,
updatedAt: null,
...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('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',
});
expect(
buildWalletLedgerPresentation(
{ entries: [incomeEntry, outcomeEntry] },
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: '资产操作消耗',
},
],
});
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+60泥点',
);
expect(
buildRechargeProductValueLabel(
buildRechargeProduct({
kind: 'membership',
pointsAmount: 0,
bonusPoints: 0,
durationDays: 30,
}),
),
).toBe('30天');
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',
}),
() => '06/03 08:00',
),
).toBe('会员至 06/03 08:00');
});