FossilRepo

fossilrepo / core / tests.py
Source Blame History 147 lines
4ce269c… ragelink 1 import pytest
4ce269c… ragelink 2 from django.contrib.auth.models import User
4ce269c… ragelink 3 from django.test import TestCase
4ce269c… ragelink 4 from django.urls import reverse
4ce269c… ragelink 5
4ce269c… ragelink 6 from .permissions import P
4ce269c… ragelink 7
4ce269c… ragelink 8
4ce269c… ragelink 9 class TrackingModelTest(TestCase):
4ce269c… ragelink 10 """Test the Tracking abstract model via a concrete model that uses it."""
4ce269c… ragelink 11
4ce269c… ragelink 12 def setUp(self):
c588255… ragelink 13 from organization.models import Organization
c588255… ragelink 14 from projects.models import Project
4ce269c… ragelink 15
4ce269c… ragelink 16 self.user = User.objects.create_superuser(username="test", password="x")
c588255… ragelink 17 self.org = Organization.objects.create(name="Test Org", created_by=self.user)
c588255… ragelink 18 self.project = Project.objects.create(name="Test Project", organization=self.org, created_by=self.user)
4ce269c… ragelink 19
4ce269c… ragelink 20 def test_version_increments_on_save(self):
c588255… ragelink 21 initial_version = self.project.version
c588255… ragelink 22 self.project.name = "Updated Project"
c588255… ragelink 23 self.project.save()
c588255… ragelink 24 self.project.refresh_from_db()
c588255… ragelink 25 self.assertEqual(self.project.version, initial_version + 1)
4ce269c… ragelink 26
4ce269c… ragelink 27 def test_soft_delete_sets_deleted_at(self):
c588255… ragelink 28 self.project.soft_delete(user=self.user)
c588255… ragelink 29 self.project.refresh_from_db()
c588255… ragelink 30 self.assertIsNotNone(self.project.deleted_at)
c588255… ragelink 31 self.assertEqual(self.project.deleted_by, self.user)
c588255… ragelink 32 self.assertTrue(self.project.is_deleted)
4ce269c… ragelink 33
4ce269c… ragelink 34 def test_created_at_auto_set(self):
c588255… ragelink 35 self.assertIsNotNone(self.project.created_at)
4ce269c… ragelink 36
4ce269c… ragelink 37 def test_updated_at_auto_set(self):
c588255… ragelink 38 self.assertIsNotNone(self.project.updated_at)
4ce269c… ragelink 39
4ce269c… ragelink 40
4ce269c… ragelink 41 class BaseCoreModelTest(TestCase):
4ce269c… ragelink 42 """Test BaseCoreModel slug generation and UUID."""
4ce269c… ragelink 43
4ce269c… ragelink 44 def setUp(self):
c588255… ragelink 45 from organization.models import Organization
c588255… ragelink 46 from projects.models import Project
4ce269c… ragelink 47
4ce269c… ragelink 48 self.user = User.objects.create_superuser(username="test", password="x")
c588255… ragelink 49 self.org = Organization.objects.create(name="Test Org", created_by=self.user)
c588255… ragelink 50 self.project = Project.objects.create(name="My Project", organization=self.org, created_by=self.user)
4ce269c… ragelink 51
4ce269c… ragelink 52 def test_slug_auto_generated(self):
c588255… ragelink 53 self.assertEqual(self.project.slug, "my-project")
4ce269c… ragelink 54
4ce269c… ragelink 55 def test_guid_is_uuid(self):
4ce269c… ragelink 56 import uuid
4ce269c… ragelink 57
c588255… ragelink 58 self.assertIsInstance(self.project.guid, uuid.UUID)
4ce269c… ragelink 59
4ce269c… ragelink 60 def test_slug_uniqueness(self):
c588255… ragelink 61 from projects.models import Project
4ce269c… ragelink 62
c588255… ragelink 63 p2 = Project.objects.create(name="My Project", organization=self.org, created_by=self.user)
c588255… ragelink 64 self.assertNotEqual(self.project.slug, p2.slug)
c588255… ragelink 65 self.assertTrue(p2.slug.startswith("my-project"))
4ce269c… ragelink 66
4ce269c… ragelink 67 def test_str_returns_name(self):
c588255… ragelink 68 self.assertEqual(str(self.project), "My Project")
4ce269c… ragelink 69
4ce269c… ragelink 70
4ce269c… ragelink 71 class PermissionsTest(TestCase):
4ce269c… ragelink 72 """Test the P permission enum."""
4ce269c… ragelink 73
4ce269c… ragelink 74 def setUp(self):
4ce269c… ragelink 75 self.superuser = User.objects.create_superuser(username="super", password="x")
4ce269c… ragelink 76 self.regular = User.objects.create_user(username="regular", password="x")
4ce269c… ragelink 77
4ce269c… ragelink 78 def test_superuser_passes_all_checks(self):
c588255… ragelink 79 self.assertTrue(P.PROJECT_VIEW.check(self.superuser))
c588255… ragelink 80 self.assertTrue(P.PROJECT_ADD.check(self.superuser))
4ce269c… ragelink 81
4ce269c… ragelink 82 def test_regular_user_without_perm_denied(self):
4ce269c… ragelink 83 from django.core.exceptions import PermissionDenied
4ce269c… ragelink 84
4ce269c… ragelink 85 with self.assertRaises(PermissionDenied):
c588255… ragelink 86 P.PROJECT_ADD.check(self.regular)
4ce269c… ragelink 87
4ce269c… ragelink 88 def test_regular_user_without_perm_returns_false(self):
c588255… ragelink 89 self.assertFalse(P.PROJECT_ADD.check(self.regular, raise_error=False))
4ce269c… ragelink 90
4ce269c… ragelink 91 def test_unauthenticated_user_denied(self):
4ce269c… ragelink 92 from django.contrib.auth.models import AnonymousUser
4ce269c… ragelink 93 from django.core.exceptions import PermissionDenied
4ce269c… ragelink 94
4ce269c… ragelink 95 with self.assertRaises(PermissionDenied):
c588255… ragelink 96 P.PROJECT_VIEW.check(AnonymousUser())
4ce269c… ragelink 97
4ce269c… ragelink 98
4ce269c… ragelink 99 @pytest.mark.django_db
4ce269c… ragelink 100 class TestDashboard:
4ce269c… ragelink 101 def test_dashboard_requires_login(self, client):
4ce269c… ragelink 102 response = client.get(reverse("dashboard"))
4ce269c… ragelink 103 assert response.status_code == 302
4ce269c… ragelink 104 assert "/auth/login/" in response.url
4ce269c… ragelink 105
4ce269c… ragelink 106 def test_dashboard_renders_for_authenticated_user(self, admin_client):
4ce269c… ragelink 107 response = admin_client.get(reverse("dashboard"))
4ce269c… ragelink 108 assert response.status_code == 200
4ce269c… ragelink 109 assert b"Dashboard" in response.content
4ce269c… ragelink 110
4ce269c… ragelink 111
4ce269c… ragelink 112 @pytest.mark.django_db
4ce269c… ragelink 113 class TestHealthCheck:
4ce269c… ragelink 114 def test_health_returns_ok(self, client):
4ce269c… ragelink 115 response = client.get(reverse("health"))
4ce269c… ragelink 116 assert response.status_code == 200
4ce269c… ragelink 117 data = response.json()
4ce269c… ragelink 118 assert data["status"] == "ok"
4ce269c… ragelink 119 assert data["service"] == "fossilrepo-django-htmx"
4ce269c… ragelink 120 assert "version" in data
4ce269c… ragelink 121 assert "uptime" in data
4ce269c… ragelink 122 assert "timestamp" in data
4ce269c… ragelink 123 assert data["checks"]["database"] == "ok"
4ce269c… ragelink 124 assert data["links"]["status"] == "/status/"
4ce269c… ragelink 125
4ce269c… ragelink 126
4ce269c… ragelink 127 @pytest.mark.django_db
4ce269c… ragelink 128 class TestStatusPage:
4ce269c… ragelink 129 def test_status_page_accessible_unauthenticated(self, client):
4ce269c… ragelink 130 response = client.get(reverse("status"))
4ce269c… ragelink 131 assert response.status_code == 200
4ce269c… ragelink 132 assert b"Fossilrepo" in response.content
4ce269c… ragelink 133 assert b"Server-rendered Django + HTMX." in response.content
4ce269c… ragelink 134
4ce269c… ragelink 135 def test_status_page_contains_links(self, client):
4ce269c… ragelink 136 response = client.get(reverse("status"))
4ce269c… ragelink 137 content = response.content.decode()
4ce269c… ragelink 138 assert "/dashboard/" in content
4ce269c… ragelink 139 assert "/admin/" in content
4ce269c… ragelink 140 assert "/health/" in content
4ce269c… ragelink 141 assert "/auth/login/" in content
4ce269c… ragelink 142
4ce269c… ragelink 143 def test_status_page_contains_meta(self, client):
4ce269c… ragelink 144 response = client.get(reverse("status"))
4ce269c… ragelink 145 content = response.content.decode()
4ce269c… ragelink 146 assert "fossilrepo-django-htmx" in content
4ce269c… ragelink 147 assert "All systems operational" in content

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button