UPDATE
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
from datetime import date
|
||||
import uuid
|
||||
|
||||
from django.test import TestCase
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from location_data.models import SoilDepthData, SoilLocation
|
||||
from farm_data.models import SensorData
|
||||
from plant.models import Plant
|
||||
from weather.models import WeatherForecast
|
||||
|
||||
|
||||
def square_boundary_for_center(lat: float, lon: float, delta: float = 0.01) -> dict:
|
||||
return {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[lon - delta, lat - delta],
|
||||
[lon + delta, lat - delta],
|
||||
[lon + delta, lat + delta],
|
||||
[lon - delta, lat + delta],
|
||||
[lon - delta, lat - delta],
|
||||
]
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
class FarmDetailApiTests(TestCase):
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
self.location = SoilLocation.objects.create(
|
||||
latitude="35.700000",
|
||||
longitude="51.400000",
|
||||
farm_boundary={"type": "Polygon", "coordinates": []},
|
||||
)
|
||||
SoilDepthData.objects.create(
|
||||
soil_location=self.location,
|
||||
depth_label="0-5cm",
|
||||
clay=22.0,
|
||||
nitrogen=10.0,
|
||||
sand=40.0,
|
||||
)
|
||||
SoilDepthData.objects.create(
|
||||
soil_location=self.location,
|
||||
depth_label="5-15cm",
|
||||
clay=18.0,
|
||||
nitrogen=8.0,
|
||||
)
|
||||
self.weather = WeatherForecast.objects.create(
|
||||
location=self.location,
|
||||
forecast_date=date(2026, 4, 10),
|
||||
temperature_min=12.0,
|
||||
temperature_max=23.0,
|
||||
temperature_mean=18.0,
|
||||
precipitation=1.2,
|
||||
humidity_mean=52.0,
|
||||
)
|
||||
self.plant1 = Plant.objects.create(name="گوجهفرنگی")
|
||||
self.plant2 = Plant.objects.create(name="خیار")
|
||||
self.farm_uuid = uuid.uuid4()
|
||||
self.farm = SensorData.objects.create(
|
||||
farm_uuid=self.farm_uuid,
|
||||
center_location=self.location,
|
||||
weather_forecast=self.weather,
|
||||
sensor_payload={
|
||||
"sensor-7-1": {
|
||||
"soil_moisture": 33.5,
|
||||
"nitrogen": 99.0,
|
||||
}
|
||||
},
|
||||
)
|
||||
self.farm.plants.set([self.plant2, self.plant1])
|
||||
|
||||
def test_returns_farm_detail_and_prioritizes_sensor_metrics_over_soil(self):
|
||||
response = self.client.get(f"/api/farm-data/{self.farm_uuid}/detail/")
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
payload = response.json()["data"]
|
||||
|
||||
self.assertEqual(payload["farm_uuid"], str(self.farm_uuid))
|
||||
self.assertEqual(payload["center_location"]["id"], self.location.id)
|
||||
self.assertEqual(payload["weather"]["id"], self.weather.id)
|
||||
self.assertEqual(
|
||||
payload["sensor_payload"]["sensor-7-1"]["soil_moisture"],
|
||||
33.5,
|
||||
)
|
||||
|
||||
resolved_metrics = payload["soil"]["resolved_metrics"]
|
||||
metric_sources = payload["soil"]["metric_sources"]
|
||||
|
||||
self.assertEqual(resolved_metrics["nitrogen"], 99.0)
|
||||
self.assertEqual(metric_sources["nitrogen"], "sensor")
|
||||
self.assertEqual(resolved_metrics["clay"], 22.0)
|
||||
self.assertEqual(metric_sources["clay"], "soil")
|
||||
self.assertEqual(len(payload["soil"]["depths"]), 2)
|
||||
self.assertCountEqual(payload["plant_ids"], [self.plant1.id, self.plant2.id])
|
||||
self.assertEqual(len(payload["plants"]), 2)
|
||||
returned_plants = {item["id"]: item for item in payload["plants"]}
|
||||
self.assertEqual(returned_plants[self.plant1.id]["name"], self.plant1.name)
|
||||
self.assertEqual(returned_plants[self.plant2.id]["name"], self.plant2.name)
|
||||
self.assertIn("light", returned_plants[self.plant1.id])
|
||||
|
||||
def test_returns_404_when_farm_is_missing(self):
|
||||
response = self.client.get(f"/api/farm-data/{uuid.uuid4()}/detail/")
|
||||
|
||||
self.assertEqual(response.status_code, 404)
|
||||
self.assertEqual(response.json()["msg"], "farm یافت نشد.")
|
||||
|
||||
|
||||
class FarmDataUpsertApiTests(TestCase):
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
self.location = SoilLocation.objects.create(
|
||||
latitude="35.710000",
|
||||
longitude="51.410000",
|
||||
)
|
||||
self.boundary = square_boundary_for_center(35.71, 51.41)
|
||||
self.weather = WeatherForecast.objects.create(
|
||||
location=self.location,
|
||||
forecast_date=date(2026, 4, 11),
|
||||
temperature_min=11.0,
|
||||
temperature_max=24.0,
|
||||
temperature_mean=17.5,
|
||||
)
|
||||
|
||||
def test_post_creates_farm_data_with_explicit_farm_uuid(self):
|
||||
farm_uuid = uuid.uuid4()
|
||||
|
||||
response = self.client.post(
|
||||
"/api/farm-data/",
|
||||
data={
|
||||
"farm_uuid": str(farm_uuid),
|
||||
"farm_boundary": self.boundary,
|
||||
"sensor_payload": {
|
||||
"sensor-7-1": {
|
||||
"soil_moisture": 31.2,
|
||||
"nitrogen": 18.0,
|
||||
}
|
||||
},
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(response.json()["data"]["farm_uuid"], str(farm_uuid))
|
||||
self.assertEqual(response.json()["data"]["center_location_id"], self.location.id)
|
||||
self.assertEqual(response.json()["data"]["weather_forecast_id"], self.weather.id)
|
||||
|
||||
farm = SensorData.objects.get(farm_uuid=farm_uuid)
|
||||
self.assertEqual(farm.center_location_id, self.location.id)
|
||||
self.assertEqual(farm.weather_forecast_id, self.weather.id)
|
||||
self.assertEqual(
|
||||
farm.sensor_payload["sensor-7-1"]["soil_moisture"],
|
||||
31.2,
|
||||
)
|
||||
|
||||
def test_post_requires_farm_uuid_in_request_body(self):
|
||||
response = self.client.post(
|
||||
"/api/farm-data/",
|
||||
data={
|
||||
"farm_boundary": self.boundary,
|
||||
"sensor_payload": {"sensor-7-1": {"soil_moisture": 31.2}},
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertIn("farm_uuid", response.json()["data"])
|
||||
|
||||
def test_post_creates_center_location_from_boundary_when_missing(self):
|
||||
farm_uuid = uuid.uuid4()
|
||||
|
||||
response = self.client.post(
|
||||
"/api/farm-data/",
|
||||
data={
|
||||
"farm_uuid": str(farm_uuid),
|
||||
"farm_boundary": {
|
||||
"corners": [
|
||||
{"lat": 50.0, "lon": 50.0},
|
||||
{"lat": 50.0, "lon": 50.02},
|
||||
{"lat": 50.02, "lon": 50.02},
|
||||
{"lat": 50.02, "lon": 50.0},
|
||||
]
|
||||
},
|
||||
"sensor_payload": {"sensor-7-1": {"soil_moisture": 40.0}},
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 201)
|
||||
farm = SensorData.objects.get(farm_uuid=farm_uuid)
|
||||
self.assertIsNotNone(farm.center_location_id)
|
||||
self.assertEqual(str(farm.center_location.latitude), "50.010000")
|
||||
self.assertEqual(str(farm.center_location.longitude), "50.010000")
|
||||
self.assertIsNone(farm.weather_forecast_id)
|
||||
Reference in New Issue
Block a user