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

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 

8 

9 

10class FullIntegrationTest(StaticLiveServerTestCase): 

11 @classmethod 

12 def setUpClass(cls): 

13 os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true" 

14 from django.conf import settings 

15 

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 

28 

29 EmailAddress.objects.create( 

30 user=cls.user, email="admin@example.com", verified=True, primary=True 

31 ) 

32 

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 

37 

38 if apps.is_installed("allauth.socialaccount"): 

39 from allauth.socialaccount.models import SocialApp 

40 

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) 

47 

48 def random_string(self, length=8): 

49 return "".join(random.choices(string.ascii_lowercase + string.digits, k=length)) 

50 

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() 

55 

56 # Login via Django session 

57 from django.test import Client 

58 

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 ) 

72 

73 page = await context.new_page() 

74 

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"]') 

82 

83 from vendor.models import Model as Vendor 

84 

85 vendor1 = await asyncio.to_thread(Vendor.objects.get, name=vendor_name) 

86 

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) 

95 

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") 

102 

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"]') 

108 

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 ) 

125 

126 exists = await asyncio.to_thread( 

127 Vendor.objects.filter(pk=vendor1.pk).exists 

128 ) 

129 self.assertFalse(exists, "Vendor 1 was not deleted!") 

130 

131 from operatingsystem.models import Model as OS 

132 

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!") 

136 

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) 

145 

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"]') 

152 

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") 

157 

158 page.once("dialog", lambda dialog: dialog.accept()) 

159 async with page.expect_navigation(): 

160 await page.click('button:has-text("Delete Everything")') 

161 

162 exists = await asyncio.to_thread( 

163 Vendor.objects.filter(pk=vendor3.pk).exists 

164 ) 

165 self.assertFalse(exists, "Vendor 3 was not deleted!") 

166 

167 await browser.close() 

168 

169 def test_full_crud_and_safe_delete(self): 

170 asyncio.run(self._async_test_crud())