Files
dashboard/utils/__tests__/cp707Uniformity.test.ts
T

82 lines
3.1 KiB
TypeScript

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');
});
});