diff --git a/panel/nix/tests.nix b/panel/nix/tests.nix
index eaa9068..a825938 100644
--- a/panel/nix/tests.nix
+++ b/panel/nix/tests.nix
@@ -26,7 +26,7 @@ lib.mapAttrs (name: test: pkgs.testers.runNixOSTest (test // { inherit name; }))
# run all application-level tests managed by Django
# https://docs.djangoproject.com/en/5.0/topics/testing/overview/
testScript = ''
- server.succeed("manage test")
+ server.succeed("manage test ${name}")
'';
};
admin = {
diff --git a/panel/src/panel/templates/base.html b/panel/src/panel/templates/base.html
index 7f8b2e1..aa29745 100644
--- a/panel/src/panel/templates/base.html
+++ b/panel/src/panel/templates/base.html
@@ -30,7 +30,7 @@
{% load custom_tags %}
{% if user.is_authenticated %}
- Welcome, {{ user.username }}! Logout
+ Welcome, {{ user.username }}! Logout
{% else %}
Login
{% endif %}
diff --git a/panel/src/panel/tests/__init__.py b/panel/src/panel/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/panel/src/panel/tests/test_user_stories.py b/panel/src/panel/tests/test_user_stories.py
new file mode 100644
index 0000000..8f45ae4
--- /dev/null
+++ b/panel/src/panel/tests/test_user_stories.py
@@ -0,0 +1,104 @@
+from django.test import TestCase
+from django.urls import reverse
+from django.contrib.auth.models import User
+from django.template import Template, Context
+from urllib.parse import unquote
+
+class Login(TestCase):
+ def setUp(self):
+ self.username = 'testuser'
+ self.password = 'securepassword123'
+ self.user = User.objects.create_user(
+ username=self.username,
+ email='test@example.com',
+ password=self.password
+ )
+
+ self.login = reverse('login')
+ self.logout = reverse('logout')
+ self.required_login = reverse('account_detail')
+ self.optional_login = reverse('service_list')
+
+ def test_optional_login_redirects_back_to_original_page(self):
+ # go to a view where authentication is optional
+ response = self.client.get(self.optional_login)
+ self.assertEqual(response.status_code, 200)
+ self.assertFalse(response.context['user'].is_authenticated)
+
+ # check that the expected login URL is in the response
+ context = response.context[0]
+ template = Template("{% load custom_tags %}{% auth_url 'login' %}")
+ login_url = template.render(context)
+ self.assertIn(login_url, response.content.decode('utf-8'))
+
+ # log in
+ response = self.client.get(login_url)
+ self.assertEqual(response.status_code, 200)
+
+ login_data = {
+ 'username': self.username,
+ 'password': self.password,
+ }
+ response = self.client.post(login_url, login_data, follow=True)
+
+ # check that we're back at the desired view and authenticated
+ self.assertEqual(response.status_code, 200)
+ self.assertTrue(response.context['user'].is_authenticated)
+ location, status = response.redirect_chain[-1]
+ self.assertEqual(location, self.optional_login)
+
+ # check that the expected logout URL is present
+ context = response.context[0]
+ template = Template("{% load custom_tags %}{% auth_url 'logout' %}")
+ logout_url = template.render(context)
+ self.assertIn(logout_url, response.content.decode('utf-8'))
+
+ # log out again
+ response = self.client.get(logout_url, follow=True)
+
+ # check that we're back at the view and logged out
+ self.assertEqual(response.status_code, 200)
+ location, status = response.redirect_chain[-1]
+ self.assertEqual(location, self.optional_login)
+ self.assertFalse(response.context['user'].is_authenticated)
+
+ def test_required_login_redirects_back_login(self):
+ # go to a view that requires authentication
+ response = self.client.get(self.required_login)
+
+ # check that we're redirected to the login view
+ self.assertEqual(response.status_code, 302)
+ redirect = response.url
+ self.assertTrue(redirect.startswith(self.login))
+
+ # log in
+ response = self.client.get(redirect)
+ self.assertEqual(response.status_code, 200)
+ login_data = {
+ 'username': self.username,
+ 'password': self.password,
+ }
+ response = self.client.post(redirect, login_data, follow=True)
+
+ # check that we reached the desired view, authenticated
+ self.assertEqual(response.status_code, 200)
+ location, status = response.redirect_chain[-1]
+ self.assertEqual(location, self.required_login)
+ self.assertTrue(response.context['user'].is_authenticated)
+
+ # check that the expected logout URL is present
+ context = response.context[0]
+ template = Template("{% load custom_tags %}{% auth_url 'logout' %}")
+ logout_url = template.render(context)
+ self.assertIn(logout_url, response.content.decode('utf-8'))
+
+ # log out
+ response = self.client.get(logout_url, follow=True)
+
+ # check that we're at the expected location, logged out
+ self.assertEqual(response.status_code, 200)
+ template = Template("{% load custom_tags %}{% auth_url 'login' %}")
+ login_url = template.render(context)
+ location, status = response.redirect_chain[-1]
+ self.assertEqual(location, unquote(login_url))
+ self.assertFalse(response.context['user'].is_authenticated)