Fix device issues and document current LAN deployment
This commit is contained in:
123
inventory/test_issue_regressions.py
Normal file
123
inventory/test_issue_regressions.py
Normal file
@@ -0,0 +1,123 @@
|
||||
"""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])
|
||||
Reference in New Issue
Block a user