import { describe, expect, it } from 'vitest'; import { buildDashboardInsightMetrics, scopeDashboardInsightMetrics, } from '../insightContextBuilders.ts'; import { getTargetWeightForAge } from '../weight.ts'; describe('buildDashboardInsightMetrics', () => { it('builds latest scalars and day-indexed weight series with targets', () => { const metrics = buildDashboardInsightMetrics({ kpis: [ { age: 10, fcr: 1.2, eef: 180, chicken_life: 9800, chicken_life_percentage: 98, iot_weight: 0, mortality_total: 200, harvest_total: 0, harvest_weight_total: 0, feed_total: 120, }, { age: 28, fcr: 1.55, eef: 260, chicken_life: 9500, chicken_life_percentage: 95, iot_weight: 1400, mortality_total: 500, harvest_total: 100, harvest_weight_total: 150_500, // grams feed_total: 400, }, ], weights: [ { age: 10, average_weight: 280, doc_weight: 40, uniformity: 82.5 }, { age: 28, average_weight: 1350, doc_weight: 40, uniformity: 78.1 }, ], currentDay: 30, docInCount: 10000, }); expect(metrics.hari_ke).toBe(28); expect(metrics.fcr).toBe(1.55); expect(metrics.eef).toBe(260); expect(metrics.ayam_hidup_ekor).toBe(9400); // 9500 life − 100 harvest expect(metrics.mortalitas_kumulatif_persen).toBe(5); expect(metrics.bobot_avg_gram).toBe(1400); expect(metrics.doc_in_ekor).toBe(10000); expect(metrics.kematian_kumulatif_ekor).toBe(500); expect(metrics.panen_kumulatif_ekor).toBe(100); expect(metrics.tonase_panen_kg).toBe(150.5); expect(metrics.uniformity_persen).toBe(78.1); expect(metrics.pakan_kumulatif_karung).toBe(400); expect(metrics.weightSeries).toHaveLength(2); expect(metrics.weightSeries[0]).toEqual({ hari: 10, actual: 280, target: getTargetWeightForAge(10, 40), }); expect(metrics.kpiSeries.map((row) => row.hari)).toEqual([10, 28]); }); it('falls back to weight average when iot_weight is missing', () => { const metrics = buildDashboardInsightMetrics({ kpis: [ { age: 7, fcr: 1.1, eef: 100, chicken_life: 9900, chicken_life_percentage: 99, iot_weight: 0, }, ], weights: [{ age: 7, average_weight: 190, doc_weight: 40 }], pakanKumulatifFallback: 55, }); expect(metrics.bobot_avg_gram).toBe(190); expect(metrics.pakan_kumulatif_karung).toBe(55); }); }); describe('scopeDashboardInsightMetrics', () => { const base = buildDashboardInsightMetrics({ kpis: [ { age: 7, fcr: 1.1, eef: 90, chicken_life: 9900, chicken_life_percentage: 99, iot_weight: 200, mortality_total: 100, harvest_total: 0, harvest_weight_total: 0, feed_total: 40, }, { age: 14, fcr: 1.3, eef: 150, chicken_life: 9800, chicken_life_percentage: 98, iot_weight: 450, mortality_total: 200, harvest_total: 50, harvest_weight_total: 80_000, // grams feed_total: 90, }, ], weights: [ { age: 7, average_weight: 200, doc_weight: 40, uniformity: 85 }, { age: 14, average_weight: 450, doc_weight: 40, uniformity: 80 }, { age: 21, average_weight: 800, doc_weight: 40, uniformity: 75 }, ], docInCount: 10000, }); it('filters weight series and uses exact-day KPI scalars', () => { const scoped = scopeDashboardInsightMetrics(base, 7); expect(scoped.hari_ke).toBe(7); expect(scoped.fcr).toBe(1.1); expect(scoped.bobot_avg_gram).toBe(200); expect(scoped.doc_in_ekor).toBe(10000); expect(scoped.kematian_kumulatif_ekor).toBe(100); expect(scoped.pakan_kumulatif_karung).toBe(40); expect(scoped.uniformity_persen).toBe(85); expect(scoped.weightSeries.map((row) => row.hari)).toEqual([7]); }); it('does not invent scalars for a day without KPI snapshot', () => { const scoped = scopeDashboardInsightMetrics(base, 21); expect(scoped.hari_ke).toBe(21); expect(scoped.fcr).toBeNull(); expect(scoped.eef).toBeNull(); expect(scoped.bobot_avg_gram).toBe(800); // from weight series at day 21 expect(scoped.kematian_kumulatif_ekor).toBeNull(); expect(scoped.doc_in_ekor).toBe(10000); // cycle constant still shown expect(scoped.weightSeries.map((row) => row.hari)).toEqual([7, 14, 21]); }); });