1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
webvirtcloud/instances/tests/test_audit_rows.py
catborise e04adec87b feat(logs): changes to users, groups, owners, flavors and settings are logged
The Logs page now records the panel's own records too: users (add,
change with the changed fields, delete, block, unblock, an admin's
password set, a user's own password change including the first-login
one, own profile edits), groups with their members and permissions,
VM owners from the accounts pages and from the VM page, OTP QR code
mails, flavors from web and API, app settings and the theme,
removed-VM records and SSH keys. No row carries a password or a key.

- A user is created in one transaction: when a VM named in
  NEW_USER_DEFAULT_INSTANCES is missing, no half-made user stays.
- The OTP mail checks the email address before it creates a device;
  without one it writes a failed row and makes no device.
- The VM page's owner rows read "VM web-1 owner bob: add" and
  "...: remove" instead of "Add owner: bob" and "Delete owner: 17"; a
  second owner refused by the single-owner setting is a failed row.
  Owner rows keep the VM's host and name, so the VM's log tab shows
  them.
- A setting or theme posted with its current value is no change and
  writes no row; the old free-text setting rows are gone.

Host-side objects (networks, storage, interfaces, secrets, filters)
follow in the next layer.
2026-10-23 11:10:24 +03:00

76 lines
3.9 KiB
Python

"""VM page owner changes and flavor changes write rows on the Logs page."""
from accounts.models import UserInstance
from appsettings.models import AppSettings
from computes.models import Compute
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse
from instances.models import Instance
from logs.models import Logs
def rows():
return list(Logs.objects.order_by("id").values_list("message", "success"))
class VMOwnerRowTests(TestCase):
def setUp(self):
self.client.force_login(get_user_model().objects.create_superuser("audit-admin", "audit@example.com", "pw"))
self.bob = get_user_model().objects.create_user("bob")
self.carol = get_user_model().objects.create_user("carol")
compute = Compute.objects.create(name="c1", hostname="127.0.0.1", login="", password="", type=4)
self.vm = Instance.objects.create(compute=compute, name="vm", uuid="77777777-7777-7777-7777-777777777777")
Logs.objects.all().delete() # the login's row
def test_vm_page_owners(self):
add = reverse("instances:add_owner", args=[self.vm.pk])
self.client.post(add, {"user_id": self.bob.pk})
self.client.post(add, {"user_id": self.bob.pk}) # already an owner: nothing changed
AppSettings.objects.filter(key="ALLOW_INSTANCE_MULTIPLE_OWNER").update(value="False")
self.client.post(add, {"user_id": self.carol.pk})
self.assertFalse(UserInstance.objects.filter(user=self.carol).exists())
ui = UserInstance.objects.get(user=self.bob)
self.client.post(reverse("instances:del_owner", args=[self.vm.pk]), {"userinstance": ui.pk})
self.assertEqual(rows(), [
("VM vm owner bob: add", True),
("VM vm owner carol: add: only one owner is allowed", False),
("VM vm owner bob: remove", True),
])
shown = [log["message"] for log in self.client.get(reverse("vm_logs", args=["vm"])).json()]
self.assertCountEqual(shown, [m for m, _ in rows()])
class FlavorRowTests(TestCase):
def setUp(self):
self.admin = get_user_model().objects.create_superuser("audit-admin", "audit@example.com", "pw")
self.client.force_login(self.admin)
Logs.objects.all().delete() # the login's row
def test_flavor_web_and_api(self):
from instances.models import Flavor
from rest_framework.test import APIClient
data = {"label": "tiny-a", "memory": 512, "vcpu": 1, "disk": 10}
self.client.post(reverse("instances:flavor_create"), data)
flavor = Flavor.objects.get(label="tiny-a")
self.client.post(reverse("instances:flavor_update", args=[flavor.pk]), {**data, "vcpu": 2})
self.client.post(reverse("instances:flavor_update", args=[flavor.pk]), {**data, "vcpu": 2}) # no change
self.client.post(reverse("instances:flavor_delete", args=[flavor.pk]))
self.assertFalse(Flavor.objects.filter(label="tiny-a").exists())
api = APIClient()
api.force_authenticate(self.admin)
data = {"label": "huge-a", "memory": 4096, "vcpu": 4, "disk": 40}
self.assertEqual(api.post(reverse("instance-flavor-list"), data, format="json").status_code, 201)
detail = reverse("instance-flavor-detail", args=[Flavor.objects.get(label="huge-a").pk])
self.assertEqual(api.patch(detail, {"vcpu": 8}, format="json").status_code, 200)
self.assertEqual(api.put(detail, {**data, "vcpu": 8, "memory": 8192}, format="json").status_code, 200)
self.assertEqual(Flavor.objects.get(label="huge-a").memory, 8192)
self.assertEqual(api.delete(detail).status_code, 204)
self.assertFalse(Flavor.objects.filter(label="huge-a").exists())
self.assertEqual([m for m, _ in rows()], [
"Flavor tiny-a: add", "Flavor tiny-a: change vcpu", "Flavor tiny-a: delete",
"Flavor huge-a: add", "Flavor huge-a: change vcpu", "Flavor huge-a: change memory", "Flavor huge-a: delete",
])