Added tests to the base model

This commit is contained in:
Alvaro Leonel 2024-04-14 12:40:54 -03:00
parent bb41d39a63
commit 5de134e355
8 changed files with 227 additions and 74 deletions

View file

@ -115,7 +115,7 @@ class AbstractNotification(models.Model):
verbose_name = _("Notification")
verbose_name_plural = _("Notifications")
def __str__(self):
def __str__(self) -> str:
ctx = {
"actor": self.actor,
"verb": self.verb,
@ -153,37 +153,29 @@ class AbstractNotification(models.Model):
self.unread = True
self.save()
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:{app_label}_{model}_change",
args=(obj_id,),
)
return format_html("<a href='{url}'>{id}</a>", url=url, id=obj_id)
except NoReverseMatch:
return obj_id
def actor_object_url(self) -> str:
try:
url = reverse(
f"admin:{self.actor_content_type.app_label}_{self.actor_content_type.model}_change",
args=(self.actor_object_id,),
)
return format_html("<a href='{url}'>{id}</a>", url=url, id=self.actor_object_id)
except NoReverseMatch:
return self.actor_object_id
return self._build_url("actor")
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 action_object_url(self) -> Union[str, None]:
return self._build_url("action_object")
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 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.
@ -192,5 +184,5 @@ class AbstractNotification(models.Model):
return naturalday(self.timestamp)
def naturaltime(self):
def naturaltime(self) -> str:
return naturaltime(self.timestamp)

View file

@ -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
VERB_LIST = (
"commented",
"liked",
"deleted",
from notifications.tests.factories.users import (
ActorFactory,
RecipientFactory,
TargetFactory,
)
VERB_LIST_SHORT = ("reached level 60", "joined to site")
class NotificationFactory(factory.django.DjangoModelFactory):
recipient = factory.SubFactory(Recipient)
VERB_LIST_WITH_TARGET = (
"commented on",
"started follow",
"liked",
)
actor = factory.SubFactory(Actor)
VERB_LIST_FULL = (
"closed",
"opened",
"liked",
)
Notification = load_model("notifications", "Notification")
class NotificationShortFactory(factory.django.DjangoModelFactory):
recipient = factory.SubFactory(RecipientFactory)
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)
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)
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 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))
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 NotificationFullFactory(NotificationWithTargetFactory, NotificationWithActionObjectFactory):
verb = factory.Iterator(VERB_LIST_FULL)

View file

@ -2,7 +2,7 @@ import factory
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")
@ -10,7 +10,7 @@ class Recipient(factory.django.DjangoModelFactory):
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")
@ -18,7 +18,7 @@ class Actor(factory.django.DjangoModelFactory):
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")

View file

@ -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 = {

View file

@ -0,0 +1,119 @@
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
@pytest.mark.django_db
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",
(
({"minutes": 10}, "today"),
({"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

View file

@ -4,8 +4,8 @@ from django.core.exceptions import ImproperlyConfigured
from django.test import override_settings
from swapper import load_model
from notifications.tests.factories.notifications import NotificationFactory
from notifications.tests.factories.users import Recipient
from notifications.tests.factories.notifications import NotificationFullFactory
from notifications.tests.factories.users import RecipientFactory
Notification = load_model("notifications", "Notification")
User = get_user_model()
@ -20,7 +20,7 @@ User = get_user_model()
)
@pytest.mark.django_db
def test_sent_unsent_methods(emailed, method):
NotificationFactory.create_batch(3, emailed=emailed)
NotificationFullFactory.create_batch(3, emailed=emailed)
assert method().count() == 3
first_notification = Notification.objects.first()
@ -45,7 +45,7 @@ def test_sent_unsent_methods(emailed, method):
)
@pytest.mark.django_db
def test_read_unread_methods(read, method):
NotificationFactory.create_batch(3, unread=read)
NotificationFullFactory.create_batch(3, unread=read)
assert method().count() == 3
first_notification = Notification.objects.first()
@ -71,7 +71,7 @@ def test_read_unread_methods(read, method):
@override_settings(DJANGO_NOTIFICATIONS_CONFIG={"SOFT_DELETE": True})
@pytest.mark.django_db
def test_read_unread_with_deleted_notifications(read, method):
NotificationFactory.create_batch(3, unread=read)
NotificationFullFactory.create_batch(3, unread=read)
assert method().count() == 3
first_notification = Notification.objects.first()
@ -91,7 +91,7 @@ def test_read_unread_with_deleted_notifications(read, method):
)
@pytest.mark.django_db
def test_mark_all_as_read_unread(status, method, check_method):
NotificationFactory.create_batch(3, unread=status)
NotificationFullFactory.create_batch(3, unread=status)
assert check_method().count() == 0
method()
@ -107,9 +107,9 @@ def test_mark_all_as_read_unread(status, method, check_method):
)
@pytest.mark.django_db
def test_mark_all_as_read_unread_with_recipient(status, method, check_method):
recipient = Recipient()
NotificationFactory.create_batch(2, unread=status, recipient=recipient)
NotificationFactory.create_batch(1, unread=status)
recipient = RecipientFactory()
NotificationFullFactory.create_batch(2, unread=status, recipient=recipient)
NotificationFullFactory.create_batch(1, unread=status)
assert Notification.objects.count() == 3
assert check_method().count() == 0
@ -127,7 +127,7 @@ def test_mark_all_as_read_unread_with_recipient(status, method, check_method):
)
@pytest.mark.django_db
def test_deleted_active_methods(deleted, method):
NotificationFactory.create_batch(3, deleted=deleted)
NotificationFullFactory.create_batch(3, deleted=deleted)
assert method().count() == 3
first_notification = Notification.objects.first()
@ -166,7 +166,7 @@ def test_deleted_active_methods_without_soft_delete(method):
)
@pytest.mark.django_db
def test_mark_all_as_deleted_active(status, method, check_method):
NotificationFactory.create_batch(3, deleted=status)
NotificationFullFactory.create_batch(3, deleted=status)
assert Notification.objects.count() == 3
assert check_method().count() == 0
@ -184,9 +184,9 @@ def test_mark_all_as_deleted_active(status, method, check_method):
)
@pytest.mark.django_db
def test_mark_all_as_deleted_active_with_recipient(status, method, check_method):
recipient = Recipient()
NotificationFactory.create_batch(2, deleted=status, recipient=recipient)
NotificationFactory.create_batch(1, deleted=status)
recipient = RecipientFactory()
NotificationFullFactory.create_batch(2, deleted=status, recipient=recipient)
NotificationFullFactory.create_batch(1, deleted=status)
assert Notification.objects.count() == 3
assert check_method().count() == 0
@ -216,7 +216,7 @@ def test_mark_all_as_deleted_active_without_soft_delete(method):
)
@pytest.mark.django_db
def test_mark_as_sent_unsent_method(status, method, check_method):
NotificationFactory.create_batch(3, emailed=status)
NotificationFullFactory.create_batch(3, emailed=status)
assert Notification.objects.count() == 3
assert check_method().count() == 0
@ -233,9 +233,9 @@ def test_mark_as_sent_unsent_method(status, method, check_method):
)
@pytest.mark.django_db
def test_mark_as_sent_unsent_with_recipient(status, method, check_method):
recipient = Recipient()
NotificationFactory.create_batch(2, emailed=status, recipient=recipient)
NotificationFactory.create_batch(1, emailed=status)
recipient = RecipientFactory()
NotificationFullFactory.create_batch(2, emailed=status, recipient=recipient)
NotificationFullFactory.create_batch(1, emailed=status)
assert Notification.objects.count() == 3
assert check_method().count() == 0

18
poetry.lock generated
View file

@ -390,6 +390,20 @@ docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1
testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"]
typing = ["typing-extensions (>=4.8)"]
[[package]]
name = "freezegun"
version = "1.4.0"
description = "Let your Python tests travel through time"
optional = true
python-versions = ">=3.7"
files = [
{file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"},
{file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"},
]
[package.dependencies]
python-dateutil = ">=2.7"
[[package]]
name = "identify"
version = "2.5.35"
@ -1215,9 +1229,9 @@ files = [
[extras]
dev = ["django-debug-toolbar", "pre-commit", "psycopg2-binary"]
lint = ["bandit", "black", "isort", "mypy", "pylint", "pylint-django"]
test = ["coverage", "django-test-migrations", "factory-boy", "pytest", "pytest-cov", "pytest-django", "pytest-xdist"]
test = ["coverage", "django-test-migrations", "factory-boy", "freezegun", "pytest", "pytest-cov", "pytest-django", "pytest-xdist"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "c05e64d20b9253c2d60188553b6a88be71d8a40ccd9caff588ae06bfd42931fc"
content-hash = "7934a50f2a5ec5aa8f522e683839042fa165ec26367ab4767f194c1455f313bd"

View file

@ -101,6 +101,7 @@ pytest = {version = "^7", optional = true }
pytest-cov = {version = "^4", optional = true }
pytest-django = {version = "^4", optional = true }
pytest-xdist = {version = "^3.3.1", optional = true }
freezegun = {version = "^1.4.0", optional = true}
[tool.poetry.extras]
dev = [
@ -121,6 +122,7 @@ test = [
"django-test",
"django-test-migrations",
"factory-boy",
"freezegun",
"pytest",
"pytest-cov",
"pytest-django",
@ -170,6 +172,7 @@ max-line-length = 120
ignored-modules = [
"pytest",
"factory",
"freezegun"
]
[tool.black]