"""Unit tests for deterministic end-cycle root-cause candidates.""" from django.test import SimpleTestCase from apps.operations.services import root_cause as rc class RootCauseHypothesisTests(SimpleTestCase): def test_early_mortality_plus_cold_hours_yields_brooding_suhu(self): graded = { "analyses": { "mortality": { "status": "critical", "severity_label": "SANGAT TINGGI", "actual_pct": 9.5, "message": "Mortalitas kumulatif 9.50% adalah SANGAT TINGGI", } } } context = { "weekly_summaries": [ { "minggu_ke": 1, "mortalitas_delta_persen": 2.5, "jam_suhu_di_bawah_standar": 12, }, {"minggu_ke": 2, "mortalitas_delta_persen": 1.2, "jam_suhu_di_bawah_standar": 6}, ] } hyps = rc.build_root_cause_hypotheses(graded, context) self.assertTrue(hyps) self.assertEqual(hyps[0]["id"], "brooding_suhu") self.assertEqual(hyps[0]["confidence"], "high") self.assertEqual(hyps[0]["fase"], "brooding") def test_fcr_and_bw_lag_yields_pakan(self): graded = { "analyses": { "fcr": {"status": "critical", "message": "FCR di atas standar"}, "bw": { "status": "warning", "direction": "di_bawah_standar", "message": "Bobot di bawah standar", }, "mortality": {"status": "ok", "message": "Mortalitas normal"}, } } hyps = rc.build_root_cause_hypotheses(graded, {}) ids = [h["id"] for h in hyps] self.assertIn("pakan_akses", ids) def test_high_mortality_alone_is_low_confidence_biosecurity(self): graded = { "analyses": { "mortality": { "status": "critical", "actual_pct": 9.49, "message": "Mortalitas kumulatif 9.49% adalah SANGAT TINGGI", } } } hyps = rc.build_root_cause_hypotheses(graded, {}) self.assertEqual(len(hyps), 1) self.assertEqual(hyps[0]["id"], "penyakit_biosekuriti") self.assertEqual(hyps[0]["confidence"], "low") class NextCycleActionsTests(SimpleTestCase): def test_actions_map_from_top_hypotheses(self): hyps = [ { "id": "brooding_suhu", "hipotesis": "Brooding / kontrol suhu", "confidence": "high", "fase": "brooding", "bukti": [], }, { "id": "pakan_akses", "hipotesis": "Pakan / akses pakan", "confidence": "med", "fase": "growth", "bukti": [], }, ] actions = rc.build_next_cycle_actions(hyps) self.assertEqual(len(actions), 2) self.assertEqual(actions[0]["fase"], "brooding") self.assertIn("suhu", actions[0]["aksi"].lower()) self.assertTrue(actions[0]["metrik_pantau"]) class SanitizeEndCycleTests(SimpleTestCase): def test_drops_invented_causes(self): hyps = [ { "id": "penyakit_biosekuriti", "hipotesis": "Penyakit / biosekuriti", "confidence": "low", "fase": "brooding", "bukti": ["mort high"], } ] actions = rc.build_next_cycle_actions(hyps) parsed = { "kesimpulan": "Ada masalah mortalitas", "masalah": ["Mortalitas tinggi"], "akar_penyebab": [ {"id": "alien_cause", "hipotesis": "Serangan alien", "bukti": ["X"]}, { "id": "penyakit_biosekuriti", "hipotesis": "Penyakit / biosekuriti", "bukti": ["Nekropsi diperlukan"], }, ], "perbaikan_siklus_berikutnya": [ {"id": "alien_cause", "fase": "brooding", "aksi": "Do nothing"}, { "id": "penyakit_biosekuriti", "fase": "brooding", "aksi": "Perketat biosekuriti custom", "metrik_pantau": "mortalitas harian", }, ], "insight": "Fokus biosekuriti", } out = rc.sanitize_end_cycle_payload( parsed, hypotheses=hyps, actions=actions, masalah=["Mortalitas tinggi"] ) ids = [a["id"] for a in out["akar_penyebab"]] self.assertEqual(ids, ["penyakit_biosekuriti"]) self.assertNotIn("alien_cause", [p["id"] for p in out["perbaikan_siklus_berikutnya"]]) self.assertIn("biosekuriti", out["perbaikan_siklus_berikutnya"][0]["aksi"].lower()) def test_format_embeds_json_marker(self): payload = rc.local_fallback_end_cycle( { "analyses": { "mortality": { "status": "critical", "actual_pct": 9.0, "message": "Mortalitas kritis", } } }, {}, ) text = rc.format_end_cycle_insight_text(payload) self.assertIn(rc.END_CYCLE_JSON_MARKER, text) parsed = rc.parse_embedded_end_cycle(text) self.assertIsNotNone(parsed) self.assertEqual(parsed["kesimpulan"], payload["kesimpulan"])