334 lines
13 KiB
Python
334 lines
13 KiB
Python
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())
|
|
|