import { describe, it, expect } from 'vitest' import { computeYpt, buildTierClassifier, annotate } from '../src/common/material-ranking' const M = (methanal: number|null, tvoc: number|null, benzene: number|null, toluene: number|null, pxylene: number|null) => ({ methanal_be_area: methanal, tvoc_be_area: tvoc, benzene_be_area: benzene, toluene_be_area: toluene, p_xylene_be_area: pxylene }) describe('computeYpt', () => { it('按权重求和', () => { // 1*1.8 + 2*0.25 + 3*1 + 4*0.45 + 5*0.38 = 1.8+0.5+3+1.8+1.9 = 9 expect(computeYpt(M(1, 2, 3, 4, 5))).toBeCloseTo(9, 6) }) it('任一缺值返回 null', () => { expect(computeYpt(M(1, null, 3, 4, 5))).toBeNull() expect(computeYpt(M(1, 2, 3, 4, undefined as never))).toBeNull() }) }) describe('buildTierClassifier', () => { it('全库三分位: 6 个材料 -> 2A 2B 2C', () => { // ypt 分别为 1..6(每个只用 methanal 权重 1.8,但相对顺序即 methanal 顺序) const all = [1, 2, 3, 4, 5, 6].map(v => M(v, 0, 0, 0, 0)) const classify = buildTierClassifier(all) const ypts = all.map(computeYpt) expect(ypts.map(classify)).toEqual(['A', 'A', 'B', 'B', 'C', 'C']) }) it('缺值材料不参与分位、classify(null)=null', () => { const all = [M(1,0,0,0,0), M(2,0,0,0,0), M(3,0,0,0,0), M(1,null,0,0,0)] const classify = buildTierClassifier(all) expect(classify(null)).toBeNull() }) it('相同 Ypt 落同档(并列不强拆)', () => { const all = [M(1,0,0,0,0), M(1,0,0,0,0), M(1,0,0,0,0)] const classify = buildTierClassifier(all) const tiers = all.map(m => classify(computeYpt(m))) expect(new Set(tiers).size).toBe(1) // 全相等 -> 同一档 }) it('小 n=1 -> 该材料为 A', () => { const all = [M(5,0,0,0,0)] const classify = buildTierClassifier(all) expect(classify(computeYpt(all[0]))).toBe('A') }) }) describe('annotate', () => { it('挂上 ypt 与 health_tier', () => { const all = [M(1,0,0,0,0), M(2,0,0,0,0), M(3,0,0,0,0)] const classify = buildTierClassifier(all) const out = annotate(all, classify) expect(out[0].health_tier).toBe('A') expect(out[0].ypt).toBeCloseTo(1.8, 6) expect(out[2].health_tier).toBe('C') }) })