2026-04-25 17:22:41 +03:30
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import uuid
|
|
|
|
|
from unittest.mock import patch
|
|
|
|
|
|
|
|
|
|
from django.test import override_settings
|
|
|
|
|
|
|
|
|
|
from farm_data.models import ParameterUpdateLog, SensorData, SensorParameter
|
|
|
|
|
from integration_tests.base import IntegrationAPITestCase
|
|
|
|
|
from plant.models import Plant
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@override_settings(ROOT_URLCONF="config.urls")
|
|
|
|
|
class FarmManagementJourneyTests(IntegrationAPITestCase):
|
|
|
|
|
def test_full_management_journey_persists_farm_related_records(self) -> None:
|
|
|
|
|
primary_method = self.create_irrigation_method_via_api("Drip Prime")
|
|
|
|
|
backup_method = self.create_irrigation_method_via_api(
|
|
|
|
|
"Sprinkler Backup",
|
|
|
|
|
category="pressure",
|
|
|
|
|
water_efficiency_percent=78.0,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
irrigation_list_response = self.client.get("/api/irrigation/")
|
|
|
|
|
self.assertEqual(irrigation_list_response.status_code, 200)
|
|
|
|
|
self.assertGreaterEqual(len(irrigation_list_response.json()["data"]), 2)
|
|
|
|
|
irrigation_detail_response = self.client.get(f"/api/irrigation/{primary_method['id']}/")
|
|
|
|
|
self.assertEqual(irrigation_detail_response.status_code, 200)
|
|
|
|
|
self.assertEqual(irrigation_detail_response.json()["data"]["name"], "Drip Prime")
|
|
|
|
|
|
|
|
|
|
moisture_parameter = self.create_sensor_parameter_via_api()
|
|
|
|
|
self.assertEqual(moisture_parameter["action"], ParameterUpdateLog.ACTION_ADDED)
|
|
|
|
|
self.assertTrue(
|
|
|
|
|
SensorParameter.objects.filter(sensor_key="sensor-7-1", code="soil_moisture").exists()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
moisture_parameter_update = self.create_sensor_parameter_via_api(
|
|
|
|
|
metadata={"min": 5, "max": 85, "ui": "gauge"},
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(moisture_parameter_update["action"], ParameterUpdateLog.ACTION_MODIFIED)
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
ParameterUpdateLog.objects.filter(parameter__code="soil_moisture").count(),
|
|
|
|
|
2,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
tomato = self.create_plant_via_api("Tomato")
|
|
|
|
|
cucumber = self.create_plant_via_api("Cucumber", watering="daily")
|
|
|
|
|
removable_plant = self.create_plant_via_api("Remove Plant")
|
|
|
|
|
|
|
|
|
|
plants_list_response = self.client.get("/api/plants/")
|
|
|
|
|
self.assertEqual(plants_list_response.status_code, 200)
|
|
|
|
|
returned_names = {item["name"] for item in plants_list_response.json()["data"]}
|
|
|
|
|
self.assertTrue({"Tomato", "Cucumber", "Remove Plant"}.issubset(returned_names))
|
|
|
|
|
|
2026-04-28 04:11:49 +03:30
|
|
|
plant_catalog = self.create_plant_via_api(
|
|
|
|
|
"Pepper",
|
|
|
|
|
growth_stage="",
|
|
|
|
|
icon="sprout",
|
|
|
|
|
)
|
|
|
|
|
Plant.objects.filter(pk=plant_catalog["id"]).update(growth_stage="", icon="")
|
|
|
|
|
plant_names_response = self.client.get("/api/plants/names/")
|
|
|
|
|
self.assertEqual(plant_names_response.status_code, 200)
|
|
|
|
|
plant_names_payload = {
|
|
|
|
|
item["name"]: item for item in plant_names_response.json()["data"]
|
|
|
|
|
}
|
|
|
|
|
self.assertEqual(plant_names_payload["Pepper"]["icon"], "leaf")
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
plant_names_payload["Pepper"]["growth_stages"],
|
|
|
|
|
["initial", "vegetative", "flowering", "fruiting", "maturity"],
|
|
|
|
|
)
|
|
|
|
|
pepper = Plant.objects.get(pk=plant_catalog["id"])
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
pepper.growth_stage,
|
|
|
|
|
"initial, vegetative, flowering, fruiting, maturity",
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(pepper.icon, "leaf")
|
|
|
|
|
|
2026-04-25 17:22:41 +03:30
|
|
|
plant_patch_response = self.client.patch(
|
|
|
|
|
f"/api/plants/{tomato['id']}/",
|
|
|
|
|
data={"growth_stage": "flowering", "watering": "daily"},
|
|
|
|
|
format="json",
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(plant_patch_response.status_code, 200)
|
|
|
|
|
self.assertEqual(Plant.objects.get(pk=tomato["id"]).growth_stage, "flowering")
|
|
|
|
|
|
|
|
|
|
plant_put_response = self.client.put(
|
|
|
|
|
f"/api/plants/{cucumber['id']}/",
|
|
|
|
|
data={
|
|
|
|
|
"name": "Cucumber",
|
|
|
|
|
"light": "full sun",
|
|
|
|
|
"watering": "every day",
|
|
|
|
|
"soil": "sandy loam",
|
|
|
|
|
"temperature": "18-30C",
|
|
|
|
|
"growth_stage": "fruiting",
|
|
|
|
|
"planting_season": "spring",
|
|
|
|
|
"harvest_time": "70 days",
|
|
|
|
|
"spacing": "40 cm",
|
|
|
|
|
"fertilizer": "potassium rich",
|
|
|
|
|
},
|
|
|
|
|
format="json",
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(plant_put_response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
with patch(
|
|
|
|
|
"plant.views.fetch_plant_info_from_api",
|
|
|
|
|
return_value={
|
|
|
|
|
"name": "Tomato",
|
|
|
|
|
"light": "full sun",
|
|
|
|
|
"watering": "daily",
|
|
|
|
|
"soil": "loamy",
|
|
|
|
|
"temperature": "20-28C",
|
|
|
|
|
"growth_stage": "flowering",
|
|
|
|
|
"planting_season": "spring",
|
|
|
|
|
"harvest_time": "90 days",
|
|
|
|
|
"spacing": "50 cm",
|
|
|
|
|
"fertilizer": "balanced NPK",
|
|
|
|
|
},
|
|
|
|
|
):
|
|
|
|
|
plant_fetch_response = self.client.post(
|
|
|
|
|
"/api/plants/fetch-info/",
|
|
|
|
|
data={"name": "Tomato"},
|
|
|
|
|
format="json",
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(plant_fetch_response.status_code, 200)
|
|
|
|
|
self.assertEqual(plant_fetch_response.json()["data"]["name"], "Tomato")
|
|
|
|
|
|
|
|
|
|
plant_delete_response = self.client.delete(f"/api/plants/{removable_plant['id']}/")
|
|
|
|
|
self.assertEqual(plant_delete_response.status_code, 200)
|
|
|
|
|
self.assertFalse(Plant.objects.filter(pk=removable_plant["id"]).exists())
|
|
|
|
|
|
|
|
|
|
farm_uuid = uuid.uuid4()
|
|
|
|
|
created_farm = self.upsert_farm_via_api(
|
|
|
|
|
farm_uuid=farm_uuid,
|
|
|
|
|
plant_ids=[tomato["id"], cucumber["id"]],
|
|
|
|
|
irrigation_method_id=primary_method["id"],
|
|
|
|
|
sensor_payload={
|
|
|
|
|
"sensor-7-1": {
|
|
|
|
|
"soil_moisture": 41.2,
|
|
|
|
|
"soil_temperature": 23.4,
|
|
|
|
|
"soil_ph": 6.8,
|
|
|
|
|
"electrical_conductivity": 1.1,
|
|
|
|
|
"nitrogen": 17.0,
|
|
|
|
|
"phosphorus": 12.5,
|
|
|
|
|
"potassium": 21.0,
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(created_farm["farm_uuid"], str(farm_uuid))
|
|
|
|
|
farm_record = SensorData.objects.get(farm_uuid=farm_uuid)
|
|
|
|
|
self.assertCountEqual(
|
|
|
|
|
list(farm_record.plants.values_list("id", flat=True)),
|
|
|
|
|
[tomato["id"], cucumber["id"]],
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(farm_record.irrigation_method_id, primary_method["id"])
|
|
|
|
|
self.assertEqual(farm_record.center_location_id, self.primary_location.id)
|
|
|
|
|
|
|
|
|
|
updated_farm = self.upsert_farm_via_api(
|
|
|
|
|
farm_uuid=farm_uuid,
|
|
|
|
|
plant_ids=[tomato["id"]],
|
|
|
|
|
irrigation_method_id=backup_method["id"],
|
|
|
|
|
sensor_payload={
|
|
|
|
|
"sensor-7-1": {
|
|
|
|
|
"nitrogen": 19.5,
|
|
|
|
|
"soil_moisture": 44.0,
|
|
|
|
|
},
|
|
|
|
|
"leaf-sensor": {
|
|
|
|
|
"leaf_wetness": 11.0,
|
|
|
|
|
"leaf_temperature": 21.3,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(updated_farm["irrigation_method_id"], backup_method["id"])
|
|
|
|
|
|
|
|
|
|
farm_record.refresh_from_db()
|
|
|
|
|
self.assertEqual(farm_record.irrigation_method_id, backup_method["id"])
|
|
|
|
|
self.assertCountEqual(list(farm_record.plants.values_list("id", flat=True)), [tomato["id"]])
|
|
|
|
|
self.assertEqual(farm_record.sensor_payload["sensor-7-1"]["soil_temperature"], 23.4)
|
|
|
|
|
self.assertEqual(farm_record.sensor_payload["sensor-7-1"]["soil_moisture"], 44.0)
|
|
|
|
|
self.assertEqual(farm_record.sensor_payload["sensor-7-1"]["nitrogen"], 19.5)
|
|
|
|
|
self.assertEqual(farm_record.sensor_payload["leaf-sensor"]["leaf_wetness"], 11.0)
|
2026-05-05 01:46:10 +03:30
|
|
|
self.assertTrue(
|
|
|
|
|
SensorParameter.objects.filter(sensor_key="leaf-sensor", code="leaf_wetness").exists()
|
|
|
|
|
)
|
2026-04-25 17:22:41 +03:30
|
|
|
|
|
|
|
|
farm_detail_response = self.client.get(f"/api/farm-data/{farm_uuid}/detail/")
|
|
|
|
|
self.assertEqual(farm_detail_response.status_code, 200)
|
|
|
|
|
farm_detail = farm_detail_response.json()["data"]
|
|
|
|
|
self.assertEqual(farm_detail["center_location"]["id"], self.primary_location.id)
|
|
|
|
|
self.assertEqual(farm_detail["irrigation_method_id"], backup_method["id"])
|
|
|
|
|
self.assertEqual(farm_detail["plant_ids"], [tomato["id"]])
|
|
|
|
|
self.assertEqual(farm_detail["plants"][0]["name"], "Tomato")
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
farm_detail["sensor_payload"]["leaf-sensor"]["leaf_temperature"],
|
|
|
|
|
21.3,
|
|
|
|
|
)
|
2026-05-05 01:46:10 +03:30
|
|
|
self.assertCountEqual(
|
|
|
|
|
[item["code"] for item in farm_detail["sensor_schema"]["leaf-sensor"]],
|
|
|
|
|
["leaf_temperature", "leaf_wetness"],
|
|
|
|
|
)
|