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
« 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
14def test_stub_math():
15 assert 1 + 1 == 2
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"
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")
33@pytest.mark.django_db
34def test_loguser_factory():
35 user = LogUserFactory()
36 assert LogUser.objects.filter(pk=user.pk).exists()
38class LogPathFactory(factory.django.DjangoModelFactory):
39 class Meta:
40 model = 'django_audit_log.LogPath'
41 path = factory.Faker('uri_path')
43class LogSessionKeyFactory(factory.django.DjangoModelFactory):
44 class Meta:
45 model = 'django_audit_log.LogSessionKey'
46 key = factory.Faker('uuid4')
48class LogIpAddressFactory(factory.django.DjangoModelFactory):
49 class Meta:
50 model = 'django_audit_log.LogIpAddress'
51 address = factory.Faker('ipv4')
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
64@pytest.mark.django_db
65def test_logpath_factory():
66 obj = LogPathFactory()
67 assert LogPath.objects.filter(pk=obj.pk).exists()
69@pytest.mark.django_db
70def test_logsessionkey_factory():
71 obj = LogSessionKeyFactory()
72 assert LogSessionKey.objects.filter(pk=obj.pk).exists()
74@pytest.mark.django_db
75def test_logipaddress_factory():
76 obj = LogIpAddressFactory()
77 assert LogIpAddress.objects.filter(pk=obj.pk).exists()
79@pytest.mark.django_db
80def test_loguseragent_factory():
81 obj = LogUserAgentFactory()
82 assert LogUserAgent.objects.filter(pk=obj.pk).exists()
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
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
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('') == ''
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()
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()
134 # No referrer
135 request2 = HttpRequest()
136 assert LogPath.from_referrer(request2) is None
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
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
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
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()
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
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
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
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"
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)
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
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
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"
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
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
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
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
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
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
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
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
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
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
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
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