loko/loko/controls/tests.py

264 lines
11 KiB
Python

import io
import json
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.urls import reverse
from openpyxl import load_workbook
from common.models import Thematic, Role, UserConfig, UserThematics
from assets.models import AssetCategory
from projects.models import Project, ProjectUserAccess
from controls.models import Control, Remark
User = get_user_model()
class ControlsIndexTests(TestCase):
def setUp(self):
# Thematics
self.thematic_a = Thematic.objects.create(
code="THEME_A",
name_fr="Voirie",
name_nl="Wegenis",
icon="bi-cone-striped"
)
self.thematic_b = Thematic.objects.create(
code="THEME_B",
name_fr="Signalisation",
name_nl="Signalisatie",
icon="bi-signpost"
)
# Categories
self.cat_a = AssetCategory.objects.create(
thematic=self.thematic_a,
code="CAT_A",
name_fr="Trottoir",
name_nl="Voetpad"
)
# Users
self.admin_user = User.objects.create_user("admin_user", password="password123")
self.admin_config = UserConfig.objects.create(user=self.admin_user, is_intern=True)
self.admin_role, _ = Role.objects.get_or_create(name="admin")
self.admin_config.roles.add(self.admin_role)
self.internal_user = User.objects.create_user("internal_user", password="password123")
self.internal_config = UserConfig.objects.create(user=self.internal_user, is_intern=True)
UserThematics.objects.create(
user_config=self.internal_config,
thematic=self.thematic_a,
can_view_interventions=True
)
self.other_user = User.objects.create_user("other_user", password="password123")
self.other_config = UserConfig.objects.create(user=self.other_user, is_intern=False)
# Project
self.project = Project.objects.create(
name="Projet Test",
code="PRJ-001",
created_by=self.admin_user
)
ProjectUserAccess.objects.create(
project=self.project,
user=self.internal_user,
can_view=True
)
# Controls
self.ctrl1 = Control.objects.create(
control_code="CTRL-0001",
description="Contrôle bordure rue Royale",
location="Rue Royale 10",
status="in_progress",
control_type="control",
control_author=self.internal_user,
project=self.project,
lon=4.36,
lat=50.85,
visibility="intern"
)
self.ctrl1.thematics.add(self.thematic_a)
self.ctrl1.categories.add(self.cat_a)
Remark.objects.create(control=self.ctrl1, description="Remarque 1")
self.ctrl2 = Control.objects.create(
control_code="CTRL-0002",
description="Constat marquage au sol",
location="Avenue Louise 20",
status="to_be_processed",
control_type="statement",
control_author=self.admin_user,
lon=4.37,
lat=50.84,
visibility="all"
)
self.ctrl2.thematics.add(self.thematic_b)
self.ctrl_private = Control.objects.create(
control_code="CTRL-0003",
description="Contrôle privé",
status="information",
control_type="meeting_report",
control_author=self.admin_user,
visibility="private"
)
def test_controls_index_page_loads(self):
self.client.login(username="admin_user", password="password123")
response = self.client.get(reverse("controls:controls_index"))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "controls/controls_index.html")
self.assertIn("thematic_choices", response.context)
self.assertIn("status_choices", response.context)
self.assertIn("control_type_choices", response.context)
self.assertIn("category_choices", response.context)
self.assertIn("project_choices", response.context)
def test_controls_index_saved_preferences_redirect(self):
self.admin_config.control_list_filters = {"thematic": ["THEME_A"]}
self.admin_config.save()
self.client.login(username="admin_user", password="password123")
response = self.client.get(reverse("controls:controls_index"))
self.assertEqual(response.status_code, 302)
self.assertIn("thematic=THEME_A", response.url)
def test_controls_geojson_endpoint(self):
self.client.login(username="admin_user", password="password123")
response = self.client.get(reverse("controls:controls_geojson"))
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertEqual(data["type"], "FeatureCollection")
self.assertTrue(len(data["features"]) >= 2)
# Check properties on first feature
feature = next(f for f in data["features"] if f["id"] == self.ctrl1.id)
self.assertEqual(feature["properties"]["control_code"], "CTRL-0001")
self.assertEqual(feature["properties"]["status"], "in_progress")
self.assertEqual(feature["properties"]["control_type"], "control")
self.assertEqual(feature["properties"]["project_name"], "Projet Test")
self.assertEqual(feature["properties"]["remarks_count"], 1)
self.assertEqual(feature["geometry"]["coordinates"], [4.36, 50.85])
def test_controls_geojson_filter_by_thematic(self):
self.client.login(username="admin_user", password="password123")
response = self.client.get(reverse("controls:controls_geojson"), {"thematic": ["THEME_A"]})
self.assertEqual(response.status_code, 200)
data = response.json()
codes = [f["properties"]["control_code"] for f in data["features"]]
self.assertIn("CTRL-0001", codes)
self.assertNotIn("CTRL-0002", codes)
def test_controls_geojson_filter_by_status_and_type(self):
self.client.login(username="admin_user", password="password123")
response = self.client.get(reverse("controls:controls_geojson"), {
"status": ["to_be_processed"],
"type": ["statement"]
})
self.assertEqual(response.status_code, 200)
data = response.json()
codes = [f["properties"]["control_code"] for f in data["features"]]
self.assertEqual(codes, ["CTRL-0002"])
def test_controls_geojson_filter_by_search_query(self):
self.client.login(username="admin_user", password="password123")
response = self.client.get(reverse("controls:controls_geojson"), {"q": "Royale"})
self.assertEqual(response.status_code, 200)
data = response.json()
codes = [f["properties"]["control_code"] for f in data["features"]]
self.assertEqual(codes, ["CTRL-0001"])
def test_controls_geojson_filter_mine(self):
self.client.login(username="internal_user", password="password123")
response = self.client.get(reverse("controls:controls_geojson"), {"mine": "1"})
self.assertEqual(response.status_code, 200)
data = response.json()
codes = [f["properties"]["control_code"] for f in data["features"]]
self.assertEqual(codes, ["CTRL-0001"])
def test_controls_geojson_permissions(self):
# Internal user without access to thematic B and not author of private control
self.client.login(username="internal_user", password="password123")
response = self.client.get(reverse("controls:controls_geojson"))
self.assertEqual(response.status_code, 200)
data = response.json()
codes = [f["properties"]["control_code"] for f in data["features"]]
self.assertIn("CTRL-0001", codes)
self.assertNotIn("CTRL-0003", codes) # private to admin_user
def test_controls_export_excel(self):
self.client.login(username="admin_user", password="password123")
response = self.client.get(reverse("controls:controls_export_excel"))
self.assertEqual(response.status_code, 200)
self.assertEqual(
response["Content-Type"],
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
)
wb = load_workbook(filename=io.BytesIO(response.content))
ws = wb.active
self.assertIn(ws.title, ["Contrôles", "Controls"])
self.assertGreaterEqual(ws.max_row, 2) # Header + rows
def test_save_control_list_preferences(self):
self.client.login(username="internal_user", password="password123")
payload = {
"filters": {"thematic": ["THEME_A"], "mine": "1"},
"sort": {"sortName": "created_at", "sortOrder": "asc"}
}
response = self.client.post(
reverse("common:save_control_list_preferences"),
data=json.dumps(payload),
content_type="application/json"
)
self.assertEqual(response.status_code, 200)
self.internal_config.refresh_from_db()
self.assertEqual(self.internal_config.control_list_filters, payload["filters"])
self.assertEqual(self.internal_config.control_list_sort, payload["sort"])
def test_projects_autocomplete(self):
self.client.login(username="admin_user", password="password123")
response = self.client.get(reverse("controls:projects_autocomplete"), {"q": "Test"})
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertTrue(len(data) >= 1)
self.assertEqual(data[0]["id"], self.project.id)
self.assertEqual(data[0]["name"], "Projet Test")
def test_controls_index_selected_projects_objs(self):
self.client.login(username="admin_user", password="password123")
response = self.client.get(reverse("controls:controls_index"), {"project": [str(self.project.id)]})
self.assertEqual(response.status_code, 200)
self.assertIn("selected_projects_objs", response.context)
selected_objs = response.context["selected_projects_objs"]
self.assertEqual(len(selected_objs), 1)
self.assertEqual(selected_objs[0].id, self.project.id)
def test_controls_index_mobile_get(self):
self.client.login(username="admin_user", password="password123")
response = self.client.get(reverse("controls:controls_index"), {"v": "mobile"})
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "controls/controls_index_mobile.html")
self.assertIn("status_filters", response.context)
self.assertIn("type_filters", response.context)
self.assertIn("markers_json", response.context)
self.assertContains(response, "CTRL-0001")
def test_controls_index_mobile_ajax(self):
self.client.login(username="admin_user", password="password123")
response = self.client.get(
reverse("controls:controls_index"),
{"v": "mobile", "status": "in_progress"},
HTTP_X_REQUESTED_WITH="XMLHttpRequest"
)
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertIn("html", data)
self.assertIn("markers", data)
self.assertIn("count", data)
self.assertIn("CTRL-0001", data["html"])
self.assertEqual(len(data["markers"]), 1)
self.assertEqual(data["markers"][0]["code"], "CTRL-0001")