import { describe, it, expect } from 'vitest'; import { calculateCp707Uniformity, getCp707UniformityStatus, CP707_UNIFORMITY_TARGET, } from '../weightUtils.ts'; /** * Keseragaman dihitung ulang dari `avg` + `Standard_deviasi` menurut CP 707 * (persen ayam dalam ±10% dari rata-rata), bukan diambil dari field * `uniformity` milik API `distribusi_bobot` yang memakai band ±5%. * * Nilai acuan di bawah berasal dari respons API sungguhan dan dicocokkan * dengan perhitungan referensi `math.erf` di Python. */ describe('calculateCp707Uniformity', () => { it.each([ // tanggal, avg (g), Standard_deviasi (g), harapan (%) ['2026-08-10', 1123.53, 161.3, 51.4], ['2026-08-05', 806.79, 26.87, 99.7], ['2026-08-04', 667.31, 139.71, 36.7], ['2026-06-18', 1324.8, 106.5, 78.6], ])('cocok dengan perhitungan referensi untuk %s', (_tanggal, avg, sd, harapan) => { expect(calculateCp707Uniformity(avg, sd)).toBeCloseTo(harapan as number, 0); }); it('tidak terpengaruh lebar bucket 50 g di awal siklus', () => { // Bobot 75 g: band ±10% hanya 15 g, jauh lebih sempit dari bucket 50 g milik // API. Perhitungan berbasis bucket gagal di sini; berbasis SD tidak. expect(calculateCp707Uniformity(75.4, 3.3)).toBeCloseTo(97.8, 0); }); it('mengembalikan 100 saat tidak ada variasi bobot', () => { expect(calculateCp707Uniformity(1000, 0)).toBe(100); }); it('hasilnya selalu berada di rentang 0-100', () => { for (const [avg, sd] of [ [1000, 1], [1000, 5000], [50, 0.1], ]) { const v = calculateCp707Uniformity(avg, sd); expect(v).not.toBeNull(); expect(v as number).toBeGreaterThanOrEqual(0); expect(v as number).toBeLessThanOrEqual(100); } }); it('mengembalikan null, bukan angka karangan, saat data tidak memadai', () => { expect(calculateCp707Uniformity(null, 10)).toBeNull(); expect(calculateCp707Uniformity(1000, null)).toBeNull(); expect(calculateCp707Uniformity(undefined, undefined)).toBeNull(); expect(calculateCp707Uniformity(0, 10)).toBeNull(); expect(calculateCp707Uniformity(-100, 10)).toBeNull(); expect(calculateCp707Uniformity(1000, -5)).toBeNull(); expect(calculateCp707Uniformity(NaN, 10)).toBeNull(); expect(calculateCp707Uniformity(1000, Infinity)).toBeNull(); }); it('naik saat sebaran menyempit', () => { const ketat = calculateCp707Uniformity(1000, 30) as number; const longgar = calculateCp707Uniformity(1000, 150) as number; expect(ketat).toBeGreaterThan(longgar); }); }); describe('getCp707UniformityStatus', () => { it('memakai ambang CP 707 (>80%), bukan ambang lama 85/75', () => { expect(getCp707UniformityStatus(CP707_UNIFORMITY_TARGET)).toBe('ok'); expect(getCp707UniformityStatus(82)).toBe('ok'); // 82 dulu berstatus "warning" dengan ambang lama 85. expect(getCp707UniformityStatus(75)).toBe('warning'); expect(getCp707UniformityStatus(70)).toBe('warning'); expect(getCp707UniformityStatus(69.9)).toBe('critical'); }); it('memperlakukan data tidak tersedia sebagai critical', () => { expect(getCp707UniformityStatus(null)).toBe('critical'); }); });