Files
Inventory/inventory/test_issue_regressions.py

124 lines
7.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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, '<td data-column="custom_count">0</td>', html=True)
self.assertContains(listing, '<td data-column="custom_disabled">NEIN</td>', 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, '<td data-column="hdmi">JA</td>', html=True)
self.assertNotContains(listing, '<th data-column="camera">')
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])