"""Regression coverage for Gitea issues #1–#3.""" from io import BytesIO from django.contrib.auth.models import User from django.core.management import call_command from django.http import QueryDict from django.test import TestCase from django.urls import reverse from openpyxl import load_workbook from .api import DeviceChangesSerializer, DeviceSerializer from .forms import DeviceForm, InspectionForm, QuickAddForm from .import_export import import_workbook, suggested_mapping, xlsx_response from .models import AuditLog, ChoiceValue, Device, Employee, FieldDefinition from .query import device_queryset from .services import update_device_field class IssueRegressionTests(TestCase): @classmethod def setUpTestData(cls): call_command("seed_initial_data", verbosity=0) cls.user = User.objects.create_superuser("issue-admin", password="test-password") cls.status = ChoiceValue.objects.get(kind="status", label="Eingang") cls.laptop = ChoiceValue.objects.get(kind="device_type", label="Laptop") cls.aio = ChoiceValue.objects.get(kind="device_type", label="AiO") cls.monitor = ChoiceValue.objects.get(kind="device_type", label="Bildschirm") def setUp(self): self.client.force_login(self.user) def device(self, **values): return Device.objects.create(status=self.status, device_type=self.laptop, **values) def test_custom_values_render_and_survive_an_unrelated_edit(self): for key, kind in (("enabled", "boolean"), ("disabled", "boolean"), ("count", "integer"), ("amount", "decimal"), ("empty", "text")): FieldDefinition.objects.create(name=key, key=key, field_type=kind, show_in_list=True) values = {"enabled": True, "disabled": False, "count": 0, "amount": "1.25", "empty": None} device = self.device(custom_values=values) url = reverse("inventory:device-detail", args=[device.pk]) response = self.client.get(url) self.assertContains(response, 'value="true" selected') self.assertContains(response, 'value="false" selected') self.assertContains(response, 'name="custom_count" value="0"') self.assertContains(response, 'name="custom_amount" value="1.25"') self.assertContains(response, 'name="custom_empty" value=""') listing = self.client.get(reverse("inventory:device-list")) self.assertContains(listing, '0', html=True) self.assertContains(listing, 'NEIN', html=True) payload = {"status": self.status.pk, "date": "2026-10-13", "device_type": self.laptop.pk, "expected_revision": device.revision, "custom_enabled": "true", "custom_disabled": "false", "custom_count": "0", "custom_amount": "1.25", "custom_empty": "", "model": "Edited"} response = self.client.post(url, payload) self.assertEqual(response.status_code, 302) device.refresh_from_db() self.assertEqual(device.custom_values, values) payload.update(expected_revision=device.revision, custom_count="invalid", custom_enabled="false") response = self.client.post(url, payload) self.assertEqual(response.status_code, 200) self.assertEqual(response.context["custom_values"]["count"], "invalid") self.assertEqual(response.context["custom_values"]["enabled"], "false") device.refresh_from_db() self.assertEqual(device.custom_values, values) def test_hardware_persistence_api_and_type_changes(self): for device_type in (self.laptop, self.aio): device = Device.objects.create(status=self.status, device_type=device_type, camera="YES", sound="NO") device.refresh_from_db() self.assertEqual((device.camera, device.sound), ("YES", "NO")) self.assertEqual(DeviceSerializer(device).data["camera"], "YES") device = update_device_field(device, "sound", "YES", self.user, expected_revision=device.revision) self.assertEqual(device.sound, "YES") self.assertTrue(AuditLog.objects.filter(device=device, field_key="sound").exists()) device.device_type = self.monitor device.save() device.refresh_from_db() self.assertEqual(device.camera, "UNKNOWN") self.assertEqual(device.custom_values["legacy_hardware"]["camera"], "YES") serializer = DeviceChangesSerializer(data={"hdmi": "sometimes"}) self.assertFalse(serializer.is_valid()) def test_monitor_filters_distinguish_no_and_unknown(self): yes = Device.objects.create(status=self.status, device_type=self.monitor, hdmi="YES", vga="NO", dvi="NO", displayport="YES", aspect_ratio="16:9", ports="USB-C") no = Device.objects.create(status=self.status, device_type=self.monitor, hdmi="NO") unknown = Device.objects.create(status=self.status, device_type=self.monitor) laptop = self.device(camera="YES", sound="NO") for query, expected in (("hdmi=YES&displayport=YES&aspect_ratio=16:9", [yes]), ("hdmi=NO", [no]), ("hdmi=UNKNOWN", [unknown]), ("camera=YES&sound=NO", [laptop]), ("hdmi=invalid", [])): self.assertEqual(list(device_queryset(QueryDict(query))), expected) yes.refresh_from_db() self.assertEqual(yes.ports, "USB-C") listing = self.client.get(reverse("inventory:device-list"), {"type": self.monitor.pk, "hdmi": "YES"}) self.assertContains(listing, 'JA', html=True) self.assertNotContains(listing, '') def test_quick_add_and_inspection_include_new_fields(self): form = QuickAddForm({"number": "1", "quantity": "1", "status": self.status.pk, "date": "2026-10-13", "device_type": self.laptop.pk, "employee": Employee.objects.first().pk, "camera": "YES", "sound": "NO"}) self.assertTrue(form.is_valid(), form.errors) self.assertEqual(form.selected_hardware_values()["camera"], "YES") device = self.device(camera="YES") self.assertEqual(DeviceForm(instance=device)["camera"].value(), "YES") inspection = InspectionForm({"defects": "No camera test yet"}, device=device) self.assertTrue(inspection.is_valid(), inspection.errors) self.assertEqual(inspection.cleaned_data["camera"], "UNKNOWN") monitor = Device.objects.create(status=self.status, device_type=self.monitor) self.assertIn("displayport", InspectionForm(device=monitor).fields) self.assertNotIn("camera", InspectionForm(device=monitor).fields) def test_new_hardware_fields_round_trip_through_xlsx(self): laptop = self.device(camera="YES", sound="NO") monitor = Device.objects.create(status=self.status, device_type=self.monitor, hdmi="YES", vga="NO", dvi="UNKNOWN", displayport="YES", aspect_ratio="21:9", ports="USB-C") payload = xlsx_response(Device.objects.all()).content book = load_workbook(BytesIO(payload)) headers = [cell.value for cell in book.active[1]] mapping = suggested_mapping(headers) expected = {device.number: DeviceSerializer(device).data for device in (laptop, monitor)} Device.objects.all().delete() result = import_workbook(BytesIO(payload), mapping, self.user) self.assertEqual(result["created"], 2) for device in Device.objects.all(): for name in ("camera", "sound", "hdmi", "vga", "dvi", "displayport", "aspect_ratio", "ports"): self.assertEqual(getattr(device, name), expected[device.number][name])