0baa8cf8cc
补齐退款人工复核、刷新恢复、会话隔离和旧冷备导入兼容 强化真实微信退款对账开关、重复环境变量和生产发布门禁 优化公开作品资产授权查询并保持实时撤销语义 统一后台微信渠道、退款追回流水和画布正式资源测试契约 补齐生成与素材侧栏回归测试
251 lines
6.5 KiB
TypeScript
251 lines
6.5 KiB
TypeScript
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> = {},
|
|
): 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: 0,
|
|
durationDays: 0,
|
|
badgeLabel: '',
|
|
description: '60泥点',
|
|
tier: 'normal',
|
|
membershipPeriodPoints: 0,
|
|
membershipPeriodDays: 0,
|
|
membershipQueueLimit: 0,
|
|
membershipDiscountBps: 0,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function buildMembership(
|
|
overrides: Partial<ProfileMembership> = {},
|
|
): 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重置');
|
|
});
|