1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 11:46:49 +00:00

feat: error pages that say what happened and what to do

The 403, 404 and 500 pages said "Oops!" and one generic sentence; a bad
request and a failed CSRF check fell back to Django's plain pages.

Every error page now shares templates/error.html: a card with the status
in a coloured badge with an icon, a heading in plain words, what happened
and what to do, and Back and Home buttons. It uses only theme-defined
Bootstrap classes, so it reads in the light and dark themes alike.

- 400: the request was not accepted; a new address belongs in ALLOWED_HOSTS.
- 403: the account lacks the permission; ask an administrator.
- 403 CSRF: the form's security check failed; reload and submit again,
  and where to look when it happens on every form.
- 404: the path, and that the object may be gone or hidden from the account.
- 500: a libvirt failure on a page says the host's data could not be
  loaded, under libvirt's message; any other failure says it was logged
  and shows the time to give an administrator.
This commit is contained in:
catborise 2026-10-29 17:20:05 +03:00
parent c3c84d780c
commit 10eb0d1c2e
8 changed files with 140 additions and 33 deletions

View file

@ -66,6 +66,8 @@ class ConnectionErrorPageTests(TestCase):
response, messages = self.handle(self.admin, libvirt_error(libvirt.VIR_FROM_RPC, HOST_DETAIL),
path="/instances/1/", method="get")
self.assertEqual(response.status_code, 500)
self.assertIn(b"Could not load data from the host", response.content)
self.assertIn(HOST_DETAIL.encode(), response.content)
self.assertTrue(messages)
def test_administrators_see_connection_details(self):

View file

@ -0,0 +1,41 @@
"""The error pages say what happened and what to do, in the panel's layout."""
from django.contrib.auth import get_user_model
from django.http import Http404
from django.test import Client, RequestFactory, TestCase
from django.views.defaults import page_not_found, server_error
class ErrorPageTests(TestCase):
def setUp(self):
self.user = get_user_model().objects.create_user("ep-user", password="pw")
self.client.force_login(self.user)
def test_a_bad_request(self):
response = self.client.get("/", HTTP_HOST="unknown.example")
self.assertContains(response, "Bad request", status_code=400)
self.assertContains(response, "ALLOWED_HOSTS", status_code=400)
def test_a_page_the_user_may_not_open(self):
response = self.client.get("/computes/")
self.assertContains(response, "Access denied", status_code=403)
self.assertContains(response, "Ask an administrator", status_code=403)
def test_a_form_that_fails_its_security_check(self):
response = Client(enforce_csrf_checks=True).post("/accounts/login/", {"username": "ep-user", "password": "pw"})
self.assertContains(response, "Form could not be submitted", status_code=403)
self.assertContains(response, "reload the page and submit the form again", status_code=403)
def test_a_page_that_is_not_there_names_its_path(self):
response = self.client.get("/no/such/page/")
self.assertContains(response, "Page not found", status_code=404)
self.assertContains(response, "<code>/no/such/page/</code>", status_code=404)
def test_the_reason_of_a_not_found_is_not_shown(self):
response = page_not_found(RequestFactory().get("/datasource/"), Http404("libvirt detail of the host"))
self.assertNotIn(b"libvirt detail", response.content)
def test_a_server_error_renders_without_a_request_context(self):
response = server_error(RequestFactory().get("/"))
self.assertContains(response, "Server error", status_code=500)
self.assertContains(response, "tell an administrator the time", status_code=500)