From 41d39486069a93261c650b8fb5a0193cc0558d6b Mon Sep 17 00:00:00 2001 From: Jan-Jelle Kester Date: Fri, 14 Mar 2014 17:15:31 +0100 Subject: [PATCH] Progress with tests --- src/auditlog/middleware.py | 11 +++--- src/auditlog/models.py | 8 +++++ src/testapp/tests.py | 68 +++++++++++++++++++++++++++++++++++++- 3 files changed, 79 insertions(+), 8 deletions(-) diff --git a/src/auditlog/middleware.py b/src/auditlog/middleware.py index 06b116f..a61f65f 100644 --- a/src/auditlog/middleware.py +++ b/src/auditlog/middleware.py @@ -20,19 +20,16 @@ class AuditlogMiddleware(object): """ if hasattr(request, 'user') and hasattr(request.user, 'is_authenticated') and request.user.is_authenticated(): user = request.user - else: - user = None - - request.auditlog_ts = time.time() - set_actor = curry(self.set_actor, user) - pre_save.connect(set_actor, sender=LogEntry, dispatch_uid=(self.__class__, request.auditlog_ts), weak=False) + request.auditlog_ts = time.time() + set_actor = curry(self.set_actor, user) + pre_save.connect(set_actor, sender=LogEntry, dispatch_uid=(self.__class__, request.auditlog_ts), weak=False) def process_response(self, request, response): """ Disconnects the signal receiver to prevent it from staying active. """ # Disconnecting the signal receiver is required because it will not be garbage collected (non-weak reference) - if hasattr(request, 'auditlog_ts'): # admin wipes auditlog_ts from request... + if hasattr(request, 'auditlog_ts'): pre_save.disconnect(sender=LogEntry, dispatch_uid=(self.__class__, request.auditlog_ts)) return response diff --git a/src/auditlog/models.py b/src/auditlog/models.py index 7ed469a..3046826 100644 --- a/src/auditlog/models.py +++ b/src/auditlog/models.py @@ -78,6 +78,11 @@ class LogEntry(models.Model): """ class Action: + """ + The actions that Auditlog distinguishes: creating, updating and deleting objects. Viewing objects is not logged. + The values of the actions are numeric, a higher integer value means a more intrusive action. This may be useful + in some cases when comparing actions because __lt, __lte, __gt, __gte can be used in queries. + """ CREATE = 0 UPDATE = 1 DELETE = 2 @@ -157,6 +162,9 @@ class AuditlogHistoryField(generic.GenericRelation): By default this field will assume that your primary keys are numeric, simply because this is the most common case. However, if you have a non-integer primary key, you can simply pass pk_indexable=False to the constructor, and Auditlog will fall back to using a non-indexed text based field for this model. + + Using this field will not automatically register the model for automatic logging. This is done so you can be more + flexible with how you use this field. """ def __init__(self, pk_indexable=True, **kwargs): diff --git a/src/testapp/tests.py b/src/testapp/tests.py index 025bb8e..18338cf 100644 --- a/src/testapp/tests.py +++ b/src/testapp/tests.py @@ -1,5 +1,10 @@ import datetime -from django.test import TestCase +from django.contrib.auth.models import User, AnonymousUser +from django.core.exceptions import ValidationError +from django.db.models.signals import pre_save +from django.http import HttpResponse +from django.test import TestCase, RequestFactory +from auditlog.middleware import AuditlogMiddleware from auditlog.models import LogEntry from testapp.models import SimpleModel, AltPrimaryKeyModel, ProxyModel @@ -67,3 +72,64 @@ class AltPrimaryKeyModelTest(SimpleModelTest): class ProxyModelTest(SimpleModelTest): def setUp(self): self.obj = ProxyModel.objects.create(text='I am not what you think.') + + +class MiddlewareTest(TestCase): + """ + Test the middleware responsible for connecting and disconnecting the signals used in automatic logging. + """ + def setUp(self): + self.middleware = AuditlogMiddleware() + self.factory = RequestFactory() + self.user = User.objects.create_user(username='test', email='test@example.com', password='top_secret') + + def test_request_anonymous(self): # TODO does not seem to validate + """No actor will be logged when a user is not logged in.""" + # Create a request + request = self.factory.get('/') + request.user = AnonymousUser() + + # Run middleware + self.middleware.process_request(request) + + # Validate result + self.assertFalse(pre_save.has_listeners(LogEntry)) + + def test_request(self): + """The actor will be logged when a user is logged in.""" + # Create a request + request = self.factory.get('/') + request.user = self.user + # Run middleware + self.middleware.process_request(request) + + # Validate result + self.assertTrue(pre_save.has_listeners(LogEntry)) + + def test_response(self): # TODO does not seem to validate + """The signal will be disconnected when the request is processed.""" + # Create a request + request = self.factory.get('/') + request.user = self.user + + # Run middleware + self.middleware.process_request(request) + self.assertTrue(pre_save.has_listeners(LogEntry)) # The signal should be present before trying to disconnect it. + self.middleware.process_response(request, HttpResponse()) + + # Validate result + self.assertFalse(pre_save.has_listeners(LogEntry)) + + def test_exception(self): + """The signal will be disconnected when an exception is raised.""" + # Create a request + request = self.factory.get('/') + request.user = self.user + + # Run middleware + self.middleware.process_request(request) + self.assertTrue(pre_save.has_listeners(LogEntry)) # The signal should be present before trying to disconnect it. + self.middleware.process_exception(request, ValidationError("Test")) + + # Validate result + self.assertFalse(pre_save.has_listeners(LogEntry))