1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 11:16:47 +00:00
webvirtcloud/instances/tests/test_create_wizard_url.py
catborise 6abea495f1 fix(logs): failures name their job, VM and user; the new VM form shows a list it could not load
- A failed cancel hook logged "worker task cancel failed": it now names
  the job, its kind, resource and user, with the traceback for a fault
  of the panel.
- novncd logged "Console connection failed" without the VM or the user.
- django-axes wrote four INFO lines a login ("Reset 0 access
  attempts"...); logins are on the Logs page, so it logs warnings only.
- The new VM form's machine types and disk buses answered 200 with an
  empty object when libvirt failed, and nothing was logged: the list
  stayed empty, the form could not go on and said nothing. They answer
  400 with the error, as the pool's volume list does, the warning is
  logged with the compute, and the form's lists show an error they get
  with the page's messages.
2026-11-06 23:48:44 +03:00

75 lines
4.3 KiB
Python

"""The create wizard's first page sends the browser to the next page for the
chosen architecture and machine type; the URL is built from placeholders."""
import json
import re
import subprocess
import shutil
import unittest
from computes.models import Compute
from django.conf import settings
from django.contrib.auth import get_user_model
from django.template.loader import render_to_string
from django.test import RequestFactory, TestCase
from lxml import html
SCRIPT = (settings.BASE_DIR / "static/js/create_machine.js").read_text()
def function(name):
"""The page script's function of this name."""
return re.search(rf"\nfunction {name}\(.*?\n}}\n", SCRIPT, re.S).group(0)
class CreateWizardUrlTests(TestCase):
def render_page(self):
compute = Compute.objects.create(name="cw", hostname="10.0.0.6", login="u", password="p", type=1)
request = RequestFactory().get("/")
request.user = get_user_model().objects.create_superuser("cw-admin", "cw@example.com", "pw")
return compute, html.fromstring(render_to_string("create_instance_w1.html", {"compute": compute}, request=request))
@unittest.skipUnless(shutil.which("node"), "node is not installed")
def test_arch_and_machine_names_land_in_their_segments(self):
_, page = self.render_page()
create_url = page.get_element_by_id("next").get("data-create-url")
script = ("const document = {querySelector: () => ({dataset: {createUrl: %s}})};" % json.dumps(create_url)
+ function("create_url"))
cases = {
("x86_64", "q35"): "x86_64/q35/",
("ppc64le", "pseries"): "ppc64le/pseries/", # contains "pc"
("aarch64", "virt"): "aarch64/virt/",
}
for (arch, machine), tail in cases.items():
js = "%s console.log(create_url(%r, %r));" % (script, arch, machine)
out = subprocess.run(["node", "-e", js], capture_output=True, text=True, timeout=30)
with self.subTest(arch=arch):
self.assertTrue(out.stdout.strip().endswith(f"/{tail}"), out.stdout + out.stderr)
@unittest.skipUnless(shutil.which("node"), "node is not installed")
def test_next_waits_for_a_machine_type(self):
load_machines = function("load_machines")
for machines, disabled in (([], True), (["q35"], False)):
js = ("const next = {disabled: false}, select = {dataset: {machinesUrl: '/__arch__/'}, replaceChildren() {}};"
"const document = {querySelector: sel => sel === '#next' ? next : select};"
"const Option = function () {};"
f"const $ = {{getJSON: (url, f) => {{ f({{machines: {machines!r}}}); return {{fail() {{}}}}; }}}};"
"const showLoadError = null;"
f"{load_machines} load_machines('x86_64'); console.log(next.disabled);")
out = subprocess.run(["node", "-e", js], capture_output=True, text=True, timeout=30)
with self.subTest(machines=machines):
self.assertEqual(out.stdout.strip(), str(disabled).lower(), out.stderr)
@unittest.skipUnless(shutil.which("node"), "node is not installed")
def test_a_newer_architecture_aborts_the_older_load(self):
load_machines = function("load_machines")
js = ("const next = {}, select = {dataset: {machinesUrl: '/__arch__/'}, children: ['pc'], replaceChildren(...items) { this.children = items; }};"
"const document = {querySelector: sel => sel === '#next' ? next : select};"
"const Option = function (text) { this.text = text; }; const requests = [];"
"const $ = {getJSON: (url, fill) => { const r = {fill, abort() { r.aborted = true; }, fail() { return r; }}; requests.push(r); return r; }};"
"const showLoadError = null;"
f"{load_machines} load_machines('x86_64'); const loading = select.children; load_machines('aarch64');"
"requests[1].fill({machines: ['virt']});"
"console.log(JSON.stringify({loading, aborted: requests.map(r => !!r.aborted), filled: select.children.map(o => o.text)}));")
out = subprocess.run(["node", "-e", js], capture_output=True, text=True, timeout=30)
self.assertEqual(json.loads(out.stdout), {"loading": [], "aborted": [True, False], "filled": ["virt"]}, out.stderr)