from django.test import TestCase, Client from django.urls import reverse from django.contrib.auth import get_user_model from django.contrib.contenttypes.models import ContentType from django.utils import timezone from assets.models import NatureRWIZ, NatureRWIAsset, ParkingSpot from interventions.models import Intervention from common.models import Thematic from .models import Inspection, ParkingSpotInspection, GiepInspection, InspectionDocument from .registry import get_inspection_partial_template User = get_user_model() class InspectionModelAndViewsTestCase(TestCase): def setUp(self): self.client = Client() self.user = User.objects.create_user( username='inspector_user', password='password123', first_name='Jean', last_name='Valjean' ) from common.models import UserConfig, Role, UserThematics admin_role, _ = Role.objects.get_or_create(name='admin') config = UserConfig.objects.create(user=self.user) config.roles.add(admin_role) self.water_thematic, _ = Thematic.objects.get_or_create( code='water', defaults={'name_fr': 'Gestion de l\'Eau / GIEP'} ) UserThematics.objects.create( user_config=config, thematic=self.water_thematic, can_view_assets=True, can_edit_assets=True ) self.thematic, _ = Thematic.objects.get_or_create( code='parking', defaults={'name_fr': 'Stationnement'} ) self.spot = ParkingSpot.objects.create( name_fr='Emplacement Vélo Test' ) self.spot_ct = ContentType.objects.get_for_model(ParkingSpot) self.rwiz = NatureRWIZ.objects.create( name_fr='Zone GIEP Test' ) self.rwiz_ct = ContentType.objects.get_for_model(NatureRWIZ) self.mission_interv = Intervention.objects.create( title="Mission inspection GIEP Nord", thematic=self.thematic, status="in_progress" ) self.work_interv = Intervention.objects.create( title="Travaux réfection par Prestataire A", thematic=self.thematic, status="done" ) def test_inspection_creation_with_interventions(self): insp = Inspection.objects.create( asset_content_type=self.spot_ct, asset_object_id=self.spot.id, inspector=self.user, mission_intervention=self.mission_interv, checked_intervention=self.work_interv, nature='technical', inspector_role='provider', result_status='compliant', notes="Tiers contrôle OK" ) self.assertTrue(insp.is_mission_inspection) self.assertTrue(insp.is_cross_check) self.assertEqual(insp.mission_intervention, self.mission_interv) self.assertEqual(insp.checked_intervention, self.work_interv) self.assertIn(insp, self.mission_interv.generated_inspections.all()) self.assertIn(insp, self.work_interv.quality_inspections.all()) def test_giep_inspection_model(self): giep_insp = GiepInspection.objects.create( asset_content_type=self.rwiz_ct, asset_object_id=self.rwiz.id, inspector=self.user, result_status='compliant', date_derniere_pluie='aujourdhui', orage_moins_une_heure=True, data={ 'ouvrages': [ {'local_id': '0', 'device_type': 'evc', 'name': 'Noue 1'} ] } ) self.assertEqual(giep_insp.detailed_inspection, giep_insp) self.assertIn("1 ouvrage(s) GIEP", giep_insp.details_summary) self.assertIn(str(giep_insp.get_date_derniere_pluie_display()), giep_insp.details_summary) def test_create_inspection_api(self): self.client.login(username='inspector_user', password='password123') url = reverse('inspections:create_inspection_api') response = self.client.post(url, { 'content_type_id': self.spot_ct.id, 'object_id': self.spot.id, 'result_status': 'compliant', 'nature': 'visual', 'inspector_role': 'provider', 'mission_intervention_id': self.mission_interv.id, 'checked_intervention_id': self.work_interv.id, 'notes': 'Inspection via API' }) self.assertEqual(response.status_code, 200) data = response.json() self.assertTrue(data['success']) insp = Inspection.objects.get(pk=data['inspection_id']) self.assertEqual(insp.mission_intervention, self.mission_interv) self.assertEqual(insp.checked_intervention, self.work_interv) def test_create_giep_inspection_api(self): self.client.login(username='inspector_user', password='password123') url = reverse('inspections:create_inspection_api') import json giep_json = json.dumps({ 'ouvrages': [ {'local_id': '0', 'device_type': 'evc', 'questions': {'engorgement': 'aucun'}} ] }) response = self.client.post(url, { 'content_type_id': self.rwiz_ct.id, 'object_id': self.rwiz.id, 'result_status': 'compliant', 'nature': 'visual', 'inspector_role': 'observer', 'date_derniere_pluie': 'moins_24h', 'orage_moins_une_heure': 'false', 'giep_data': giep_json, 'notes': 'Inspection GIEP via API' }) self.assertEqual(response.status_code, 200) data = response.json() self.assertTrue(data['success']) insp = GiepInspection.objects.get(pk=data['inspection_id']) self.assertEqual(insp.date_derniere_pluie, 'moins_24h') self.assertFalse(insp.orage_moins_une_heure) self.assertEqual(len(insp.data.get('ouvrages', [])), 1) def test_registry_giep_template(self): tmpl_rwiz = get_inspection_partial_template('naturerwiz') tmpl_asset = get_inspection_partial_template('naturerwiasset') self.assertEqual(tmpl_rwiz, 'inspections/components/asset_fields/_naturerwiz.html') self.assertIsNone(tmpl_asset) def test_registry_detail_templates(self): from .registry import get_inspection_detail_partial_template giep_insp = GiepInspection(asset=self.rwiz) parking_insp = ParkingSpotInspection(asset=self.spot) tmpl_giep = get_inspection_detail_partial_template(giep_insp) tmpl_parking = get_inspection_detail_partial_template(parking_insp) self.assertEqual(tmpl_giep, 'inspections/components/asset_details/_naturerwiz.html') self.assertEqual(tmpl_parking, 'inspections/components/asset_details/_parkingspot.html') def test_zirw_detail_view_renders_cleanly(self): self.client.login(username='inspector_user', password='password123') url = reverse('assets:zirw_detail', kwargs={'location_id': self.rwiz.id}) response = self.client.get(url) self.assertEqual(response.status_code, 200) def test_spatial_location_resolution_for_rwi_asset(self): from assets.templatetags.asset_tags import asset_location_link from django.contrib.gis.geos import Polygon, MultiPolygon, Point poly = Polygon(((0, 0), (0, 10), (10, 10), (10, 0), (0, 0)), srid=3812) rwiz_poly = NatureRWIZ.objects.create( code="RWIZ-TEST", name_fr="Zone Test Polygons", geom=MultiPolygon(poly, srid=3812) ) rwi_asset = NatureRWIAsset.objects.create( code="RWI-TEST-01", geom=Point(5, 5, srid=3812) ) ctx = asset_location_link({'request': None}, rwi_asset) self.assertEqual(ctx['location'], rwiz_poly) def test_can_edit_inspection_permissions(self): other_user = User.objects.create_user( username='regular_user', password='password123' ) insp = Inspection.objects.create( asset_content_type=self.spot_ct, asset_object_id=self.spot.id, inspector=self.user, result_status='compliant', notes="Initial note" ) # Author can edit self.assertTrue(insp.can_edit(self.user)) # Superuser can edit admin_user = User.objects.create_superuser( username='admin_superuser', password='password123' ) self.assertTrue(insp.can_edit(admin_user)) # Other non-author non-admin cannot edit self.assertFalse(insp.can_edit(other_user)) def test_update_inspection_api_author_success(self): insp = Inspection.objects.create( asset_content_type=self.spot_ct, asset_object_id=self.spot.id, inspector=self.user, result_status='compliant', notes="Initial note" ) initial_updated_at = insp.updated_at self.client.login(username='inspector_user', password='password123') url = reverse('inspections:update_inspection_api', kwargs={'inspection_id': insp.id}) response = self.client.post(url, { 'result_status': 'non_compliant', 'notes': 'Modifié par auteur', 'nature': 'detailed' }) self.assertEqual(response.status_code, 200) data = response.json() self.assertTrue(data['success']) insp.refresh_from_db() self.assertEqual(insp.result_status, 'non_compliant') self.assertEqual(insp.notes, 'Modifié par auteur') self.assertEqual(insp.nature, 'detailed') self.assertGreaterEqual(insp.updated_at, initial_updated_at) def test_update_inspection_api_forbidden(self): other_user = User.objects.create_user( username='other_user_stranger', password='password123' ) insp = Inspection.objects.create( asset_content_type=self.spot_ct, asset_object_id=self.spot.id, inspector=self.user, result_status='compliant', notes="Original note" ) self.client.login(username='other_user_stranger', password='password123') url = reverse('inspections:update_inspection_api', kwargs={'inspection_id': insp.id}) response = self.client.post(url, { 'result_status': 'damaged', 'notes': 'Hacked note' }) self.assertEqual(response.status_code, 403) data = response.json() self.assertFalse(data['success']) insp.refresh_from_db() self.assertEqual(insp.result_status, 'compliant') self.assertEqual(insp.notes, 'Original note') def test_update_parking_spot_inspection_api(self): from inspections.models import ParkingSpotInspection ps_insp = ParkingSpotInspection.objects.create( asset_content_type=self.spot_ct, asset_object_id=self.spot.id, inspector=self.user, result_status='compliant', notes="Initial parking inspection", verified_standard_stand_count=5, verified_cargo_stand_count=2, verified_marking_quality='good' ) self.client.login(username='inspector_user', password='password123') url = reverse('inspections:update_inspection_api', kwargs={'inspection_id': ps_insp.id}) response = self.client.post(url, { 'result_status': 'partially_compliant', 'verified_standard_stand_count': '10', 'verified_cargo_stand_count': '4', 'verified_drop_off_stand_count': '1', 'verified_has_scooter_drop_zone': 'true', 'verified_marking_quality': 'damaged', 'apply_physical_counts_to_asset': 'true' }) self.assertEqual(response.status_code, 200) data = response.json() self.assertTrue(data['success']) ps_insp.refresh_from_db() self.assertEqual(ps_insp.result_status, 'partially_compliant') self.assertEqual(ps_insp.verified_standard_stand_count, 10) self.assertEqual(ps_insp.verified_cargo_stand_count, 4) self.assertEqual(ps_insp.verified_drop_off_stand_count, 1) self.assertTrue(ps_insp.verified_has_scooter_drop_zone) self.assertEqual(ps_insp.verified_marking_quality, 'damaged') def test_delete_inspection_document_api(self): from inspections.models import InspectionDocument from django.core.files.uploadedfile import SimpleUploadedFile insp = Inspection.objects.create( asset_content_type=self.spot_ct, asset_object_id=self.spot.id, inspector=self.user, result_status='compliant' ) dummy_file = SimpleUploadedFile("test_doc.txt", b"file content", content_type="text/plain") doc = InspectionDocument.objects.create( inspection=insp, file=dummy_file ) self.client.login(username='inspector_user', password='password123') url = reverse('inspections:delete_inspection_document_api', kwargs={'document_id': doc.id}) response = self.client.post(url) self.assertEqual(response.status_code, 200) self.assertTrue(response.json()['success']) self.assertFalse(InspectionDocument.objects.filter(id=doc.id).exists())