from __future__ import annotations from datetime import date from types import SimpleNamespace from unittest.mock import patch import uuid from django.test import override_settings from farm_data.models import SensorData from integration_tests.base import IntegrationAPITestCase from location_data.models import AnalysisGridCell, AnalysisGridObservation, RemoteSensingRun @override_settings(ROOT_URLCONF="integration_tests.location_data_urls") class RemoteSensingApiFlowTests(IntegrationAPITestCase): def setUp(self) -> None: super().setUp() self.farm_uuid = uuid.UUID("11111111-1111-1111-1111-111111111111") self.farm = SensorData.objects.create( farm_uuid=self.farm_uuid, center_location=self.primary_location, sensor_payload={}, ) self.temporal_end = date(2026, 5, 9) self.temporal_start = date(2026, 4, 9) @patch("location_data.views.timezone.localdate", return_value=date(2026, 5, 10)) @patch("location_data.views.run_remote_sensing_analysis_task.delay") def test_remote_sensing_post_and_status_flow(self, mock_delay, _mock_localdate) -> None: task_id = uuid.UUID("9e7f3aaa-6d34-4428-a196-478af7a2d7f6") mock_delay.return_value = SimpleNamespace(id=str(task_id)) enqueue_response = self.client.post( "/api/location-data/remote-sensing/", data={ "farm_uuid": str(self.farm_uuid), "force_refresh": False, }, format="json", ) self.assertEqual(enqueue_response.status_code, 202, enqueue_response.json()) enqueue_payload = enqueue_response.json()["data"] self.assertEqual(enqueue_payload["status"], "processing") self.assertEqual(enqueue_payload["source"], "processing") self.assertEqual(enqueue_payload["task_id"], str(task_id)) self.assertEqual(enqueue_payload["temporal_extent"]["start_date"], "2026-04-09") self.assertEqual(enqueue_payload["temporal_extent"]["end_date"], "2026-05-09") run = RemoteSensingRun.objects.get(pk=enqueue_payload["run"]["id"]) self.assertEqual(run.status, RemoteSensingRun.STATUS_PENDING) self.assertEqual(run.temporal_start, self.temporal_start) self.assertEqual(run.temporal_end, self.temporal_end) self.assertEqual(run.metadata["task_id"], str(task_id)) mock_delay.assert_called_once() status_response = self.client.get(f"/api/location-data/remote-sensing/runs/{task_id}/status/") self.assertEqual(status_response.status_code, 200, status_response.json()) status_payload = status_response.json()["data"] self.assertEqual(status_payload["status"], "pending") self.assertEqual(status_payload["run"]["pipeline_status"], "pending") cell = AnalysisGridCell.objects.create( soil_location=self.primary_location, block_code="", cell_code="cell-1", chunk_size_sqm=900, geometry=self.primary_boundary, centroid_lat=f"{self.primary_lat:.6f}", centroid_lon=f"{self.primary_lon:.6f}", ) AnalysisGridObservation.objects.create( cell=cell, run=run, temporal_start=self.temporal_start, temporal_end=self.temporal_end, ndvi=0.61, ndwi=0.22, soil_vv=0.13, soil_vv_db=-8.860566, dem_m=1550.0, slope_deg=4.2, metadata={"backend_name": "openeo"}, ) run.status = RemoteSensingRun.STATUS_SUCCESS run.metadata = {**(run.metadata or {}), "stage": "completed", "selected_features": ["ndvi", "ndwi"]} run.save(update_fields=["status", "metadata", "updated_at"]) completed_response = self.client.get(f"/api/location-data/remote-sensing/runs/{task_id}/status/") self.assertEqual(completed_response.status_code, 200, completed_response.json()) completed_payload = completed_response.json()["data"] self.assertEqual(completed_payload["status"], "completed") self.assertEqual(completed_payload["summary"]["cell_count"], 1) self.assertEqual(completed_payload["summary"]["ndvi_mean"], 0.61) self.assertEqual(len(completed_payload["cells"]), 1) self.assertEqual(completed_payload["cells"][0]["cell_code"], "cell-1")