Coverage for sm / test_multitenancy_expanded.py: 0%
162 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-24 12:43 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-24 12:43 +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 client_no_group = Client()
224 client_no_group.login(username="user_no_group", password=self.password)
226 # Grant necessary permissions
227 add_perm = Permission.objects.get(
228 codename="add_model", content_type__app_label="vendor"
229 )
230 view_perm = Permission.objects.get(
231 codename="view_model", content_type__app_label="vendor"
232 )
233 user_no_group.user_permissions.add(add_perm, view_perm)
235 # User should be able to create items but without group assignment
236 response = client_no_group.post(
237 reverse("vendor:create"),
238 {"name": "Vendor No Group", "is_hardware": True, "is_software": True},
239 follow=True,
240 )
242 # Should succeed and create item without a group
243 self.assertEqual(response.status_code, 200)
244 vendor = Vendor.objects.filter(name="Vendor No Group").first()
245 self.assertIsNotNone(vendor)
246 self.assertIsNone(vendor.group)
248 def test_concurrent_quota_checks(self) -> None:
249 """Test that concurrent quota checks are handled properly."""
250 self.profile_a.max_items = 1
251 self.profile_a.save()
253 # Grant add permission
254 add_perm = Permission.objects.get(
255 codename="add_model", content_type__app_label="vendor"
256 )
257 self.group_a.permissions.add(add_perm)
259 # Create one vendor to reach the limit
260 Vendor.objects.create(name="Vendor 1", group=self.group_a)
262 # Try to create another vendor (should fail due to quota)
263 response = self.client_a.post(
264 reverse("vendor:create"),
265 {"name": "Vendor 2", "is_hardware": True, "is_software": True},
266 follow=True,
267 )
268 self.assertContains(response, "Quota exceeded")
269 self.assertEqual(Vendor.objects.filter(group=self.group_a).count(), 1)
271 def test_unique_constraints_per_group(self) -> None:
272 """Test that unique constraints work correctly per group."""
273 # Both groups should be able to have vendors with the same name
274 Vendor.objects.create(name="Same Name", group=self.group_a)
275 Vendor.objects.create(name="Same Name", group=self.group_b)
277 # Should have 2 vendors total
278 self.assertEqual(Vendor.objects.filter(name="Same Name").count(), 2)
280 # But each user should only see their own
281 response_a = self.client_a.get(reverse("vendor:index"))
282 response_b = self.client_b.get(reverse("vendor:index"))
284 # Count occurrences in response (this is a simple check)
285 self.assertContains(response_a, "Same Name")
286 self.assertContains(response_b, "Same Name")
288 def test_group_deletion_cascades_to_items(self) -> None:
289 """Test that deleting a group properly handles related items."""
290 # Create some items in group A
291 Vendor.objects.create(name="Vendor A1", group=self.group_a)
292 Vendor.objects.create(name="Vendor A2", group=self.group_a)
294 initial_count = Vendor.objects.count()
296 # Delete the group (should be protected by on_delete=models.PROTECT)
297 with self.assertRaises(Exception):
298 self.group_a.delete()
300 # Items should still exist
301 self.assertEqual(Vendor.objects.count(), initial_count)
303 def test_user_cannot_access_other_group_items_directly(self) -> None:
304 """Test that users cannot access items from other groups via direct URLs."""
305 # Create an item in group B
306 vendor_b = Vendor.objects.create(name="Vendor B Private", group=self.group_b)
308 # User A should not be able to access this item's detail page
309 response = self.client_a.get(reverse("vendor:detail", args=[vendor_b.pk]))
311 # Should either return 404 or 403
312 self.assertIn(response.status_code, [403, 404])
314 def test_starter_pack_with_existing_data(self) -> None:
315 """Test starter pack import when group already has some data."""
316 # Create some existing data
317 Vendor.objects.create(name="Existing Vendor", group=self.group_a)
319 # Import starter pack
320 results = import_starter_pack(self.group_a)
322 # Should still import new vendors but not duplicate existing ones
323 self.assertGreaterEqual(results["vendors"], 0)
325 # Existing vendor should still be there
326 self.assertTrue(
327 Vendor.objects.filter(group=self.group_a, name="Existing Vendor").exists()
328 )
330 def test_quota_zero_allows_no_items(self) -> None:
331 """Test that setting quota to zero prevents all item creation."""
332 self.profile_a.max_items = 0
333 self.profile_a.save()
335 # Grant add permission
336 add_perm = Permission.objects.get(
337 codename="add_model", content_type__app_label="vendor"
338 )
339 self.group_a.permissions.add(add_perm)
341 # Try to create an item (should fail)
342 response = self.client_a.post(
343 reverse("vendor:create"),
344 {"name": "Vendor Zero Quota", "is_hardware": True, "is_software": True},
345 follow=True,
346 )
347 self.assertContains(response, "Quota exceeded")
348 self.assertEqual(Vendor.objects.filter(group=self.group_a).count(), 0)
350 def test_group_profile_creation_on_group_creation(self) -> None:
351 """Test GroupProfile auto-creation when new group is created."""
352 # Create a new group
353 new_group = Group.objects.create(name="New Group")
355 # Should have a profile
356 self.assertTrue(hasattr(new_group, "profile"))
357 self.assertIsNotNone(new_group.profile)
359 # Profile should have default values
360 self.assertEqual(new_group.profile.max_items, 200)
361 self.assertEqual(new_group.profile.max_users, 2)