mirror of
https://github.com/jazzband/django-auditlog.git
synced 2026-03-16 22:20:26 +00:00
* split auditlog HTML render logic from Admin mixin into reusable functions * add AuditlogHistoryAdminMixin class * add test cases for auditlog html render functions * add audit log history view documentation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix minor * Add missing versionadded and configuration options for AuditlogHistoryAdminMixin * Add missing test cases * Update versionadded to 3.2.2 for AuditlogHistoryAdminMixin * Update CHANGELOG.md --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
175 lines
6 KiB
Python
175 lines
6 KiB
Python
from unittest.mock import patch
|
|
|
|
from django.contrib import admin
|
|
from django.contrib.admin.sites import AdminSite
|
|
from django.contrib.auth import get_user_model
|
|
from django.test import RequestFactory, TestCase
|
|
from test_app.models import SimpleModel
|
|
|
|
from auditlog.mixins import AuditlogHistoryAdminMixin
|
|
|
|
|
|
class TestModelAdmin(AuditlogHistoryAdminMixin, admin.ModelAdmin):
|
|
model = SimpleModel
|
|
auditlog_history_per_page = 5
|
|
|
|
|
|
class TestAuditlogHistoryAdminMixin(TestCase):
|
|
def setUp(self):
|
|
self.user = get_user_model().objects.create_user(
|
|
username="test_admin", is_staff=True, is_superuser=True, is_active=True
|
|
)
|
|
self.site = AdminSite()
|
|
|
|
self.admin = TestModelAdmin(SimpleModel, self.site)
|
|
|
|
self.obj = SimpleModel.objects.create(text="Test object")
|
|
|
|
def test_auditlog_history_view_requires_permission(self):
|
|
request = RequestFactory().get("/")
|
|
request.user = get_user_model().objects.create_user(
|
|
username="non_staff_user", password="testpass"
|
|
)
|
|
|
|
with self.assertRaises(Exception):
|
|
self.admin.auditlog_history_view(request, str(self.obj.pk))
|
|
|
|
def test_auditlog_history_view_with_permission(self):
|
|
request = RequestFactory().get("/")
|
|
request.user = self.user
|
|
|
|
response = self.admin.auditlog_history_view(request, str(self.obj.pk))
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn("log_entries", response.context_data)
|
|
self.assertIn("object", response.context_data)
|
|
self.assertEqual(response.context_data["object"], self.obj)
|
|
|
|
def test_auditlog_history_view_pagination(self):
|
|
"""Test that pagination works correctly."""
|
|
for i in range(10):
|
|
self.obj.text = f"Updated text {i}"
|
|
self.obj.save()
|
|
|
|
request = RequestFactory().get("/")
|
|
request.user = self.user
|
|
|
|
response = self.admin.auditlog_history_view(request, str(self.obj.pk))
|
|
|
|
self.assertTrue(response.context_data["pagination_required"])
|
|
self.assertEqual(len(response.context_data["log_entries"]), 5)
|
|
|
|
def test_auditlog_history_view_page_parameter(self):
|
|
# Create more log entries by updating the object
|
|
for i in range(10):
|
|
self.obj.text = f"Updated text {i}"
|
|
self.obj.save()
|
|
|
|
request = RequestFactory().get("/?p=2")
|
|
request.user = self.user
|
|
|
|
response = self.admin.auditlog_history_view(request, str(self.obj.pk))
|
|
|
|
# Should be on page 2
|
|
self.assertEqual(response.context_data["log_entries"].number, 2)
|
|
|
|
def test_auditlog_history_view_context_data(self):
|
|
request = RequestFactory().get("/")
|
|
request.user = self.user
|
|
|
|
response = self.admin.auditlog_history_view(request, str(self.obj.pk))
|
|
|
|
context = response.context_data
|
|
required_keys = [
|
|
"title",
|
|
"module_name",
|
|
"page_range",
|
|
"page_var",
|
|
"pagination_required",
|
|
"object",
|
|
"opts",
|
|
"log_entries",
|
|
]
|
|
|
|
for key in required_keys:
|
|
self.assertIn(key, context)
|
|
|
|
self.assertIn(str(self.obj), context["title"])
|
|
self.assertEqual(context["object"], self.obj)
|
|
self.assertEqual(context["opts"], self.obj._meta)
|
|
|
|
def test_auditlog_history_view_extra_context(self):
|
|
request = RequestFactory().get("/")
|
|
request.user = self.user
|
|
|
|
extra_context = {"extra_key": "extra_value"}
|
|
response = self.admin.auditlog_history_view(
|
|
request, str(self.obj.pk), extra_context
|
|
)
|
|
|
|
self.assertIn("extra_key", response.context_data)
|
|
self.assertEqual(response.context_data["extra_key"], "extra_value")
|
|
|
|
def test_auditlog_history_view_template(self):
|
|
request = RequestFactory().get("/")
|
|
request.user = self.user
|
|
|
|
response = self.admin.auditlog_history_view(request, str(self.obj.pk))
|
|
|
|
self.assertEqual(response.template_name, self.admin.auditlog_history_template)
|
|
|
|
def test_auditlog_history_view_log_entries_ordering(self):
|
|
self.obj.text = "First update"
|
|
self.obj.save()
|
|
self.obj.text = "Second update"
|
|
self.obj.save()
|
|
|
|
request = RequestFactory().get("/")
|
|
request.user = self.user
|
|
|
|
response = self.admin.auditlog_history_view(request, str(self.obj.pk))
|
|
|
|
log_entries = list(response.context_data["log_entries"])
|
|
self.assertGreaterEqual(log_entries[0].timestamp, log_entries[1].timestamp)
|
|
|
|
def test_get_list_display_with_auditlog_link(self):
|
|
self.admin.show_auditlog_history_link = True
|
|
list_display = self.admin.get_list_display(RequestFactory().get("/"))
|
|
|
|
self.assertIn("auditlog_link", list_display)
|
|
|
|
self.admin.show_auditlog_history_link = False
|
|
list_display = self.admin.get_list_display(RequestFactory().get("/"))
|
|
|
|
self.assertNotIn("auditlog_link", list_display)
|
|
|
|
def test_get_urls_includes_auditlog_url(self):
|
|
urls = self.admin.get_urls()
|
|
|
|
self.assertGreater(len(urls), 0)
|
|
|
|
url_names = [
|
|
url.name for url in urls if hasattr(url, "name") and url.name is not None
|
|
]
|
|
auditlog_urls = [name for name in url_names if "auditlog" in name]
|
|
self.assertGreater(len(auditlog_urls), 0)
|
|
|
|
@patch("auditlog.mixins.reverse")
|
|
def test_auditlog_link(self, mock_reverse):
|
|
"""Test that auditlog_link method returns correct HTML link."""
|
|
# Mock the reverse function to return a test URL
|
|
expected_url = f"/admin/test_app/simplemodel/{self.obj.pk}/auditlog/"
|
|
mock_reverse.return_value = expected_url
|
|
|
|
link_html = self.admin.auditlog_link(self.obj)
|
|
|
|
self.assertIsInstance(link_html, str)
|
|
|
|
self.assertIn("<a href=", link_html)
|
|
self.assertIn("View</a>", link_html)
|
|
|
|
self.assertIn(expected_url, link_html)
|
|
|
|
opts = self.obj._meta
|
|
expected_url_name = f"admin:{opts.app_label}_{opts.model_name}_auditlog"
|
|
mock_reverse.assert_called_once_with(expected_url_name, args=[self.obj.pk])
|