Coverage for sm / test_multitenancy_expanded.py: 0%
163 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-17 13:46 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-17 13:46 +0000
1from django.test import TestCase, Client
2from django.contrib.auth.models import User, Group, Permission
3from vendor.models import Model as Vendor
4from django.urls import reverse
5from .utils_starterpack import import_starter_pack
8class MultiTenancyExpandedTest(TestCase):
9 def setUp(self) -> None:
10 self.password = "password123"
12 # Create two groups
13 self.group_a = Group.objects.create(name="Group A")
14 self.group_b = Group.objects.create(name="Group B")
16 # Create profiles
17 self.profile_a = self.group_a.profile
18 self.profile_b = self.group_b.profile
20 # Create users
21 self.user_a = User.objects.create_user(
22 username="user_a", password=self.password
23 )
24 self.user_b = User.objects.create_user(
25 username="user_b", password=self.password
26 )
28 self.user_a.groups.add(self.group_a)
29 self.user_b.groups.add(self.group_b)
31 # Set owners
32 self.profile_a.owner = self.user_a
33 self.profile_a.save()
34 self.profile_b.owner = self.user_b
35 self.profile_b.save()
37 # Create clients
38 self.client_a = Client()
39 self.client_a.login(username="user_a", password=self.password)
40 self.client_b = Client()
41 self.client_b.login(username="user_b", password=self.password)
43 def test_vendor_partitioning(self) -> None:
44 """Test that vendors are filtered by group."""
45 Vendor.objects.create(name="Vendor A", group=self.group_a)
46 Vendor.objects.create(name="Vendor B", group=self.group_b)
48 # User A should only see Vendor A
49 # Give permission first
50 view_perm = Permission.objects.get(
51 codename="view_model", content_type__app_label="vendor"
52 )
53 self.group_a.permissions.add(view_perm)
55 response = self.client_a.get(reverse("vendor:index"))
56 self.assertContains(response, "Vendor A")
57 self.assertNotContains(response, "Vendor B")
59 def test_starter_pack_import(self) -> None:
60 """Test starter pack utility logic."""
61 # Ensure group is empty
62 Vendor.objects.filter(group=self.group_a).delete()
64 results = import_starter_pack(self.group_a)
65 self.assertGreater(results["vendors"], 0)
66 self.assertGreater(results["os"], 0)
68 self.assertTrue(
69 Vendor.objects.filter(group=self.group_a, name="Red Hat").exists()
70 )
72 def test_item_quota_enforcement(self) -> None:
73 """Test that group item quota is enforced."""
74 self.profile_a.max_items = 1
75 self.profile_a.save()
77 # Create one vendor
78 Vendor.objects.create(name="Vendor 1", group=self.group_a)
80 # Try to create another via view (should fail)
81 add_perm = Permission.objects.get(
82 codename="add_model", content_type__app_label="vendor"
83 )
84 self.group_a.permissions.add(add_perm)
86 response = self.client_a.post(
87 reverse("vendor:create"),
88 {"name": "Vendor 2", "is_hardware": True, "is_software": True},
89 follow=True,
90 )
91 self.assertContains(response, "Quota exceeded")
92 self.assertEqual(Vendor.objects.filter(group=self.group_a).count(), 1)
94 def test_user_quota_enforcement(self) -> None:
95 """Test that group user quota is enforced."""
96 self.profile_a.max_users = 1 # Only user_a
97 self.profile_a.save()
99 user_c = User.objects.create_user(username="user_c", password=self.password)
101 response = self.client_a.post(
102 reverse("group_member_add", args=[self.group_a.id]),
103 {"username": "user_c"},
104 follow=True,
105 )
106 self.assertContains(response, "User quota exceeded")
107 self.assertNotIn(user_c, self.group_a.user_set.all())
109 def test_group_owner_permissions_management(self) -> None:
110 """Test that group owner can change group permissions."""
111 # Check initial perms (view only by default signal)
112 from django.contrib.contenttypes.models import ContentType
114 server_ct = ContentType.objects.get(app_label="server", model="model")
115 change_perm = Permission.objects.get(
116 content_type=server_ct, codename="change_model"
117 )
119 self.assertNotIn(change_perm, self.group_a.permissions.all())
121 # Toggle edit_server
122 # The form field name is edit_<app_label>
123 response = self.client_a.post(
124 reverse("group_permission_edit", args=[self.group_a.id]),
125 {"edit_server": True},
126 follow=True,
127 )
128 self.assertEqual(response.status_code, 200)
130 self.assertIn(change_perm, self.group_a.permissions.all())
132 # Toggle off
133 self.client_a.post(
134 reverse("group_permission_edit", args=[self.group_a.id]),
135 {"edit_server": False},
136 follow=True,
137 )
138 self.assertNotIn(change_perm, self.group_a.permissions.all())
141class MultiTenancyEdgeCasesTest(TestCase):
142 def setUp(self) -> None:
143 self.password = "password123"
145 # Create groups
146 self.group_a = Group.objects.create(name="Group A")
147 self.group_b = Group.objects.create(name="Group B")
149 # Create profiles
150 self.profile_a = self.group_a.profile
151 self.profile_b = self.group_b.profile
153 # Create users
154 self.user_a = User.objects.create_user(
155 username="user_a", password=self.password
156 )
157 self.user_b = User.objects.create_user(
158 username="user_b", password=self.password
159 )
160 self.superuser = User.objects.create_superuser(
161 username="superuser", password=self.password, email="super@example.com"
162 )
164 self.user_a.groups.add(self.group_a)
165 self.user_b.groups.add(self.group_b)
167 # Set owners
168 self.profile_a.owner = self.user_a
169 self.profile_a.save()
170 self.profile_b.owner = self.user_b
171 self.profile_b.save()
173 # Create clients
174 self.client_a = Client()
175 self.client_a.login(username="user_a", password=self.password)
176 self.client_b = Client()
177 self.client_b.login(username="user_b", password=self.password)
178 self.superuser_client = Client()
179 self.superuser_client.login(username="superuser", password=self.password)
181 def test_superuser_bypasses_all_restrictions(self) -> None:
182 """Test that superusers can see and create items in any group."""
183 # Create items in different groups
184 Vendor.objects.create(name="Vendor A", group=self.group_a)
185 Vendor.objects.create(name="Vendor B", group=self.group_b)
187 # Superuser should see all items
188 response = self.superuser_client.get(reverse("vendor:index"))
189 self.assertContains(response, "Vendor A")
190 self.assertContains(response, "Vendor B")
192 # Superuser should be able to create items without group assignment
193 response = self.superuser_client.post(
194 reverse("vendor:create"),
195 {"name": "Vendor Super", "is_hardware": True, "is_software": True},
196 follow=True,
197 )
198 self.assertEqual(response.status_code, 200)
200 # Item should be created without a group (global)
201 vendor = Vendor.objects.get(name="Vendor Super")
202 self.assertIsNone(vendor.group)
204 def test_global_items_visible_to_all_users(self) -> None:
205 """Test that items with no group are visible to all users."""
206 # Create a global vendor (no group)
207 global_vendor = Vendor.objects.create(name="Global Vendor")
208 self.assertIsNone(global_vendor.group)
210 # Both users should see the global vendor
211 response_a = self.client_a.get(reverse("vendor:index"))
212 response_b = self.client_b.get(reverse("vendor:index"))
214 self.assertContains(response_a, "Global Vendor")
215 self.assertContains(response_b, "Global Vendor")
217 def test_user_without_group_cannot_create_items(self) -> None:
218 """Test that users without any groups cannot create items."""
219 # Create a user without any groups
220 user_no_group = User.objects.create_user(
221 username="user_no_group", password=self.password
222 )
223 # Clear automatically created personal group for this test case
224 user_no_group.groups.clear()
226 client_no_group = Client()
227 client_no_group.login(username="user_no_group", password=self.password)
229 # Grant necessary permissions
230 add_perm = Permission.objects.get(
231 codename="add_model", content_type__app_label="vendor"
232 )
233 view_perm = Permission.objects.get(
234 codename="view_model", content_type__app_label="vendor"
235 )
236 user_no_group.user_permissions.add(add_perm, view_perm)
238 # User should be able to create items but without group assignment
239 response = client_no_group.post(
240 reverse("vendor:create"),
241 {"name": "Vendor No Group", "is_hardware": True, "is_software": True},
242 follow=True,
243 )
245 # Should succeed and create item without a group
246 self.assertEqual(response.status_code, 200)
247 vendor = Vendor.objects.filter(name="Vendor No Group").first()
248 self.assertIsNotNone(vendor)
249 self.assertIsNone(vendor.group)
251 def test_concurrent_quota_checks(self) -> None:
252 """Test that concurrent quota checks are handled properly."""
253 self.profile_a.max_items = 1
254 self.profile_a.save()
256 # Grant add permission
257 add_perm = Permission.objects.get(
258 codename="add_model", content_type__app_label="vendor"
259 )
260 self.group_a.permissions.add(add_perm)
262 # Create one vendor to reach the limit
263 Vendor.objects.create(name="Vendor 1", group=self.group_a)
265 # Try to create another vendor (should fail due to quota)
266 response = self.client_a.post(
267 reverse("vendor:create"),
268 {"name": "Vendor 2", "is_hardware": True, "is_software": True},
269 follow=True,
270 )
271 self.assertContains(response, "Quota exceeded")
272 self.assertEqual(Vendor.objects.filter(group=self.group_a).count(), 1)
274 def test_unique_constraints_per_group(self) -> None:
275 """Test that unique constraints work correctly per group."""
276 # Both groups should be able to have vendors with the same name
277 Vendor.objects.create(name="Same Name", group=self.group_a)
278 Vendor.objects.create(name="Same Name", group=self.group_b)
280 # Should have 2 vendors total
281 self.assertEqual(Vendor.objects.filter(name="Same Name").count(), 2)
283 # But each user should only see their own
284 response_a = self.client_a.get(reverse("vendor:index"))
285 response_b = self.client_b.get(reverse("vendor:index"))
287 # Count occurrences in response (this is a simple check)
288 self.assertContains(response_a, "Same Name")
289 self.assertContains(response_b, "Same Name")
291 def test_group_deletion_cascades_to_items(self) -> None:
292 """Test that deleting a group properly handles related items."""
293 # Create some items in group A
294 Vendor.objects.create(name="Vendor A1", group=self.group_a)
295 Vendor.objects.create(name="Vendor A2", group=self.group_a)
297 initial_count = Vendor.objects.count()
299 # Delete the group (should be protected by on_delete=models.PROTECT)
300 with self.assertRaises(Exception):
301 self.group_a.delete()
303 # Items should still exist
304 self.assertEqual(Vendor.objects.count(), initial_count)
306 def test_user_cannot_access_other_group_items_directly(self) -> None:
307 """Test that users cannot access items from other groups via direct URLs."""
308 # Create an item in group B
309 vendor_b = Vendor.objects.create(name="Vendor B Private", group=self.group_b)
311 # User A should not be able to access this item's detail page
312 response = self.client_a.get(reverse("vendor:detail", args=[vendor_b.pk]))
314 # Should either return 404 or 403
315 self.assertIn(response.status_code, [403, 404])
317 def test_starter_pack_with_existing_data(self) -> None:
318 """Test starter pack import when group already has some data."""
319 # Create some existing data
320 Vendor.objects.create(name="Existing Vendor", group=self.group_a)
322 # Import starter pack
323 results = import_starter_pack(self.group_a)
325 # Should still import new vendors but not duplicate existing ones
326 self.assertGreaterEqual(results["vendors"], 0)
328 # Existing vendor should still be there
329 self.assertTrue(
330 Vendor.objects.filter(group=self.group_a, name="Existing Vendor").exists()
331 )
333 def test_quota_zero_allows_no_items(self) -> None:
334 """Test that setting quota to zero prevents all item creation."""
335 self.profile_a.max_items = 0
336 self.profile_a.save()
338 # Grant add permission
339 add_perm = Permission.objects.get(
340 codename="add_model", content_type__app_label="vendor"
341 )
342 self.group_a.permissions.add(add_perm)
344 # Try to create an item (should fail)
345 response = self.client_a.post(
346 reverse("vendor:create"),
347 {"name": "Vendor Zero Quota", "is_hardware": True, "is_software": True},
348 follow=True,
349 )
350 self.assertContains(response, "Quota exceeded")
351 self.assertEqual(Vendor.objects.filter(group=self.group_a).count(), 0)
353 def test_group_profile_creation_on_group_creation(self) -> None:
354 """Test GroupProfile auto-creation when new group is created."""
355 # Create a new group
356 new_group = Group.objects.create(name="New Group")
358 # Should have a profile
359 self.assertTrue(hasattr(new_group, "profile"))
360 self.assertIsNotNone(new_group.profile)
362 # Profile should have default values
363 self.assertEqual(new_group.profile.max_items, 200)
364 self.assertEqual(new_group.profile.max_users, 2)