import { fmtBalance, fmtPercent, fmtSize, fmtTokens, isMatrixReady, normProgress, usageOf, } from './format'; import { Agent } from './api/models'; /** * 纯函数单测 —— 格式化与派生口径 * * 依据 SPEC NFR-06(可测)与《数据访问层设计.md》§五(格式化唯一实现)。 * 这些口径曾在存量项目里出现 4 份重复实现,故必须有回归保护。 */ describe('format 纯函数', () => { describe('fmtSize', () => { it('按 1024 进制换算并保留 1 位小数', () => { expect(fmtSize(0)).toBe('0 B'); expect(fmtSize(512)).toBe('512 B'); expect(fmtSize(1024)).toBe('1.0 KB'); expect(fmtSize(1536)).toBe('1.5 KB'); expect(fmtSize(1024 * 1024)).toBe('1.0 MB'); }); it('空值返回占位符', () => { expect(fmtSize(undefined)).toBe('—'); }); }); describe('fmtTokens', () => { it('按 1000 进制分档(k / M)', () => { expect(fmtTokens(0)).toBe('0'); expect(fmtTokens(999)).toBe('999'); expect(fmtTokens(1500)).toBe('1.5k'); expect(fmtTokens(1_500_000)).toBe('1.5M'); }); it('空值返回占位符', () => { expect(fmtTokens(undefined)).toBe('—'); }); }); describe('fmtBalance(FR-07 AC-07.4:余额转人民币)', () => { it('有记账时显示 ¥ xx.xx(两位小数)', () => { expect(fmtBalance({ remainCny: 17842.31 })).toBe('¥ 17842.31'); expect(fmtBalance({ remainCny: 0 })).toBe('¥ 0.00'); expect(fmtBalance({ remainCny: 86.4 })).toBe('¥ 86.40'); }); it('无记账时显示占位符,不伪造 ¥0', () => { expect(fmtBalance(undefined)).toBe('—'); expect(fmtBalance({})).toBe('—'); }); it('负值如实显示为 -¥(不截断为 0,2026-11-11 余额口径事件)', () => { expect(fmtBalance({ remainCny: -76.12 })).toBe('-¥ 76.12'); expect(fmtBalance({ remainCny: -0.5 })).toBe('-¥ 0.50'); }); }); describe('normProgress(契约 §六-5:百分比 or 比例语义推断)', () => { it('0 { expect(normProgress(0.5)).toBe(50); expect(normProgress(0.25)).toBe(25); expect(normProgress(1)).toBe(100); }); it('>1 视为整数百分比,原样返回', () => { expect(normProgress(50)).toBe(50); expect(normProgress(100)).toBe(100); }); it('越界被夹到 [0,100](防脏数据撑破进度条)', () => { expect(normProgress(150)).toBe(100); expect(normProgress(-5)).toBe(0); }); it('0 与空值:0 返回 0,空值返回 undefined', () => { expect(normProgress(0)).toBe(0); expect(normProgress(undefined)).toBeUndefined(); expect(normProgress(NaN)).toBeUndefined(); }); }); describe('fmtPercent', () => { it('归一后带百分号', () => { expect(fmtPercent(0.5)).toBe('50%'); expect(fmtPercent(72)).toBe('72%'); }); it('空值返回占位符', () => { expect(fmtPercent(undefined)).toBe('—'); }); }); describe('usageOf / isMatrixReady(映射口径)', () => { it('usageOf 容忍缺失的 usage 字段', () => { expect(usageOf({ id: 'a', name: 'a', status: 'idle', matrixReady: false, unread: 0 })).toEqual( {}, ); }); it('isMatrixReady 采用**账号级**口径(matrixConfig.userId),不只看 homeserver 可达', () => { const base = { id: 'a', name: 'a', status: 'idle', unread: 0 } as unknown as Agent; // 未接入 expect(isMatrixReady({ ...base, matrixReady: false })).toBe(false); // 仅 channels.matrix=true 不算就绪(homeserver 可达 ≠ 该 Agent 有账号) expect(isMatrixReady({ ...base, channels: { matrix: true } })).toBe(false); // 账号级就绪:matrixConfig.userId 存在 expect( isMatrixReady({ ...base, channels: { matrixConfig: { userId: '@x:fmode.cn' } } }), ).toBe(true); // 映射层已判定就绪时直接放行 expect(isMatrixReady({ ...base, matrixReady: true })).toBe(true); }); }); });