Skip to content

Commit

Permalink
Refactor tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hugorodgerbrown committed Sep 18, 2023
1 parent 540345b commit 7f1fedb
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 56 deletions.
9 changes: 3 additions & 6 deletions anonymiser/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,8 @@ class BaseAnonymiser:
# Override with the model to be anonymised
model: type[models.Model]

# override with a list of fields to exclude from anonymisation report
exclude_rules = (lambda f: f.is_relation or isinstance(f, models.AutoField),)

# field_name: redaction_value. redaction_value can be a static value or a
# callable, such as a function (e.g. F expression) or a class (e.g. Func).
# field_name: redaction_value. redaction_value can be a static value
# or a db function, e.g. F("field_name") or Value("static value").
field_redactions: dict[str, Any] = {}

def __setattr__(self, __name: str, __value: Any) -> None:
Expand Down Expand Up @@ -189,7 +186,7 @@ def _max_length(f: models.Field) -> int:
def redact_queryset(
self,
queryset: models.QuerySet[models.Model],
auto_redact: bool = True,
auto_redact: bool = False,
**field_overrides: Any,
) -> int:
"""
Expand Down
24 changes: 11 additions & 13 deletions tests/anon.py → tests/anonymisers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from django.db import models
from django.db.models import F, Value
from django.db.models.functions import Concat

from anonymiser.db.expressions import GenerateUuid4
from anonymiser.decorators import register_anonymiser
from anonymiser.models import BaseAnonymiser

Expand All @@ -11,17 +10,6 @@
@register_anonymiser
class UserAnonymiser(BaseAnonymiser):
model = User
field_redactions = {
"first_name": "FIRST_NAME",
"last_name": "LAST_NAME",
"uuid": GenerateUuid4(),
"email": Concat(
models.F("first_name"),
models.Value("."),
models.F("last_name"),
models.Value("@example.com"),
),
}

def anonymise_first_name(self, obj: User) -> None:
obj.first_name = "Anonymous"
Expand All @@ -33,3 +21,13 @@ class BadUserAnonymiser(BaseAnonymiser):
def anonymise_first_name(self, obj: User) -> None:
# this is not allowed - should be obj.first_name.
self.first_name = "Anonymous"


class UserRedacter(BaseAnonymiser):
model = User

field_redactions = {
"first_name": "FIRST_NAME",
"last_name": "LAST_NAME",
"email": Concat(Value("user_"), F("id"), Value("@example.com")),
}
2 changes: 1 addition & 1 deletion tests/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ class TestsConfig(AppConfig):
def ready(self) -> None:
super().ready()
logger.debug("Adding tests app anonymisers")
from . import anon # noqa F401
from . import anonymisers # noqa F401
7 changes: 6 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest
from django.conf import settings

from tests.anon import UserAnonymiser
from tests.anonymisers import UserAnonymiser, UserRedacter
from tests.models import User

IS_POSTGRES = (
Expand Down Expand Up @@ -35,3 +35,8 @@ def user2() -> User:
@pytest.fixture
def user_anonymiser() -> UserAnonymiser:
return UserAnonymiser()


@pytest.fixture
def user_redacter() -> UserRedacter:
return UserRedacter()
64 changes: 32 additions & 32 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from anonymiser.db.expressions import GenerateUuid4
from anonymiser.models import FieldSummaryData

from .anon import BadUserAnonymiser, UserAnonymiser
from .anonymisers import BadUserAnonymiser, UserAnonymiser, UserRedacter
from .models import User


Expand Down Expand Up @@ -125,55 +125,46 @@ def test_bad_anonymiser() -> None:


@pytest.mark.django_db
@mock.patch.object(CursorWrapper, "execute")
@skipUnless(settings.IS_POSTGRES, "Test requires Postgres.")
def test_generate_uuid4(mock_execute: mock.MagicMock) -> None:
User.objects.update(uuid=GenerateUuid4())
assert (
mock_execute.call_args[0][0]
== 'UPDATE "tests_user" SET "uuid" = uuid_generate_v4()'
)


@pytest.mark.django_db
class TestPostgresRedaction:
class TestRedaction:
@pytest.fixture(autouse=settings.IS_POSTGRES)
def activate_postgresql_uuid(self) -> None:
"""Activate the uuid-ossp extension in the test database."""
with connection.cursor() as cursor:
cursor.execute('CREATE EXTENSION IF NOT EXISTS "uuid-ossp";')

@skipUnless(settings.IS_POSTGRES, "Test requires Postgres.")
@mock.patch.object(CursorWrapper, "execute")
def test_generate_uuid4(self, mock_execute: mock.MagicMock) -> None:
User.objects.update(uuid=GenerateUuid4())
assert (
mock_execute.call_args[0][0]
== 'UPDATE "tests_user" SET "uuid" = uuid_generate_v4()'
)

def test_redact_queryset_none(
self, user: User, user_anonymiser: UserAnonymiser
self, user: User, user_redacter: UserRedacter
) -> None:
assert user_anonymiser.redact_queryset(User.objects.none()) == 0
assert user_redacter.redact_queryset(User.objects.none()) == 0

@skipUnless(settings.IS_POSTGRES, "Test requires Postgres.")
def test_redact_queryset_one(
self, user: User, user_anonymiser: UserAnonymiser
) -> None:
uuid = user.uuid
assert user_anonymiser.redact_queryset(User.objects.all()) == 1
def test_redact_queryset_one(self, user: User, user_redacter: UserRedacter) -> None:
assert user_redacter.redact_queryset(User.objects.all()) == 1
user.refresh_from_db()
assert user.first_name == "FIRST_NAME"
assert user.last_name == "LAST_NAME"
assert user.uuid != uuid
assert user.email == f"user_{user.id}@example.com"

@skipUnless(settings.IS_POSTGRES, "Test requires Postgres.")
def test_redact_queryset_two(
self,
user: User,
user2: User,
user_anonymiser: UserAnonymiser,
user_redacter: UserRedacter,
) -> None:
assert user_anonymiser.redact_queryset(User.objects.all()) == 2
assert user_redacter.redact_queryset(User.objects.all()) == 2
user.refresh_from_db()
user2.refresh_from_db()
# confirm that we haven't reused the same uuid for all objects
assert user.uuid != user2.uuid

@skipUnless(settings.IS_POSTGRES, "Test requires Postgres.")
@pytest.mark.parametrize(
"auto_redact,location,biography",
[
Expand All @@ -184,24 +175,33 @@ def test_redact_queryset_two(
def test_redact_queryset__auto_redact(
self,
user: User,
user_anonymiser: UserAnonymiser,
user_redacter: UserRedacter,
auto_redact: bool,
location: str,
biography: str,
) -> None:
user_anonymiser.redact_queryset(User.objects.all(), auto_redact=auto_redact)
user_redacter.redact_queryset(User.objects.all(), auto_redact=auto_redact)
user.refresh_from_db()
# auto-redacted fields
assert user.location == location
assert user.biography == biography

@skipUnless(settings.IS_POSTGRES, "Test requires Postgres.")
def test_redact_queryset__field_overrides(
self,
user: User,
user_anonymiser: UserAnonymiser,
user_redacter: UserRedacter,
) -> None:
user_anonymiser.redact_queryset(User.objects.all(), location="Area 51")
user_redacter.redact_queryset(User.objects.all(), location="Area 51")
user.refresh_from_db()
# auto-redacted fields
assert user.location == "Area 51"

@skipUnless(settings.IS_POSTGRES, "Test requires Postgres.")
def test_redact_queryset__field_overrides__postgres(
self,
user: User,
user_redacter: UserRedacter,
) -> None:
uuid = user.uuid
user_redacter.redact_queryset(User.objects.all(), uuid=GenerateUuid4())
user.refresh_from_db()
assert user.uuid != uuid
4 changes: 1 addition & 3 deletions tests/test_registry.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
from anonymiser.decorators import register_anonymiser
from anonymiser.registry import _registry, anonymisable_models

from .anon import UserAnonymiser
from .anonymisers import UserAnonymiser
from .models import User


def test_registry() -> None:
# assert anonymisable_models() == []
# register(UserAnonymiser)
assert anonymisable_models() == [User]


Expand Down

0 comments on commit 7f1fedb

Please sign in to comment.