forked from rc/aircox
feat: add error message page; improve admin ui; add missing test files
This commit is contained in:
0
aircox/tests/admin/__init__.py
Normal file
0
aircox/tests/admin/__init__.py
Normal file
56
aircox/tests/controllers/test_diffusion_monitor.py
Normal file
56
aircox/tests/controllers/test_diffusion_monitor.py
Normal file
@ -0,0 +1,56 @@
|
||||
from datetime import date, datetime, timedelta, time
|
||||
from django.utils import timezone as tz
|
||||
|
||||
import pytest
|
||||
|
||||
from aircox import models
|
||||
from aircox.controllers import diffusion_monitor
|
||||
|
||||
now = date.today()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def monitor(logger):
|
||||
logger = logger._imeta.clone().inject(diffusion_monitor, "logger")
|
||||
yield diffusion_monitor.DiffusionMonitor(date=now)
|
||||
logger.release()
|
||||
|
||||
|
||||
class TestDiffusion:
|
||||
@pytest.mark.django_db
|
||||
def test_update(self, monitor, schedules, sched_initials, logger):
|
||||
monitor.update()
|
||||
|
||||
diffusions = models.Diffusion.objects.filter(
|
||||
schedule__in=sched_initials
|
||||
)
|
||||
by_date = {}
|
||||
for diff in diffusions:
|
||||
assert diff.episode_id
|
||||
by_date.setdefault(diff.schedule_id, set()).add(
|
||||
(diff.start, diff.end)
|
||||
)
|
||||
|
||||
for schedule in sched_initials:
|
||||
if schedule.pk not in by_date:
|
||||
continue
|
||||
_, items = schedule.diffusions_of_month(now)
|
||||
assert all(
|
||||
(item.start, item.end) in by_date[schedule.pk]
|
||||
for item in items
|
||||
)
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_clean(self, monitor, episode):
|
||||
start = tz.make_aware(
|
||||
datetime.combine(monitor.date - timedelta(days=1), time(10, 20))
|
||||
)
|
||||
diff = models.Diffusion(
|
||||
type=models.Diffusion.TYPE_UNCONFIRMED,
|
||||
episode=episode,
|
||||
start=start,
|
||||
end=start + timedelta(minutes=30),
|
||||
)
|
||||
diff.save()
|
||||
monitor.clean()
|
||||
assert not models.Diffusion.objects.filter(pk=diff.pk).first()
|
110
aircox/tests/models/test_signals.py
Normal file
110
aircox/tests/models/test_signals.py
Normal file
@ -0,0 +1,110 @@
|
||||
from datetime import timedelta
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.utils import timezone as tz
|
||||
|
||||
import pytest
|
||||
from model_bakery import baker
|
||||
|
||||
from aircox.conf import settings
|
||||
from aircox.models import signals, Diffusion, Episode
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sched(program, sched_initials):
|
||||
return next(r for r in sched_initials if r.program == program)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def eps_diffs(program, sched):
|
||||
eps = baker.make(Episode, program=program, _quantity=3)
|
||||
diffs = []
|
||||
for ep in eps:
|
||||
diffs += baker.make(
|
||||
Diffusion,
|
||||
start=tz.now() + timedelta(days=10),
|
||||
end=tz.now() + timedelta(days=10, hours=1),
|
||||
schedule=sched,
|
||||
episode=ep,
|
||||
_quantity=3,
|
||||
)
|
||||
return eps, diffs
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_user_default_groups():
|
||||
user = User(username="test")
|
||||
user.save()
|
||||
|
||||
default_groups = settings.DEFAULT_USER_GROUPS
|
||||
groups = Group.objects.filter(name__in=default_groups.keys())
|
||||
assert groups.exists()
|
||||
assert all(
|
||||
set(group.permissions.all().values_list("codename", flat=True))
|
||||
== set(default_groups[group.name])
|
||||
for group in groups
|
||||
)
|
||||
user_groups = set(user.groups.all().values_list("name", flat=True))
|
||||
assert set(default_groups.keys()) == user_groups
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_user_default_groups_skip_on_superuser():
|
||||
user = User(username="test", is_superuser=True)
|
||||
user.save()
|
||||
assert list(user.groups.all()) == []
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_page_post_save(program, episodes):
|
||||
episodes = [r for r in episodes if r.program == program]
|
||||
for episode in episodes:
|
||||
episode.cover = None
|
||||
Episode.objects.bulk_update(episodes, ["cover"])
|
||||
# TODO: cover must be an fk to Image
|
||||
# program.cover = "dummy/cover.png"
|
||||
# program.save()
|
||||
|
||||
# query = Episode.objects.filter(program=program) \
|
||||
# .values_list("cover", flat=True)
|
||||
# assert all(r == program.cover for r in query)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_program_post_save(program, eps_diffs):
|
||||
eps, diffs = eps_diffs
|
||||
program.active = False
|
||||
program.save()
|
||||
|
||||
eps_ids = [r.id for r in eps]
|
||||
diff_ids = [r.id for r in diffs]
|
||||
assert not Episode.objects.filter(id__in=eps_ids).exists()
|
||||
assert not Diffusion.objects.filter(id__in=diff_ids).exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_schedule_pre_save(sched_initials):
|
||||
sched = sched_initials[0]
|
||||
signals.schedule_pre_save(None, sched)
|
||||
assert getattr(sched, "_initial")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_schedule_post_save():
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_schedule_pre_delete(sched, eps_diffs):
|
||||
eps, diffs = eps_diffs
|
||||
signals.schedule_pre_delete(None, sched)
|
||||
assert not Episode.objects.filter(id__in=(r.id for r in eps)).exists()
|
||||
assert not Diffusion.objects.filter(id__in=(r.id for r in diffs)).exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_diffusion_post_delete(eps_diffs):
|
||||
eps = eps_diffs[0][0]
|
||||
Diffusion.objects.filter(
|
||||
id__in=[r.id for r in eps.diffusion_set.all()]
|
||||
).delete()
|
||||
assert Episode.objects.filter(id=eps.id).first() is None
|
48
aircox/tests/test_middleware.py
Normal file
48
aircox/tests/test_middleware.py
Normal file
@ -0,0 +1,48 @@
|
||||
import pytest
|
||||
from django.utils import timezone as tz
|
||||
|
||||
from django.conf import settings
|
||||
from aircox.middleware import AircoxMiddleware
|
||||
|
||||
from .conftest import req_factory
|
||||
|
||||
|
||||
settings.ALLOWED_HOSTS = list(settings.ALLOWED_HOSTS) + ["unknown-host"]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def middleware():
|
||||
return AircoxMiddleware(lambda r: r)
|
||||
|
||||
|
||||
class TestAircoxMiddleware:
|
||||
@pytest.mark.django_db
|
||||
def test_get_station(self, middleware, station, sub_station):
|
||||
req = req_factory.get("/tmp/test", headers={"host": sub_station.hosts})
|
||||
assert middleware.get_station(req) == sub_station
|
||||
|
||||
req = req_factory.get("/tmp/test", headers={"host": station.hosts})
|
||||
assert middleware.get_station(req) == station
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_station_use_default(self, middleware, station, stations):
|
||||
req = req_factory.get("/tmp/test", headers={"host": "unknown-host"})
|
||||
assert middleware.get_station(req) == station
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_init_timezone(self, middleware):
|
||||
req = req_factory.get("/tmp/test")
|
||||
req.session = {middleware.timezone_session_key: "Europe/Brussels"}
|
||||
middleware.init_timezone(req)
|
||||
|
||||
current_tz = tz.get_current_timezone()
|
||||
assert current_tz.key == "Europe/Brussels"
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_init_timezone_wrong_timezone(self, middleware):
|
||||
req = req_factory.get("/tmp/test")
|
||||
req.session = {middleware.timezone_session_key: "Oceania/Arlon"}
|
||||
middleware.init_timezone(req)
|
||||
|
||||
current_tz = tz.get_current_timezone()
|
||||
assert current_tz.key != "Oceania/Arlon"
|
45
aircox/tests/test_utils.py
Normal file
45
aircox/tests/test_utils.py
Normal file
@ -0,0 +1,45 @@
|
||||
from datetime import date, datetime, timedelta
|
||||
import pytest
|
||||
|
||||
from aircox import utils
|
||||
|
||||
|
||||
def test_redirect():
|
||||
with pytest.raises(utils.Redirect):
|
||||
utils.redirect("/redirect")
|
||||
|
||||
|
||||
def test_str_to_date():
|
||||
result = utils.str_to_date("2023-01-10", "-")
|
||||
assert result == date(2023, 1, 10)
|
||||
|
||||
|
||||
def test_cast_date():
|
||||
val = datetime(2023, 1, 12)
|
||||
result = utils.cast_date(val)
|
||||
assert isinstance(result, date)
|
||||
assert result == val.date()
|
||||
|
||||
|
||||
def test_date_or_default():
|
||||
result = utils.date_or_default(None, date)
|
||||
assert isinstance(result, date)
|
||||
assert result == date.today()
|
||||
|
||||
|
||||
def test_to_timedelta():
|
||||
val = datetime(2023, 1, 10, hour=20, minute=10, second=1)
|
||||
assert utils.to_timedelta(val) == timedelta(
|
||||
hours=20, minutes=10, seconds=1
|
||||
)
|
||||
|
||||
|
||||
def test_to_seconds():
|
||||
val = datetime(2023, 1, 10, hour=20, minute=10, second=1)
|
||||
assert utils.to_seconds(val) == 20 * 3600 + 10 * 60 + 1
|
||||
|
||||
|
||||
def test_seconds_to_time():
|
||||
val = 20 * 3600 + 10 * 60 + 1
|
||||
result = utils.seconds_to_time(val)
|
||||
assert (result.hour, result.minute, result.second) == (20, 10, 1)
|
0
aircox/tests/views/__init__.py
Normal file
0
aircox/tests/views/__init__.py
Normal file
40
aircox/tests/views/conftest.py
Normal file
40
aircox/tests/views/conftest.py
Normal file
@ -0,0 +1,40 @@
|
||||
import pytest
|
||||
from model_bakery import baker
|
||||
|
||||
from aircox import models
|
||||
|
||||
|
||||
class FakeView:
|
||||
context = None
|
||||
kwargs = {}
|
||||
|
||||
def ___init__(self):
|
||||
self.kwargs = {}
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def get_queryset(self):
|
||||
return self.queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return kwargs
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def published_pages():
|
||||
return baker.make(
|
||||
models.Page, status=models.StaticPage.STATUS_PUBLISHED, _quantity=3
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def unpublished_pages():
|
||||
return baker.make(
|
||||
models.Page, status=models.StaticPage.STATUS_DRAFT, _quantity=3
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pages(published_pages, unpublished_pages):
|
||||
return published_pages + unpublished_pages
|
76
aircox/tests/views/test_base.py
Normal file
76
aircox/tests/views/test_base.py
Normal file
@ -0,0 +1,76 @@
|
||||
import pytest
|
||||
|
||||
from django.urls import reverse
|
||||
|
||||
from aircox import models
|
||||
from aircox.test import Interface
|
||||
from aircox.views import base
|
||||
from .conftest import FakeView
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fake_request(station):
|
||||
return Interface(station=station)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def base_view(fake_request):
|
||||
class View(base.BaseView, FakeView):
|
||||
model = models.Page
|
||||
request = fake_request
|
||||
|
||||
return View()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def base_api_view(fake_request):
|
||||
class View(base.BaseAPIView, FakeView):
|
||||
model = models.Program
|
||||
queryset = models.Program.objects.all()
|
||||
request = fake_request
|
||||
|
||||
return View()
|
||||
|
||||
|
||||
class TestBaseView:
|
||||
@pytest.mark.django_db
|
||||
def test_station(self, base_view, station):
|
||||
assert base_view.station == station
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_sidebar_queryset(self, base_view, pages, published_pages):
|
||||
query = base_view.get_sidebar_queryset().values_list("id", flat=True)
|
||||
page_ids = {r.id for r in published_pages}
|
||||
assert set(query) == page_ids
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_sidebar_url(self, base_view):
|
||||
assert base_view.get_sidebar_url() == reverse("page-list")
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_context_data(self, base_view, station, published_pages):
|
||||
base_view.has_sidebar = True
|
||||
base_view.get_sidebar_queryset = lambda: published_pages
|
||||
context = base_view.get_context_data()
|
||||
assert context == {
|
||||
"view": base_view,
|
||||
"station": station,
|
||||
"page": None, # get_page() returns None
|
||||
"has_sidebar": base_view.has_sidebar,
|
||||
"has_filters": False,
|
||||
"sidebar_object_list": published_pages[: base_view.list_count],
|
||||
"sidebar_list_url": base_view.get_sidebar_url(),
|
||||
"audio_streams": station.streams,
|
||||
"model": base_view.model,
|
||||
}
|
||||
|
||||
|
||||
class TestBaseAPIView:
|
||||
@pytest.mark.django_db
|
||||
def test_station(self, base_api_view, station):
|
||||
assert base_api_view.station == station
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_queryset(self, base_api_view, station, programs):
|
||||
query = base_api_view.get_queryset()
|
||||
assert set(query.values_list("station", flat=True)) == {station.id}
|
166
aircox/tests/views/test_mixins.py
Normal file
166
aircox/tests/views/test_mixins.py
Normal file
@ -0,0 +1,166 @@
|
||||
from datetime import date
|
||||
|
||||
from django.http import Http404
|
||||
from model_bakery import baker
|
||||
import pytest
|
||||
|
||||
from aircox import filters, models
|
||||
from aircox.views import mixins
|
||||
from aircox.test import Interface
|
||||
from aircox.tests.conftest import req_factory
|
||||
from .conftest import FakeView
|
||||
|
||||
|
||||
today = date.today()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def redirect_interface():
|
||||
iface = Interface.inject(mixins, "redirect", {"__call__": "redirect"})
|
||||
yield iface
|
||||
iface._irelease()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def date_mixin():
|
||||
class Mixin(mixins.GetDateMixin, FakeView):
|
||||
pass
|
||||
|
||||
return Mixin()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def parent_mixin():
|
||||
class Mixin(mixins.ParentMixin, FakeView):
|
||||
parent_model = models.Program
|
||||
|
||||
return Mixin()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def attach_mixin():
|
||||
class Mixin(mixins.AttachedToMixin, FakeView):
|
||||
attach_to_value = models.StaticPage.ATTACH_TO_HOME
|
||||
|
||||
return Mixin()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def filters_mixin():
|
||||
class Mixin(mixins.FiltersMixin, FakeView):
|
||||
filterset_class = filters.PageFilters
|
||||
queryset = models.Page.objects.all()
|
||||
|
||||
return Mixin()
|
||||
|
||||
|
||||
class TestGetDateMixin:
|
||||
req = req_factory.get("/test", {"date": today.strftime("%Y-%m-%d")})
|
||||
|
||||
def test_get_date(self, date_mixin):
|
||||
date_mixin.request = self.req
|
||||
assert date_mixin.get_date() == today
|
||||
|
||||
def test_get_date_from_kwargs(self, date_mixin):
|
||||
date_mixin.request = req_factory.get("/test")
|
||||
date_mixin.kwargs = {"date": today}
|
||||
assert date_mixin.get_date() == today
|
||||
|
||||
def test_get_date_none_provided(self, date_mixin):
|
||||
date_mixin.request = req_factory.get("/test")
|
||||
assert date_mixin.get_date() is None
|
||||
|
||||
def test_get_redirect(self, date_mixin, redirect_interface):
|
||||
date_mixin.redirect_date_url = "redirect_date_url"
|
||||
date_mixin.request = self.req
|
||||
assert date_mixin.get() == "redirect"
|
||||
assert redirect_interface._trace() == (
|
||||
(date_mixin.redirect_date_url,),
|
||||
{"date": today.strftime("%Y/%m/%d")},
|
||||
)
|
||||
|
||||
def test_get_calls_get_date(self, date_mixin):
|
||||
date_mixin.get_date = lambda: today
|
||||
date_mixin.get()
|
||||
assert date_mixin.date == today
|
||||
|
||||
|
||||
class TestParentMixin:
|
||||
req = req_factory.get("/test")
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_parent(self, parent_mixin, program):
|
||||
parent = parent_mixin.get_parent(self.req, parent_slug=program.slug)
|
||||
assert parent.pk == program.pk
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_parent_raises_404(self, parent_mixin):
|
||||
with pytest.raises(Http404):
|
||||
parent_mixin.get_parent(
|
||||
self.req, parent_slug="parent-invalid-slug"
|
||||
)
|
||||
|
||||
def test_get_parent_not_parent_model(self, parent_mixin):
|
||||
parent_mixin.parent_model = None
|
||||
assert parent_mixin.get_parent(self.req) is None
|
||||
|
||||
def test_get_parent_not_parent_url_kwargs(self, parent_mixin):
|
||||
assert parent_mixin.get_parent(self.req) is None
|
||||
|
||||
def test_get_calls_parent(self, parent_mixin):
|
||||
parent = "parent object"
|
||||
parent_mixin.get_parent = lambda *_, **kw: parent
|
||||
parent_mixin.get(self.req)
|
||||
assert parent_mixin.parent == parent
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_queryset_with_parent(self, parent_mixin, program, episodes):
|
||||
parent_mixin.queryset = models.Episode.objects.all()
|
||||
parent_mixin.parent = program
|
||||
episodes_id = {r.id for r in episodes if r.parent_id == program.id}
|
||||
query = parent_mixin.get_queryset().values_list("id", flat=True)
|
||||
assert set(query) == episodes_id
|
||||
|
||||
def test_get_context_data_with_parent(self, parent_mixin):
|
||||
parent_mixin.parent = Interface(cover="parent-cover")
|
||||
context = parent_mixin.get_context_data()
|
||||
assert context["cover"] == "parent-cover"
|
||||
|
||||
|
||||
class TestAttachedToMixin:
|
||||
@pytest.mark.django_db
|
||||
def test_get_page_with_attach_to(self, attach_mixin):
|
||||
page = baker.make(
|
||||
models.StaticPage,
|
||||
attach_to=attach_mixin.attach_to_value,
|
||||
status=models.StaticPage.STATUS_PUBLISHED,
|
||||
)
|
||||
assert attach_mixin.get_page() == page
|
||||
|
||||
|
||||
class TestFiltersMixin:
|
||||
req = req_factory.get("/test", {"data": True, "page": "page"})
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_filterset(self, filters_mixin):
|
||||
filterset = filters_mixin.get_filterset({}, models.Page.objects.all())
|
||||
assert isinstance(filterset, filters_mixin.filterset_class)
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_queryset(self, filters_mixin):
|
||||
filterset = Interface(qs="filterset-qs")
|
||||
filters_mixin.request = self.req
|
||||
filters_mixin.get_filterset = lambda *_, **__: filterset
|
||||
assert filters_mixin.get_queryset() == filterset.qs
|
||||
|
||||
def test_get_context_data_valid_filterset(self, filters_mixin):
|
||||
filterset = Interface(
|
||||
None,
|
||||
{"is_valid": True},
|
||||
qs="filterset-qs",
|
||||
form=Interface(cleaned_data="cleaned_data"),
|
||||
)
|
||||
filters_mixin.request = self.req
|
||||
context = filters_mixin.get_context_data(filterset=filterset)
|
||||
assert context["filterset_data"] == "cleaned_data"
|
||||
assert dict(context["get_params"]) == {"data": ["True"]}
|
Reference in New Issue
Block a user