Coverage for src/django_audit_log/tests.py: 100%

324 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-02 11:48 +0700

1import pytest 

2from django.urls import reverse 

3from django.contrib.auth import get_user_model 

4from django.contrib.admin.sites import site 

5import factory 

6from .models import LogUser, AccessLog, LogPath, LogSessionKey, LogIpAddress, LogUserAgent, UserAgentUtil 

7import types 

8from django.http import HttpRequest, HttpResponse 

9from django.contrib.auth.models import AnonymousUser 

10from django.test import RequestFactory 

11from django_audit_log import admin as audit_admin 

12from django.contrib import admin 

13 

14def test_stub_math(): 

15 assert 1 + 1 == 2 

16 

17@pytest.mark.django_db 

18def test_admin_pages_accessible(admin_client): 

19 # Get all registered models 

20 for model, model_admin in site._registry.items(): 

21 app_label = model._meta.app_label 

22 model_name = model._meta.model_name 

23 url = reverse(f'admin:{app_label}_{model_name}_changelist') 

24 response = admin_client.get(url) 

25 assert response.status_code == 200, f"Admin page for {model.__name__} not accessible" 

26 

27class LogUserFactory(factory.django.DjangoModelFactory): 

28 class Meta: 

29 model = LogUser 

30 id = factory.Sequence(lambda n: n + 1) 

31 user_name = factory.Faker("user_name") 

32 

33@pytest.mark.django_db 

34def test_loguser_factory(): 

35 user = LogUserFactory() 

36 assert LogUser.objects.filter(pk=user.pk).exists() 

37 

38class LogPathFactory(factory.django.DjangoModelFactory): 

39 class Meta: 

40 model = 'django_audit_log.LogPath' 

41 path = factory.Faker('uri_path') 

42 

43class LogSessionKeyFactory(factory.django.DjangoModelFactory): 

44 class Meta: 

45 model = 'django_audit_log.LogSessionKey' 

46 key = factory.Faker('uuid4') 

47 

48class LogIpAddressFactory(factory.django.DjangoModelFactory): 

49 class Meta: 

50 model = 'django_audit_log.LogIpAddress' 

51 address = factory.Faker('ipv4') 

52 

53class LogUserAgentFactory(factory.django.DjangoModelFactory): 

54 class Meta: 

55 model = 'django_audit_log.LogUserAgent' 

56 user_agent = factory.Faker('user_agent') 

57 browser = factory.Faker('chrome') 

58 browser_version = factory.Faker('numerify', text='##.0') 

59 operating_system = factory.Faker('linux_platform_token') 

60 operating_system_version = factory.Faker('numerify', text='##.##') 

61 device_type = factory.Iterator(['Desktop', 'Mobile', 'Tablet']) 

62 is_bot = False 

63 

64@pytest.mark.django_db 

65def test_logpath_factory(): 

66 obj = LogPathFactory() 

67 assert LogPath.objects.filter(pk=obj.pk).exists() 

68 

69@pytest.mark.django_db 

70def test_logsessionkey_factory(): 

71 obj = LogSessionKeyFactory() 

72 assert LogSessionKey.objects.filter(pk=obj.pk).exists() 

73 

74@pytest.mark.django_db 

75def test_logipaddress_factory(): 

76 obj = LogIpAddressFactory() 

77 assert LogIpAddress.objects.filter(pk=obj.pk).exists() 

78 

79@pytest.mark.django_db 

80def test_loguseragent_factory(): 

81 obj = LogUserAgentFactory() 

82 assert LogUserAgent.objects.filter(pk=obj.pk).exists() 

83 

84class AccessLogFactory(factory.django.DjangoModelFactory): 

85 class Meta: 

86 model = AccessLog 

87 path = factory.SubFactory(LogPathFactory) 

88 referrer = factory.SubFactory(LogPathFactory) 

89 response_url = factory.SubFactory(LogPathFactory) 

90 method = factory.Iterator(["GET", "POST", "PUT", "DELETE"]) 

91 data = factory.LazyFunction(lambda: {"foo": "bar"}) 

92 status_code = 200 

93 user_agent = factory.Faker("user_agent") 

94 user_agent_normalized = factory.SubFactory(LogUserAgentFactory) 

95 user = factory.SubFactory(LogUserFactory) 

96 session_key = factory.SubFactory(LogSessionKeyFactory) 

97 ip = factory.SubFactory(LogIpAddressFactory) 

98 in_always_log_urls = False 

99 in_sample_urls = False 

100 sample_rate = 1.0 

101 

102@pytest.mark.django_db 

103def test_accesslog_factory(): 

104 log = AccessLogFactory() 

105 assert AccessLog.objects.filter(pk=log.pk).exists() 

106 assert log.user is not None 

107 assert log.ip is not None 

108 assert log.session_key is not None 

109 assert log.path is not None 

110 assert log.user_agent_normalized is not None 

111 

112@pytest.mark.django_db 

113def test_logpath_normalize_path(): 

114 assert LogPath.normalize_path('https://example.com/foo/bar') == '/foo/bar' 

115 assert LogPath.normalize_path('/foo/bar') == '/foo/bar' 

116 assert LogPath.normalize_path('') == '' 

117 

118@pytest.mark.django_db 

119def test_logpath_from_request(): 

120 request = HttpRequest() 

121 request.path = '/test/path' 

122 obj = LogPath.from_request(request) 

123 assert obj.path == '/test/path' 

124 assert LogPath.objects.filter(path='/test/path').exists() 

125 

126@pytest.mark.django_db 

127def test_logpath_from_referrer(): 

128 request = HttpRequest() 

129 request.META['HTTP_REFERER'] = 'https://example.com/ref/path' 

130 obj = LogPath.from_referrer(request) 

131 assert obj.path == '/ref/path' 

132 assert LogPath.objects.filter(path='/ref/path').exists() 

133 

134 # No referrer 

135 request2 = HttpRequest() 

136 assert LogPath.from_referrer(request2) is None 

137 

138@pytest.mark.django_db 

139def test_logpath_from_response(): 

140 class DummyResponse: 

141 url = 'https://example.com/resp/path' 

142 response = DummyResponse() 

143 obj = LogPath.from_response(response) 

144 assert obj.path == '/resp/path' 

145 assert LogPath.objects.filter(path='/resp/path').exists() 

146 # None response 

147 assert LogPath.from_response(None) is None 

148 # Response with no url 

149 class NoUrl: pass 

150 assert LogPath.from_response(NoUrl()) is None 

151 

152@pytest.mark.django_db 

153def test_logsessionkey_from_request(): 

154 request = HttpRequest() 

155 request.session = types.SimpleNamespace(session_key='abc123') 

156 obj = LogSessionKey.from_request(request) 

157 assert obj.key == 'abc123' 

158 assert LogSessionKey.objects.filter(key='abc123').exists() 

159 # No session key 

160 request2 = HttpRequest() 

161 request2.session = types.SimpleNamespace(session_key=None) 

162 assert LogSessionKey.from_request(request2) is None 

163 

164@pytest.mark.django_db 

165def test_loguser_from_request(db, django_user_model): 

166 # Anonymous user 

167 request = HttpRequest() 

168 request.user = types.SimpleNamespace(is_anonymous=True) 

169 obj = LogUser.from_request(request) 

170 assert obj.id == 0 

171 assert obj.user_name == 'anonymous' 

172 # Authenticated user 

173 user = django_user_model.objects.create(username='bob', id=42) 

174 request2 = HttpRequest() 

175 request2.user = user 

176 obj2 = LogUser.from_request(request2) 

177 assert obj2.id == user.pk 

178 assert obj2.user_name == user.username 

179 

180@pytest.mark.django_db 

181def test_logipaddress_from_request(): 

182 request = HttpRequest() 

183 request.META['REMOTE_ADDR'] = '1.2.3.4' 

184 obj = LogIpAddress.from_request(request) 

185 assert obj.address == '1.2.3.4' 

186 assert LogIpAddress.objects.filter(address='1.2.3.4').exists() 

187 # With X-Forwarded-For 

188 request2 = HttpRequest() 

189 request2.META['HTTP_X_FORWARDED_FOR'] = '5.6.7.8, 9.10.11.12' 

190 obj2 = LogIpAddress.from_request(request2) 

191 assert obj2.address == '5.6.7.8' 

192 assert LogIpAddress.objects.filter(address='5.6.7.8').exists() 

193 

194@pytest.mark.django_db 

195def test_loguseragent_from_user_agent_string(): 

196 ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/90.0.4430.212' 

197 obj = LogUserAgent.from_user_agent_string(ua) 

198 assert obj.user_agent == ua 

199 assert obj.browser == 'Chrome' 

200 assert obj.operating_system == 'Windows 10' 

201 assert LogUserAgent.objects.filter(user_agent=ua).exists() 

202 # None input 

203 assert LogUserAgent.from_user_agent_string(None) is None 

204 

205def test_useragentutil_normalize_user_agent(): 

206 ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/90.0.4430.212' 

207 info = UserAgentUtil.normalize_user_agent(ua) 

208 assert info['browser'] == 'Chrome' 

209 assert info['os'] == 'Windows 10' 

210 assert info['device_type'] == 'Mobile' or info['device_type'] == 'Desktop' or info['device_type'] == 'Unknown' 

211 # Eskola APK 

212 eskola_ua = 'tl.eskola.eskola_app-1.2.3-release/Pixel4' 

213 info2 = UserAgentUtil.normalize_user_agent(eskola_ua) 

214 assert info2['browser'] == 'Eskola APK' 

215 assert info2['os'] == 'Android' 

216 assert 'Device:' in info2['os_version'] 

217 # Bot 

218 bot_ua = 'Googlebot/2.1 (+http://www.google.com/bot.html)' 

219 info3 = UserAgentUtil.normalize_user_agent(bot_ua) 

220 assert info3['is_bot'] is True 

221 

222@pytest.mark.django_db 

223def test_readonlyadmin_permissions(): 

224 class DummyRequest: pass 

225 dummy = DummyRequest() 

226 ro_admin = audit_admin.ReadOnlyAdmin(LogUser, admin.site) 

227 assert ro_admin.has_add_permission(dummy) is False 

228 assert ro_admin.has_change_permission(dummy) is False 

229 assert ro_admin.has_delete_permission(dummy) is False 

230 

231@pytest.mark.django_db 

232def test_accesslogadmin_browser_type(): 

233 log = AccessLogFactory() 

234 admin_obj = audit_admin.AccessLogAdmin(AccessLog, admin.site) 

235 assert admin_obj.browser_type(log) == log.user_agent_normalized.browser 

236 log.user_agent_normalized = None 

237 assert admin_obj.browser_type(log) == "Unknown" 

238 

239@pytest.mark.django_db 

240def test_accesslogadmin_normalized_user_agent(): 

241 log = AccessLogFactory() 

242 admin_obj = audit_admin.AccessLogAdmin(AccessLog, admin.site) 

243 html = admin_obj.normalized_user_agent(log) 

244 assert "ua-info" in html 

245 log.user_agent_normalized = None 

246 assert "No user agent data" in admin_obj.normalized_user_agent(log) 

247 

248@pytest.mark.django_db 

249def test_loguseradmin_access_count(): 

250 user = LogUserFactory() 

251 log = AccessLogFactory(user=user) 

252 admin_obj = audit_admin.LogUserAdmin(LogUser, admin.site) 

253 obj = user 

254 setattr(obj, "access_count", 1) 

255 assert admin_obj.access_count(obj) == 1 

256 

257@pytest.mark.django_db 

258def test_loguseradmin_ip_addresses_count(): 

259 user = LogUserFactory() 

260 log = AccessLogFactory(user=user) 

261 admin_obj = audit_admin.LogUserAdmin(LogUser, admin.site) 

262 obj = user 

263 setattr(obj, "ip_count", 2) 

264 assert admin_obj.ip_addresses_count(obj) == 2 

265 

266@pytest.mark.django_db 

267def test_loguseradmin_last_active(): 

268 user = LogUserFactory() 

269 log = AccessLogFactory(user=user) 

270 admin_obj = audit_admin.LogUserAdmin(LogUser, admin.site) 

271 obj = user 

272 from django.utils import timezone 

273 now = timezone.now() 

274 setattr(obj, "last_activity", now) 

275 assert str(admin_obj.last_active(obj))[:4].isdigit() or admin_obj.last_active(obj) == "Never" 

276 # No last_activity 

277 obj2 = LogUserFactory() 

278 assert admin_obj.last_active(obj2) == "Never" 

279 

280@pytest.mark.django_db 

281def test_loguseradmin_user_agent_stats(): 

282 user = LogUserFactory() 

283 log = AccessLogFactory(user=user) 

284 admin_obj = audit_admin.LogUserAdmin(LogUser, admin.site) 

285 html = admin_obj.user_agent_stats(user) 

286 assert "ua-stats" in html or "No user agent data available" in html 

287 

288@pytest.mark.django_db 

289def test_loguseradmin_recent_activity(): 

290 user = LogUserFactory() 

291 log = AccessLogFactory(user=user) 

292 admin_obj = audit_admin.LogUserAdmin(LogUser, admin.site) 

293 html = admin_obj.recent_activity(user) 

294 assert "activity-list" in html or "No recent activity" in html 

295 

296@pytest.mark.django_db 

297def test_loguseradmin_ip_addresses_used(): 

298 user = LogUserFactory() 

299 log = AccessLogFactory(user=user, ip=LogIpAddressFactory()) 

300 admin_obj = audit_admin.LogUserAdmin(LogUser, admin.site) 

301 html = admin_obj.ip_addresses_used(user) 

302 assert "ip-list" in html or "No IP addresses recorded" in html 

303 

304@pytest.mark.django_db 

305def test_loguseradmin_url_access_stats(): 

306 user = LogUserFactory() 

307 log = AccessLogFactory(user=user, path=LogPathFactory()) 

308 admin_obj = audit_admin.LogUserAdmin(LogUser, admin.site) 

309 html = admin_obj.url_access_stats(user) 

310 assert "url-table" in html or "No URLs recorded" in html 

311 

312@pytest.mark.django_db 

313def test_loguseradmin_distinct_user_agents(): 

314 user = LogUserFactory() 

315 log = AccessLogFactory(user=user, user_agent_normalized=LogUserAgentFactory()) 

316 admin_obj = audit_admin.LogUserAdmin(LogUser, admin.site) 

317 html = admin_obj.distinct_user_agents(user) 

318 assert "ua-raw" in html or "No user agent data available" in html 

319 

320@pytest.mark.django_db 

321def test_logipaddressadmin_user_count(): 

322 ip = LogIpAddressFactory() 

323 log = AccessLogFactory(ip=ip) 

324 admin_obj = audit_admin.LogIpAddressAdmin(LogIpAddress, admin.site) 

325 obj = ip 

326 setattr(obj, "user_count", 1) 

327 assert admin_obj.user_count(obj) == 1 

328 

329@pytest.mark.django_db 

330def test_logipaddressadmin_request_count(): 

331 ip = LogIpAddressFactory() 

332 log = AccessLogFactory(ip=ip) 

333 admin_obj = audit_admin.LogIpAddressAdmin(LogIpAddress, admin.site) 

334 obj = ip 

335 setattr(obj, "request_count", 2) 

336 assert admin_obj.request_count(obj) == 2 

337 

338@pytest.mark.django_db 

339def test_logipaddressadmin_user_agent_stats(): 

340 ip = LogIpAddressFactory() 

341 log = AccessLogFactory(ip=ip) 

342 admin_obj = audit_admin.LogIpAddressAdmin(LogIpAddress, admin.site) 

343 html = admin_obj.user_agent_stats(ip) 

344 assert "ua-stats" in html or "No user agent data available" in html 

345 

346@pytest.mark.django_db 

347def test_loguseragentadmin_usage_count(): 

348 ua = LogUserAgentFactory() 

349 admin_obj = audit_admin.LogUserAgentAdmin(LogUserAgent, admin.site) 

350 obj = ua 

351 setattr(obj, "usage_count", 3) 

352 assert admin_obj.usage_count(obj) == 3 

353 

354@pytest.mark.django_db 

355def test_loguseragentadmin_unique_users_count(): 

356 ua = LogUserAgentFactory() 

357 admin_obj = audit_admin.LogUserAgentAdmin(LogUserAgent, admin.site) 

358 obj = ua 

359 setattr(obj, "unique_users", 2) 

360 assert admin_obj.unique_users_count(obj) == 2 

361 

362@pytest.mark.django_db 

363def test_loguseragentadmin_usage_details(): 

364 ua = LogUserAgentFactory() 

365 log = AccessLogFactory(user_agent_normalized=ua) 

366 admin_obj = audit_admin.LogUserAgentAdmin(LogUserAgent, admin.site) 

367 setattr(ua, "usage_count", 1) 

368 html = admin_obj.usage_details(ua) 

369 assert "ua-usage" in html 

370 

371@pytest.mark.django_db 

372def test_loguseragentadmin_related_users(): 

373 ua = LogUserAgentFactory() 

374 user = LogUserFactory() 

375 log = AccessLogFactory(user_agent_normalized=ua, user=user) 

376 admin_obj = audit_admin.LogUserAgentAdmin(LogUserAgent, admin.site) 

377 html = admin_obj.related_users(ua) 

378 assert "user-list" in html or "No users have used this user agent" in html