Coverage for sm / test_integration.py: 0%
110 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
1import os
2import asyncio
3from django.contrib.staticfiles.testing import StaticLiveServerTestCase
4from django.contrib.auth import get_user_model
5from playwright.async_api import async_playwright
6import random
7import string
10class FullIntegrationTest(StaticLiveServerTestCase):
11 @classmethod
12 def setUpClass(cls):
13 os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
14 from django.conf import settings
16 settings.ACCOUNT_EMAIL_VERIFICATION = "none"
17 settings.AUTHENTICATION_BACKENDS = ["django.contrib.auth.backends.ModelBackend"]
18 super().setUpClass()
19 User = get_user_model()
20 cls.username = "testadmin"
21 cls.password = "testpass123"
22 cls.user = User.objects.create_superuser(
23 cls.username, "admin@example.com", cls.password
24 )
25 cls.user.set_password(cls.password)
26 cls.user.save()
27 from allauth.account.models import EmailAddress
29 EmailAddress.objects.create(
30 user=cls.user, email="admin@example.com", verified=True, primary=True
31 )
33 def setUp(self):
34 # Create SocialApp records needed for allauth login tags to work
35 from django.contrib.sites.models import Site
36 from django.apps import apps
38 if apps.is_installed("allauth.socialaccount"):
39 from allauth.socialaccount.models import SocialApp
41 site = Site.objects.get_current()
42 SocialApp.objects.get_or_create(
43 provider="facebook", name="Facebook", client_id="123", secret="abc"
44 )
45 for app in SocialApp.objects.all():
46 app.sites.add(site)
48 def random_string(self, length=8):
49 return "".join(random.choices(string.ascii_lowercase + string.digits, k=length))
51 async def _async_test_crud(self):
52 async with async_playwright() as p:
53 browser = await p.chromium.launch(headless=True)
54 context = await browser.new_context()
56 # Login via Django session
57 from django.test import Client
59 client = Client()
60 client.force_login(self.user)
61 session_key = client.cookies["sessionid"].value
62 await context.add_cookies(
63 [
64 {
65 "name": "sessionid",
66 "value": session_key,
67 "domain": "localhost",
68 "path": "/",
69 }
70 ]
71 )
73 page = await context.new_page()
75 # 1. Create a Vendor (enable software)
76 vendor_name = f"Vendor-{self.random_string()}"
77 await page.goto(f"{self.live_server_url}/vendor/create")
78 await page.fill('input[name="name"]', vendor_name)
79 await page.set_checked("#id_is_software", True)
80 async with page.expect_navigation():
81 await page.click('form.form button[type="submit"]')
83 from vendor.models import Model as Vendor
85 vendor1 = await asyncio.to_thread(Vendor.objects.get, name=vendor_name)
87 # 2. Create a second Vendor
88 vendor_name2 = f"Vendor-Safe-{self.random_string()}"
89 await page.goto(f"{self.live_server_url}/vendor/create")
90 await page.fill('input[name="name"]', vendor_name2)
91 await page.set_checked("#id_is_software", True)
92 async with page.expect_navigation():
93 await page.click('form.form button[type="submit"]')
94 vendor2 = await asyncio.to_thread(Vendor.objects.get, name=vendor_name2)
96 # 3. Create an OS
97 os_version = f"OS-{self.random_string()}"
98 await page.goto(f"{self.live_server_url}/operatingsystem/create")
99 await page.wait_for_load_state("networkidle")
100 await asyncio.sleep(2)
101 await page.screenshot(path="os_form_ready.png")
103 await page.fill('input[name="version"]', os_version)
104 await page.select_option('select[name="vendor"]', label=vendor_name)
105 await page.screenshot(path="os_form_filled.png")
106 async with page.expect_navigation():
107 await page.click('form.form button[type="submit"]')
109 # 4. Test Protected Deletion (Reassignment)
110 await page.goto(f"{self.live_server_url}/vendor/delete/{vendor1.pk}/")
111 try:
112 await page.click('button:has-text("Confirm Delete")')
113 except Exception:
114 await page.screenshot(path="delete_confirm_fail.png")
115 print(f"Failed to find Confirm Delete on {page.url}")
116 print(f"PAGE CONTENT: {await page.content()}")
117 raise
118 await page.wait_for_selector("text=Action Required: This item is in use")
119 # 5. Test Reassignment
120 await page.select_option('select[name="new_target"]', value=str(vendor2.pk))
121 async with page.expect_navigation():
122 await page.click(
123 f'button:has-text("Reassign items and delete the {vendor1.name}")'
124 )
126 exists = await asyncio.to_thread(
127 Vendor.objects.filter(pk=vendor1.pk).exists
128 )
129 self.assertFalse(exists, "Vendor 1 was not deleted!")
131 from operatingsystem.models import Model as OS
133 os_obj = await asyncio.to_thread(OS.objects.get, version=os_version)
134 v_id = await asyncio.to_thread(lambda: os_obj.vendor.id)
135 self.assertEqual(v_id, vendor2.id, "OS was not reassigned to Vendor 2!")
137 # 5. Test Bulk Delete (Danger Zone)
138 v3_name = f"Vendor-Bulk-{self.random_string()}"
139 await page.goto(f"{self.live_server_url}/vendor/create")
140 await page.fill('input[name="name"]', v3_name)
141 await page.set_checked("#id_is_software", True)
142 async with page.expect_navigation():
143 await page.click('form.form button[type="submit"]')
144 vendor3 = await asyncio.to_thread(Vendor.objects.get, name=v3_name)
146 # Link an OS to it
147 await page.goto(f"{self.live_server_url}/operatingsystem/create")
148 await page.fill('input[name="version"]', f"OS-Bulk-{self.random_string()}")
149 await page.select_option('select[name="vendor"]', label=v3_name)
150 async with page.expect_navigation():
151 await page.click('form.form button[type="submit"]')
153 # Now delete Vendor 3 with "Delete All"
154 await page.goto(f"{self.live_server_url}/vendor/delete/{vendor3.pk}/")
155 await page.click('button:has-text("Confirm Delete")')
156 await page.wait_for_selector("text=Action Required: This item is in use")
158 page.once("dialog", lambda dialog: dialog.accept())
159 async with page.expect_navigation():
160 await page.click('button:has-text("Delete Everything")')
162 exists = await asyncio.to_thread(
163 Vendor.objects.filter(pk=vendor3.pk).exists
164 )
165 self.assertFalse(exists, "Vendor 3 was not deleted!")
167 await browser.close()
169 def test_full_crud_and_safe_delete(self):
170 asyncio.run(self._async_test_crud())