forked from rc/aircox
cfr #121 Co-authored-by: Christophe Siraut <d@tobald.eu.org> Co-authored-by: bkfox <thomas bkfox net> Co-authored-by: Thomas Kairos <thomas@bkfox.net> Reviewed-on: rc/aircox#131 Co-authored-by: Chris Tactic <ctactic@noreply.git.radiocampus.be> Co-committed-by: Chris Tactic <ctactic@noreply.git.radiocampus.be>
This commit is contained in:
46
aircox/tests/_test_permissions.py
Normal file
46
aircox/tests/_test_permissions.py
Normal file
@ -0,0 +1,46 @@
|
||||
import pytest
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
@pytest.mark.django_db()
|
||||
def test_no_admin(user, client):
|
||||
client.force_login(user)
|
||||
response = client.get("/admin/")
|
||||
assert response.status_code != 200
|
||||
|
||||
|
||||
@pytest.mark.django_db()
|
||||
def test_user_cannot_change_program_or_episode(user, client, program):
|
||||
assert not user.has_perm("aircox.change_program")
|
||||
assert not user.has_perm("aircox.change_episode")
|
||||
|
||||
|
||||
@pytest.mark.django_db()
|
||||
def test_group_can_change_program(user, client, program):
|
||||
assert program.editors in Group.objects.all()
|
||||
assert not user.has_perm("aircox.%s" % program.change_permission_codename)
|
||||
user.groups.add(program.editors)
|
||||
user = User.objects.get(pk=user.pk) # reload user in order to have permissions set
|
||||
assert program.editors in user.groups.all()
|
||||
assert user.has_perm("aircox.%s" % program.change_permission_codename)
|
||||
|
||||
|
||||
@pytest.mark.django_db()
|
||||
def test_group_change_program(user, client, program):
|
||||
client.force_login(user)
|
||||
response = client.get(reverse("program-edit", kwargs={"pk": program.pk}))
|
||||
assert response.status_code == 403
|
||||
user.groups.add(program.editors)
|
||||
response = client.get(reverse("program-edit", kwargs={"pk": program.pk}))
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.django_db()
|
||||
def test_group_change_episode(user, client, program, episode):
|
||||
client.force_login(user)
|
||||
response = client.get(reverse("episode-edit", kwargs={"pk": episode.pk}))
|
||||
assert response.status_code == 403
|
||||
user.groups.add(program.editors)
|
||||
response = client.get(reverse("episode-edit", kwargs={"pk": episode.pk}))
|
||||
assert response.status_code == 200
|
@ -1,6 +1,7 @@
|
||||
from datetime import time, timedelta
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
@ -130,25 +131,32 @@ def episode(episodes):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def podcasts(episodes):
|
||||
items = []
|
||||
for episode in episodes:
|
||||
sounds = baker.prepare(
|
||||
models.Sound,
|
||||
episode=episode,
|
||||
program=episode.program,
|
||||
is_public=True,
|
||||
_quantity=2,
|
||||
)
|
||||
for i, sound in enumerate(sounds):
|
||||
sound.file = f"test_sound_{episode.pk}_{i}.mp3"
|
||||
items += sounds
|
||||
return items
|
||||
def sound(program):
|
||||
return baker.make(models.Sound, file="tmp/test.wav", program=program)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sound(program):
|
||||
return baker.make(models.Sound, file="tmp/test.wav", program=program)
|
||||
def sounds(program):
|
||||
objs = [
|
||||
models.Sound(program=program, file=f"tmp/test-{i}.wav", broadcast=(i == 0), is_downloadable=(i == 1))
|
||||
for i in range(0, 3)
|
||||
]
|
||||
models.Sound.objects.bulk_create(objs)
|
||||
return objs
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def podcasts(episode, sounds):
|
||||
objs = [
|
||||
models.EpisodeSound(
|
||||
episode=episode,
|
||||
sound=sound,
|
||||
broadcast=True,
|
||||
)
|
||||
for sound in sounds
|
||||
]
|
||||
models.EpisodeSound.objects.bulk_create(objs)
|
||||
return objs
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -157,3 +165,15 @@ def tracks(episode, sound):
|
||||
items += [baker.prepare(models.Track, sound=sound, position=i, timestamp=i * 60) for i in range(0, 3)]
|
||||
models.Track.objects.bulk_create(items)
|
||||
return items
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def user():
|
||||
return User.objects.create_user(username="user1", password="bar")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def png_content():
|
||||
image_file = "{}/image.png".format(os.path.dirname(__file__))
|
||||
with open(image_file, "rb") as fh:
|
||||
return fh.read()
|
||||
|
@ -1,14 +1,12 @@
|
||||
import pytest
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from django.conf import settings as conf
|
||||
from django.utils import timezone as tz
|
||||
|
||||
from aircox import models
|
||||
from aircox.controllers.sound_file import SoundFile
|
||||
|
||||
|
||||
# FIXME: use from tests.models.sound
|
||||
@pytest.fixture
|
||||
def path_infos():
|
||||
return {
|
||||
@ -27,6 +25,7 @@ def path_infos():
|
||||
"day": 2,
|
||||
"hour": 10,
|
||||
"minute": 13,
|
||||
"n": None,
|
||||
"name": "Sample 2",
|
||||
},
|
||||
"test/20220103_1_sample_3.mp3": {
|
||||
@ -56,42 +55,25 @@ def sound_files(path_infos):
|
||||
return {k: r for k, r in ((path, SoundFile(conf.MEDIA_ROOT + "/" + path)) for path in path_infos.keys())}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sound_file(sound_files):
|
||||
return next(sound_files.items())
|
||||
|
||||
|
||||
def test_sound_path(sound_files):
|
||||
for path, sound_file in sound_files.items():
|
||||
assert path == sound_file.sound_path
|
||||
|
||||
|
||||
def test_read_path(path_infos, sound_files):
|
||||
for path, sound_file in sound_files.items():
|
||||
expected = path_infos[path]
|
||||
result = sound_file.read_path(path)
|
||||
# remove None values
|
||||
result = {k: v for k, v in result.items() if v is not None}
|
||||
assert expected == result, "path: {}".format(path)
|
||||
class TestSoundFile:
|
||||
def sound_path(self, sound_file):
|
||||
assert sound_file[0] == sound_file[1].sound_path
|
||||
|
||||
def sync(self):
|
||||
raise NotImplementedError("test is not implemented")
|
||||
|
||||
def _setup_diff(program, info):
|
||||
episode = models.Episode(program=program, title="test-episode")
|
||||
at = tz.datetime(**{k: info[k] for k in ("year", "month", "day", "hour", "minute") if info.get(k)})
|
||||
at = tz.make_aware(at)
|
||||
diff = models.Diffusion(episode=episode, start=at, end=at + timedelta(hours=1))
|
||||
episode.save()
|
||||
diff.save()
|
||||
return diff
|
||||
def create_episode_sound(self):
|
||||
raise NotImplementedError("test is not implemented")
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
def test_find_episode(sound_files):
|
||||
station = models.Station(name="test-station")
|
||||
program = models.Program(station=station, title="test")
|
||||
station.save()
|
||||
program.save()
|
||||
|
||||
for path, sound_file in sound_files.items():
|
||||
infos = sound_file.read_path(path)
|
||||
diff = _setup_diff(program, infos)
|
||||
sound = models.Sound(program=diff.program, file=path)
|
||||
result = sound_file.find_episode(sound, infos)
|
||||
assert diff.episode == result
|
||||
|
||||
# TODO: find_playlist, sync
|
||||
def _on_delete(self):
|
||||
raise NotImplementedError("test is not implemented")
|
||||
|
@ -223,22 +223,19 @@ class TestSoundMonitor:
|
||||
[
|
||||
(("scan all programs...",), {}),
|
||||
]
|
||||
+ [
|
||||
((f"#{program.id} {program.title}",), {})
|
||||
for program in programs
|
||||
]
|
||||
+ [((f"#{program.id} {program.title}",), {}) for program in programs]
|
||||
)
|
||||
assert dirs == [program.abspath for program in programs]
|
||||
traces = tuple(
|
||||
[
|
||||
[
|
||||
(
|
||||
(program, settings.SOUND_ARCHIVES_SUBDIR),
|
||||
{"logger": logger, "type": Sound.TYPE_ARCHIVE},
|
||||
(program, settings.SOUND_BROADCASTS_SUBDIR),
|
||||
{"logger": logger, "broadcast": True},
|
||||
),
|
||||
(
|
||||
(program, settings.SOUND_EXCERPTS_SUBDIR),
|
||||
{"logger": logger, "type": Sound.TYPE_EXCERPT},
|
||||
{"logger": logger, "broadcast": False},
|
||||
),
|
||||
]
|
||||
for program in programs
|
||||
@ -247,6 +244,7 @@ class TestSoundMonitor:
|
||||
traces_flat = tuple([item for sublist in traces for item in sublist])
|
||||
assert interface._traces("scan_for_program") == traces_flat
|
||||
|
||||
# TODO / FIXME
|
||||
def broken_test_monitor(self, monitor, monitor_interfaces, logger):
|
||||
def sleep(*args, **kwargs):
|
||||
monitor.stop()
|
||||
@ -260,6 +258,7 @@ class TestSoundMonitor:
|
||||
assert observer
|
||||
schedules = observer._traces("schedule")
|
||||
for (handler, *_), kwargs in schedules:
|
||||
breakpoint()
|
||||
assert isinstance(handler, sound_monitor.MonitorHandler)
|
||||
assert isinstance(handler.pool, futures.ThreadPoolExecutor)
|
||||
assert (handler.subdir, handler.type) in (
|
||||
|
BIN
aircox/tests/image.png
Normal file
BIN
aircox/tests/image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 69 B |
122
aircox/tests/models/test_sound.py
Normal file
122
aircox/tests/models/test_sound.py
Normal file
@ -0,0 +1,122 @@
|
||||
from datetime import timedelta
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils import timezone as tz
|
||||
|
||||
from aircox import models
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def path_infos():
|
||||
return {
|
||||
"test/20220101_10h13_1_sample_1.mp3": {
|
||||
"year": 2022,
|
||||
"month": 1,
|
||||
"day": 1,
|
||||
"hour": 10,
|
||||
"minute": 13,
|
||||
"n": 1,
|
||||
"name": "Sample 1",
|
||||
},
|
||||
"test/20220102_10h13_sample_2.mp3": {
|
||||
"year": 2022,
|
||||
"month": 1,
|
||||
"day": 2,
|
||||
"hour": 10,
|
||||
"minute": 13,
|
||||
"n": None,
|
||||
"name": "Sample 2",
|
||||
},
|
||||
"test/20220103_1_sample_3.mp3": {
|
||||
"year": 2022,
|
||||
"month": 1,
|
||||
"day": 3,
|
||||
"hour": None,
|
||||
"minute": None,
|
||||
"n": 1,
|
||||
"name": "Sample 3",
|
||||
},
|
||||
"test/20220104_sample_4.mp3": {
|
||||
"year": 2022,
|
||||
"month": 1,
|
||||
"day": 4,
|
||||
"hour": None,
|
||||
"minute": None,
|
||||
"n": None,
|
||||
"name": "Sample 4",
|
||||
},
|
||||
"test/20220105.mp3": {
|
||||
"year": 2022,
|
||||
"month": 1,
|
||||
"day": 5,
|
||||
"hour": None,
|
||||
"minute": None,
|
||||
"n": None,
|
||||
"name": "20220105",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class TestSoundQuerySet:
|
||||
@pytest.mark.django_db
|
||||
def test_downloadable(self, sounds):
|
||||
query = models.Sound.objects.downloadable().values_list("is_downloadable", flat=True)
|
||||
assert set(query) == {True}
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_broadcast(self, sounds):
|
||||
query = models.Sound.objects.broadcast().values_list("broadcast", flat=True)
|
||||
assert set(query) == {True}
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_playlist(self, sounds):
|
||||
expected = [os.path.join(settings.MEDIA_ROOT, s.file.path) for s in sounds]
|
||||
assert models.Sound.objects.all().playlist() == expected
|
||||
|
||||
|
||||
class TestSound:
|
||||
@pytest.mark.django_db
|
||||
def test_read_path(self, path_infos):
|
||||
for path, expected in path_infos.items():
|
||||
result = models.Sound.read_path(path)
|
||||
assert expected == result
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test__as_name(self):
|
||||
name = "some_1_file"
|
||||
assert models.Sound._as_name(name) == "Some 1 File"
|
||||
|
||||
def _setup_diff(self, program, info):
|
||||
episode = models.Episode(program=program, title="test-episode")
|
||||
at = tz.datetime(**{k: info[k] for k in ("year", "month", "day", "hour", "minute") if info.get(k)})
|
||||
at = tz.make_aware(at)
|
||||
diff = models.Diffusion(episode=episode, start=at, end=at + timedelta(hours=1))
|
||||
episode.save()
|
||||
diff.save()
|
||||
return diff
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
def test_find_episode(self, program, path_infos):
|
||||
for path, infos in path_infos.items():
|
||||
diff = self._setup_diff(program, infos)
|
||||
sound = models.Sound(program=diff.program, file=path)
|
||||
result = sound.find_episode(infos)
|
||||
assert diff.episode == result
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_find_playlist(self):
|
||||
raise NotImplementedError("test is not implemented")
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_upload_dir(self):
|
||||
raise NotImplementedError("test is not implemented")
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_sync_fs(self):
|
||||
raise NotImplementedError("test is not implemented")
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_read_metadata(self):
|
||||
raise NotImplementedError("test is not implemented")
|
@ -1,45 +0,0 @@
|
||||
from django.urls import path, reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import pytest
|
||||
|
||||
from aircox import admin_site, urls as _urls
|
||||
from .conftest import req_factory
|
||||
|
||||
|
||||
# Just for code quality: urls module is required because we need some
|
||||
# url resolvers to be registered in order to run tests.
|
||||
_urls
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def site():
|
||||
return admin_site.AdminSite()
|
||||
|
||||
|
||||
class TestAdminSite:
|
||||
@pytest.mark.django_db
|
||||
def test_each_context(self, site, staff_user):
|
||||
req = req_factory.get("admin/test")
|
||||
req.user = staff_user
|
||||
context = site.each_context(req)
|
||||
assert "programs" in context
|
||||
assert "diffusions" in context
|
||||
assert "comments" in context
|
||||
|
||||
def test_get_urls(self, site):
|
||||
extra_url = path("test/path", lambda *_, **kw: _)
|
||||
site.extra_urls.append(extra_url)
|
||||
urls = site.get_urls()
|
||||
assert extra_url in urls
|
||||
|
||||
def test_get_tools(self, site):
|
||||
tools = site.get_tools()
|
||||
tools = dict(tools)
|
||||
assert tools == {
|
||||
_("Statistics"): reverse("admin:tools-stats"),
|
||||
}
|
||||
|
||||
def test_route_view(self, site):
|
||||
# TODO
|
||||
pass
|
@ -11,6 +11,9 @@ class FakeView:
|
||||
def ___init__(self):
|
||||
self.kwargs = {}
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from django.urls import reverse
|
||||
|
||||
from aircox import models
|
||||
from aircox.test import Interface
|
||||
@ -37,31 +36,15 @@ class TestBaseView:
|
||||
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,
|
||||
"nav_menu": [],
|
||||
}
|
||||
|
||||
|
||||
|
@ -40,7 +40,7 @@ def parent_mixin():
|
||||
@pytest.fixture
|
||||
def attach_mixin():
|
||||
class Mixin(mixins.AttachedToMixin, FakeView):
|
||||
attach_to_value = models.StaticPage.ATTACH_TO_HOME
|
||||
attach_to_value = models.StaticPage.Target.HOME
|
||||
|
||||
return Mixin()
|
||||
|
||||
@ -80,7 +80,7 @@ class TestGetDateMixin:
|
||||
)
|
||||
|
||||
def test_get_calls_get_date(self, date_mixin):
|
||||
date_mixin.get_date = lambda: today
|
||||
date_mixin.get_date = lambda *_: today
|
||||
date_mixin.get()
|
||||
assert date_mixin.date == today
|
||||
|
||||
@ -105,10 +105,10 @@ class TestParentMixin:
|
||||
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):
|
||||
def test_dispatch_calls_parent(self, parent_mixin):
|
||||
parent = "parent object"
|
||||
parent_mixin.get_parent = lambda *_, **kw: parent
|
||||
parent_mixin.get(self.req)
|
||||
parent_mixin.dispatch(self.req)
|
||||
assert parent_mixin.parent == parent
|
||||
|
||||
@pytest.mark.django_db
|
||||
@ -120,7 +120,7 @@ class TestParentMixin:
|
||||
assert set(query) == episodes_id
|
||||
|
||||
def test_get_context_data_with_parent(self, parent_mixin):
|
||||
parent_mixin.parent = Interface(cover="parent-cover")
|
||||
parent_mixin.parent = Interface(cover=Interface(url="parent-cover"))
|
||||
context = parent_mixin.get_context_data()
|
||||
assert context["cover"] == "parent-cover"
|
||||
|
||||
|
Reference in New Issue
Block a user