This commit is contained in:
2026-04-28 04:11:49 +03:30
parent 10186a0e4c
commit 8471d648a3
15 changed files with 1444 additions and 140 deletions
+16 -9
View File
@@ -98,7 +98,11 @@ class RecommendationServiceDefaultsTests(TestCase):
"label": "تغذیه نگهدارنده",
"score": 72.0,
"expected_yield_index": 78.0,
"fertilizer_type": "20-20-20",
"amount_kg_per_ha": 45.0,
"application_method": "کودآبیاری",
"timing": "صبح زود",
"reasoning": ["برای نگهداری تعادل تغذیه ای گزینه سبک تری است."],
}
],
}
@@ -203,7 +207,7 @@ class RecommendationServiceDefaultsTests(TestCase):
self.build_fertilization_optimizer_result()
)
mock_response = Mock()
mock_response.choices = [Mock(message=Mock(content='{"sections": [{"type": "recommendation", "title": "برنامه", "icon": "leaf", "content": "مصرف انجام شود", "fertilizerType": "20-20-20", "amount": "45 کیلوگرم در هکتار", "applicationMethod": "کودآبیاری", "timing": "صبح", "validityPeriod": "5 روز", "expandableExplanation": "توضیح"}, {"type": "list", "title": "هشدار", "icon": "list", "items": ["مورد 1"]}, {"type": "warning", "title": "هشدار", "icon": "alert-triangle", "content": "از اختلاط نامناسب خودداری شود."}]}'))]
mock_response.choices = [Mock(message=Mock(content='{"status": "success", "data": {"primary_recommendation": {"display_title": "کود کامل 20-20-20", "reasoning": "توضیح", "summary": "مصرف انجام شود"}, "nutrient_analysis": {"macro": [{"key": "n", "name": "نیتروژن (N)", "value": 20, "unit": "percent", "description": "..."}, {"key": "p", "name": "فسفر (P)", "value": 20, "unit": "percent", "description": "..."}, {"key": "k", "name": "پتاسیم (K)", "value": 20, "unit": "percent", "description": "..."}], "micro": []}, "application_guide": {"safety_warning": "از اختلاط نامناسب خودداری شود.", "steps": [{"step_number": 1, "title": "آماده سازی", "description": "مورد 1"}]}, "alternative_recommendations": []}}'))]
mock_get_chat_client.return_value.chat.completions.create.return_value = mock_response
result = get_fertilization_recommendation(
@@ -211,16 +215,17 @@ class RecommendationServiceDefaultsTests(TestCase):
growth_stage="رویشی",
)
self.assertEqual(result["sections"][0]["fertilizerType"], "20-20-20")
self.assertEqual(result["data"]["primary_recommendation"]["npk_ratio"]["label"], "20-20-20")
self.assertEqual(result["data"]["primary_recommendation"]["dosage"]["base_amount_per_hectare"], 65.0)
mock_build_plant_text.assert_called_once_with("گوجه‌فرنگی", "رویشی")
self.assertEqual(result["simulation_optimizer"]["engine"], "crop_simulation_heuristic")
self.assertEqual(result["sections"][2]["content"], "از اختلاط نامناسب خودداری شود.")
self.assertEqual(result["data"]["application_guide"]["safety_warning"], "از اختلاط نامناسب خودداری شود.")
@patch("rag.services.fertilization.build_plant_text", return_value="plant text")
@patch("rag.services.fertilization.build_rag_context", return_value="")
@patch("rag.services.fertilization._get_optimizer")
@patch("rag.services.fertilization.get_chat_client")
def test_fertilization_recommendation_raises_when_llm_returns_invalid_payload(
def test_fertilization_recommendation_falls_back_to_optimizer_when_llm_returns_invalid_payload(
self,
mock_get_chat_client,
mock_get_optimizer,
@@ -234,8 +239,10 @@ class RecommendationServiceDefaultsTests(TestCase):
mock_response.choices = [Mock(message=Mock(content="not-json"))]
mock_get_chat_client.return_value.chat.completions.create.return_value = mock_response
with self.assertRaises(ValueError):
get_fertilization_recommendation(
farm_uuid=str(self.farm_uuid),
growth_stage="رویشی",
)
result = get_fertilization_recommendation(
farm_uuid=str(self.farm_uuid),
growth_stage="رویشی",
)
self.assertEqual(result["data"]["primary_recommendation"]["npk_ratio"]["label"], "20-20-20")
self.assertEqual(result["data"]["primary_recommendation"]["dosage"]["base_amount_per_hectare"], 65.0)