Skip to content

Commit

Permalink
Added tests to the base model
Browse files Browse the repository at this point in the history
  • Loading branch information
AlvaroLQueiroz committed Apr 17, 2024
1 parent bb41d39 commit b32eac9
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 65 deletions.
44 changes: 18 additions & 26 deletions notifications/models/base.py
Expand Up @@ -115,7 +115,7 @@ class Meta:
verbose_name = _("Notification")
verbose_name_plural = _("Notifications")

def __str__(self):
def __str__(self) -> str:
ctx = {
"actor": self.actor,
"verb": self.verb,
Expand Down Expand Up @@ -153,37 +153,29 @@ def mark_as_unread(self) -> None:
self.unread = True
self.save()

def actor_object_url(self) -> str:
def _build_url(self, field_name: str) -> str:
app_label = getattr(getattr(self, f"{field_name}_content_type"), "app_label")
model = getattr(getattr(self, f"{field_name}_content_type"), "model")
obj_id = getattr(self, f"{field_name}_object_id")
try:
url = reverse(
f"admin:{self.actor_content_type.app_label}_{self.actor_content_type.model}_change",
args=(self.actor_object_id,),
f"admin:{app_label}_{model}_change",
args=(obj_id,),
)
return format_html("<a href='{url}'>{id}</a>", url=url, id=self.actor_object_id)
return format_html("<a href='{url}'>{id}</a>", url=url, id=obj_id)
except NoReverseMatch:
return self.actor_object_id
return obj_id

def action_object_url(self) -> str:
try:
url = reverse(
f"admin:{self.action_object_content_type.app_label}_{self.action_object_content_type.model}_change",
args=(self.action_object_id,),
)
return format_html("<a href='{url}'>{id}</a>", url=url, id=self.action_object_object_id)
except NoReverseMatch:
return self.action_object_object_id
def actor_object_url(self) -> str:
return self._build_url("actor")

def target_object_url(self) -> str:
try:
url = reverse(
f"admin:{self.target_content_type.app_label}_{self.target_content_type.model}_change",
args=(self.target_object_id,),
)
return format_html("<a href='{url}'>{id}</a>", url=url, id=self.target_object_id)
except NoReverseMatch:
return self.target_object_id
def action_object_url(self) -> Union[str, None]:
return self._build_url("action_object")

def target_object_url(self) -> Union[str, None]:
return self._build_url("target")

def naturalday(self):
def naturalday(self) -> Union[str, None]:
"""
Shortcut for the ``humanize``.
Take a parameter humanize_type. This parameter control the which humanize method use.
Expand All @@ -192,5 +184,5 @@ def naturalday(self):

return naturalday(self.timestamp)

def naturaltime(self):
def naturaltime(self) -> str:
return naturaltime(self.timestamp)
51 changes: 38 additions & 13 deletions notifications/tests/factories/notifications.py
@@ -1,33 +1,58 @@
import factory
from django.contrib.contenttypes.models import ContentType
from swapper import load_model

from notifications.models import Notification
from notifications.tests.factories.users import Actor, Recipient, Target
from notifications.tests.factories.users import (
ActorFactory,
RecipientFactory,
TargetFactory,
)

VERB_LIST_SHORT = ("reached level 60", "joined to site")

VERB_LIST_WITH_TARGET = (
"commented on",
"started follow",
"liked",
)

VERB_LIST = (
"commented",
VERB_LIST_FULL = (
"closed",
"opened",
"liked",
"deleted",
)

Notification = load_model("notifications", "Notification")


class NotificationFactory(factory.django.DjangoModelFactory):
recipient = factory.SubFactory(Recipient)
class NotificationShortFactory(factory.django.DjangoModelFactory):
recipient = factory.SubFactory(RecipientFactory)

actor = factory.SubFactory(Actor)
actor = factory.SubFactory(ActorFactory)
actor_object_id = factory.SelfAttribute("actor.id")
actor_content_type = factory.LazyAttribute(lambda obj: ContentType.objects.get_for_model(obj.actor))

verb = factory.Iterator(VERB_LIST)
verb = factory.Iterator(VERB_LIST_SHORT)
description = factory.Faker("catch_phrase")

target = factory.SubFactory(Target)
class Meta:
model = Notification


class NotificationWithTargetFactory(NotificationShortFactory):
verb = factory.Iterator(VERB_LIST_WITH_TARGET)

target = factory.SubFactory(TargetFactory)
target_object_id = factory.SelfAttribute("target.id")
target_content_type = factory.LazyAttribute(lambda obj: ContentType.objects.get_for_model(obj.target))

action_object = factory.SubFactory(Target)

class NotificationWithActionObjectFactory(NotificationShortFactory):
verb = factory.Iterator(VERB_LIST_WITH_TARGET)
action_object = factory.SubFactory(TargetFactory)
action_object_object_id = factory.SelfAttribute("action_object.id")
action_object_content_type = factory.LazyAttribute(lambda obj: ContentType.objects.get_for_model(obj.action_object))

class Meta:
model = Notification

class NotificationFullFactory(NotificationWithTargetFactory, NotificationWithActionObjectFactory):
verb = factory.Iterator(VERB_LIST_FULL)
6 changes: 3 additions & 3 deletions notifications/tests/factories/users.py
Expand Up @@ -2,23 +2,23 @@
from django.conf import settings


class Recipient(factory.django.DjangoModelFactory):
class RecipientFactory(factory.django.DjangoModelFactory):
username = factory.Sequence(lambda n: f"recipient-{n}")
first_name = factory.SelfAttribute("username")

class Meta:
model = settings.AUTH_USER_MODEL


class Actor(factory.django.DjangoModelFactory):
class ActorFactory(factory.django.DjangoModelFactory):
username = factory.Sequence(lambda n: f"actor-{n}")
first_name = factory.SelfAttribute("username")

class Meta:
model = settings.AUTH_USER_MODEL


class Target(factory.django.DjangoModelFactory):
class TargetFactory(factory.django.DjangoModelFactory):
username = factory.Sequence(lambda n: f"target-{n}")
first_name = factory.SelfAttribute("username")

Expand Down
6 changes: 3 additions & 3 deletions notifications/tests/test_migrations/test_level_migration.py
Expand Up @@ -12,9 +12,9 @@ def test_main_migration0002(migrator):
OldNotification = old_state.apps.get_model("notifications", "Notification") # pylint: disable=invalid-name
OldContentType = old_state.apps.get_model("contenttypes", "ContentType") # pylint: disable=invalid-name

mark_follower = factory.create(OldUser, FACTORY_CLASS=user_factory.Recipient)
guido = factory.create(OldUser, FACTORY_CLASS=user_factory.Target)
mark = factory.create(OldUser, FACTORY_CLASS=user_factory.Actor)
mark_follower = factory.create(OldUser, FACTORY_CLASS=user_factory.RecipientFactory)
guido = factory.create(OldUser, FACTORY_CLASS=user_factory.TargetFactory)
mark = factory.create(OldUser, FACTORY_CLASS=user_factory.ActorFactory)

user_type = OldContentType.objects.get_for_model(mark)
notification_base = {
Expand Down
118 changes: 118 additions & 0 deletions notifications/tests/test_models.py
@@ -0,0 +1,118 @@
from datetime import datetime, timedelta
from unittest.mock import patch

import pytest
from django.urls import NoReverseMatch
from freezegun import freeze_time
from swapper import load_model

from notifications.tests.factories.notifications import (
NotificationFullFactory,
NotificationShortFactory,
NotificationWithActionObjectFactory,
NotificationWithTargetFactory,
)

Notification = load_model("notifications", "Notification")


@pytest.mark.django_db
def test__str__():
notification = NotificationShortFactory()

notification_str = str(notification)
assert str(notification.actor) in notification_str
assert str(notification.verb) in notification_str
assert str(notification.action_object) not in notification_str
assert str(notification.target) not in notification_str

notification = NotificationWithTargetFactory()
notification_str = str(notification)
assert str(notification.actor) in notification_str
assert str(notification.verb) in notification_str
assert str(notification.target) in notification_str
assert str(notification.action_object) not in notification_str

notification = NotificationWithActionObjectFactory()
notification_str = str(notification)
assert str(notification.actor) in notification_str
assert str(notification.verb) in notification_str
assert str(notification.action_object) in notification_str
assert str(notification.target) not in notification_str

notification = NotificationFullFactory()
notification_str = str(notification)
assert str(notification.actor) in notification_str
assert str(notification.verb) in notification_str
assert str(notification.target) in notification_str
assert str(notification.action_object) in notification_str


@pytest.mark.parametrize(
"increase,expected_result",
(
({"minutes": 10}, "10\xa0minutes"),
({"days": 2}, "2\xa0days"),
),
)
@pytest.mark.django_db
def test_timesince(increase, expected_result):
initial_date = datetime(2023, 1, 1, 0, 0, 0)
with freeze_time(initial_date):
notification = NotificationShortFactory()
with freeze_time(initial_date + timedelta(**increase)):
assert notification.timesince() == expected_result


def test_slug():
notification = NotificationShortFactory()
assert notification.id == notification.slug


@pytest.mark.parametrize(
"before,method",
(
(True, "mark_as_read"),
(False, "mark_as_unread"),
),
)
@pytest.mark.django_db
def test_mark_as_read_unread(before, method):
notification = NotificationShortFactory(unread=before)

assert Notification.objects.filter(unread=before).count() == 1
func = getattr(notification, method)
func()
assert Notification.objects.filter(unread=before).count() == 0
assert Notification.objects.filter(unread=not before).count() == 1


@pytest.mark.django_db
def test_build_url():
notification = NotificationShortFactory()

url = notification.actor_object_url()

assert "<a href=" in url
assert str(notification.actor.id) in url

with patch("notifications.models.base.reverse") as mock:
mock.side_effect = NoReverseMatch
url = notification.actor_object_url()
assert notification.actor.id == url


@pytest.mark.parametrize(
"increase,expected_result",
(
({"seconds": 3}, "30\xa0seconds\xa0ago"),
({"days": 1}, "yesterday"),
),
)
@pytest.mark.django_db
def test_natural_day(increase, expected_result):
initial_date = datetime(2023, 1, 1, 0, 0, 0)
with freeze_time(initial_date):
notification = NotificationShortFactory()
with freeze_time(initial_date + timedelta(**increase)):
assert notification.naturalday() == expected_result

0 comments on commit b32eac9

Please sign in to comment.