124 lines
7.5 KiB
Python
124 lines
7.5 KiB
Python
"""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])
|