文件
OmniCard/backend/test/modules/team/finance-analytics.test.ts

86 行
3.8 KiB
TypeScript

import { TeamFinanceAnalyticsService } from '../../../src/modules/team/service/finance-analytics';
import { AdminTeamBillingController } from '../../../src/modules/team/controller/admin/billing';
import { TeamInvoiceEntity } from '../../../src/modules/team/entity/invoice';
import { TeamSubscriptionEntity } from '../../../src/modules/team/entity/subscription';
describe('【财务与运营新账统计】TeamFinanceAnalyticsService 单元测试 (TDD Red -> Green)', () => {
let service: TeamFinanceAnalyticsService;
beforeEach(() => {
service = new TeamFinanceAnalyticsService();
});
it('1. 必须提供 getOverviewAnalytics 接口并返回符合契约的数据结构', async () => {
const result = await service.getOverviewAnalytics({ period: 'month' });
expect(result).toBeDefined();
expect(result.period).toBe('month');
expect(result.summary).toBeDefined();
// CFO 指标
expect(typeof result.summary.totalInvoiced).toBe('number');
expect(typeof result.summary.totalCollected).toBe('number');
expect(typeof result.summary.pendingReconciliation).toBe('number');
expect(typeof result.summary.overdueAmount).toBe('number');
expect(typeof result.summary.refundAmount).toBe('number');
expect(typeof result.summary.estimatedTax).toBe('number');
// COO 指标
expect(typeof result.summary.mrr).toBe('number');
expect(typeof result.summary.arr).toBe('number');
expect(typeof result.summary.newAccountsCount).toBe('number');
expect(typeof result.summary.payingAccountsCount).toBe('number');
expect(typeof result.summary.expansionRate).toBe('number');
expect(typeof result.summary.churnRate).toBe('number');
// 多币种归集
expect(Array.isArray(result.currencyBreakdown)).toBe(true);
expect(result.currencyBreakdown.length).toBeGreaterThanOrEqual(1);
// 账龄与坏账
expect(Array.isArray(result.agingAnalysis)).toBe(true);
expect(result.agingAnalysis.some((item) => item.range.includes('0-30'))).toBe(true);
// 套餐分布
expect(Array.isArray(result.planDistribution)).toBe(true);
expect(result.planDistribution.some((item) => item.plan === 'pro')).toBe(true);
// 出海区域分布
expect(Array.isArray(result.regionalRevenue)).toBe(true);
// 流水明细
expect(Array.isArray(result.recentLedger)).toBe(true);
});
it('2. 能够按指定周期 (today / week / month / quarter / year / all) 准确过滤统计', async () => {
const todayRes = await service.getOverviewAnalytics({ period: 'today' });
expect(todayRes.period).toBe('today');
const yearRes = await service.getOverviewAnalytics({ period: 'year' });
expect(yearRes.period).toBe('year');
expect(yearRes.summary.totalInvoiced).toBeGreaterThanOrEqual(todayRes.summary.totalInvoiced);
});
it('3. 能够按多币种 (USD / EUR / GBP / CNY) 进行精准汇率或单币种透视过滤', async () => {
const usdRes = await service.getOverviewAnalytics({ currency: 'USD' });
expect(usdRes).toBeDefined();
const cnyRes = await service.getOverviewAnalytics({ currency: 'CNY' });
expect(cnyRes).toBeDefined();
});
it('4. 账龄分析必须具备合理的风险分层 (低风险、中风险、高风险)', async () => {
const result = await service.getOverviewAnalytics({ period: 'all' });
const ranges = result.agingAnalysis.map((r) => r.riskLevel);
expect(ranges).toContain('low');
});
it('5. 后台 Billing 控制器必须挂载 /analytics 路由并返回标准 ok 响应', async () => {
const controller = new AdminTeamBillingController();
controller.teamFinanceAnalyticsService = service;
(controller as any).ok = (data: any) => ({ code: 1000, message: 'success', data });
const response = await (controller as any).postAnalytics({ period: 'month' });
expect(response.code).toBe(1000);
expect(response.data.summary.mrr).toBeDefined();
});
});