forked from rc/aircox
!111 Co-authored-by: bkfox <thomas bkfox net> Reviewed-on: rc/aircox#114
This commit is contained in:
@ -1,10 +1,20 @@
|
||||
from datetime import time, timedelta
|
||||
import itertools
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
from model_bakery import baker
|
||||
|
||||
from aircox import models
|
||||
from aircox.test import Interface
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def logger():
|
||||
logger = Interface(
|
||||
logging, {"info": None, "debug": None, "error": None, "warning": None}
|
||||
)
|
||||
return logger
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -100,3 +110,24 @@ def podcasts(episodes):
|
||||
sound.file = f"test_sound_{episode.pk}_{i}.mp3"
|
||||
items += sounds
|
||||
return items
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sound(program):
|
||||
return baker.make(models.Sound, file="tmp/test.wav", program=program)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tracks(episode, sound):
|
||||
items = [
|
||||
baker.prepare(
|
||||
models.Track, episode=episode, position=i, timestamp=i * 60
|
||||
)
|
||||
for i in range(0, 3)
|
||||
]
|
||||
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
|
||||
|
3
aircox/tests/controllers/playlist.csv
Normal file
3
aircox/tests/controllers/playlist.csv
Normal file
@ -0,0 +1,3 @@
|
||||
Artist 1;Title 1;1;0;tag1,tag12;info1
|
||||
Artist 2;Title 2;2;1;tag2,tag12;info2
|
||||
Artist 3;Title 3;3;2;;
|
|
110
aircox/tests/controllers/test_log_archiver.py
Normal file
110
aircox/tests/controllers/test_log_archiver.py
Normal file
@ -0,0 +1,110 @@
|
||||
from django.utils import timezone as tz
|
||||
|
||||
import pytest
|
||||
from model_bakery import baker
|
||||
|
||||
from aircox import models
|
||||
from aircox.test import Interface, File
|
||||
from aircox.controllers import log_archiver
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def diffusions(episodes):
|
||||
items = [
|
||||
baker.prepare(
|
||||
models.Diffusion,
|
||||
program=episode.program,
|
||||
episode=episode,
|
||||
type=models.Diffusion.TYPE_ON_AIR,
|
||||
)
|
||||
for episode in episodes
|
||||
]
|
||||
models.Diffusion.objects.bulk_create(items)
|
||||
return items
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def logs(diffusions, sound, tracks):
|
||||
now = tz.now()
|
||||
station = diffusions[0].program.station
|
||||
items = [
|
||||
models.Log(
|
||||
station=diffusion.program.station,
|
||||
type=models.Log.TYPE_START,
|
||||
date=now + tz.timedelta(hours=-10, minutes=i),
|
||||
source="13",
|
||||
diffusion=diffusion,
|
||||
)
|
||||
for i, diffusion in enumerate(diffusions)
|
||||
]
|
||||
items += [
|
||||
models.Log(
|
||||
station=station,
|
||||
type=models.Log.TYPE_ON_AIR,
|
||||
date=now + tz.timedelta(hours=-9, minutes=i),
|
||||
source="14",
|
||||
track=track,
|
||||
sound=track.sound,
|
||||
)
|
||||
for i, track in enumerate(tracks)
|
||||
]
|
||||
models.Log.objects.bulk_create(items)
|
||||
return items
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def logs_qs(logs):
|
||||
return models.Log.objects.filter(pk__in=(r.pk for r in logs))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def file():
|
||||
return File(data=b"")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def gzip(file):
|
||||
gzip = Interface.inject(log_archiver, "gzip", {"open": file})
|
||||
yield gzip
|
||||
gzip._irelease()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def archiver():
|
||||
return log_archiver.LogArchiver()
|
||||
|
||||
|
||||
class TestLogArchiver:
|
||||
@pytest.mark.django_db
|
||||
def test_archive_then_load_file(self, archiver, file, gzip, logs, logs_qs):
|
||||
# before logs are deleted from db, get data
|
||||
sorted = archiver.sort_logs(logs_qs)
|
||||
paths = {
|
||||
archiver.get_path(station, date) for station, date in sorted.keys()
|
||||
}
|
||||
|
||||
count = archiver.archive(logs_qs, keep=False)
|
||||
assert count == len(logs)
|
||||
assert not logs_qs.count()
|
||||
assert all(
|
||||
path in paths for path, *_ in gzip._traces("open", args=True)
|
||||
)
|
||||
|
||||
results = archiver.load_file("dummy path")
|
||||
assert results
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_archive_no_qs(self, archiver):
|
||||
count = archiver.archive(models.Log.objects.none())
|
||||
assert not count
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_sort_log(self, archiver, logs_qs):
|
||||
sorted = archiver.sort_logs(logs_qs)
|
||||
|
||||
assert sorted
|
||||
for (station, date), logs in sorted.items():
|
||||
assert all(
|
||||
log.station == station and log.date.date() == date
|
||||
for log in logs
|
||||
)
|
64
aircox/tests/controllers/test_playlist_import.py
Normal file
64
aircox/tests/controllers/test_playlist_import.py
Normal file
@ -0,0 +1,64 @@
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from aircox.test import Interface
|
||||
from aircox.controllers import playlist_import
|
||||
|
||||
|
||||
csv_data = [
|
||||
{
|
||||
"artist": "Artist 1",
|
||||
"title": "Title 1",
|
||||
"minutes": "1",
|
||||
"seconds": "0",
|
||||
"tags": "tag1,tag12",
|
||||
"info": "info1",
|
||||
},
|
||||
{
|
||||
"artist": "Artist 2",
|
||||
"title": "Title 2",
|
||||
"minutes": "2",
|
||||
"seconds": "1",
|
||||
"tags": "tag2,tag12",
|
||||
"info": "info2",
|
||||
},
|
||||
{
|
||||
"artist": "Artist 3",
|
||||
"title": "Title 3",
|
||||
"minutes": "3",
|
||||
"seconds": "2",
|
||||
"tags": "",
|
||||
"info": "",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def importer(sound):
|
||||
path = os.path.join(os.path.dirname(__file__), "playlist.csv")
|
||||
return playlist_import.PlaylistImport(path, sound=sound)
|
||||
|
||||
|
||||
class TestPlaylistImport:
|
||||
@pytest.mark.django_db
|
||||
def test_run(self, importer):
|
||||
iface = Interface(None, {"read": None, "make_playlist": None})
|
||||
importer.read = iface.read
|
||||
importer.make_playlist = iface.make_playlist
|
||||
importer.run()
|
||||
assert iface._trace("read")
|
||||
assert iface._trace("make_playlist")
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_read(self, importer):
|
||||
importer.read()
|
||||
assert importer.data == csv_data
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_make_playlist(self, importer, sound):
|
||||
importer.data = csv_data
|
||||
importer.make_playlist()
|
||||
track_artists = sound.track_set.all().values_list("artist", flat=True)
|
||||
csv_artists = {r["artist"] for r in csv_data}
|
||||
assert set(track_artists) == csv_artists
|
||||
# TODO: check other values
|
@ -6,7 +6,7 @@ from django.conf import settings as conf
|
||||
from django.utils import timezone as tz
|
||||
|
||||
from aircox import models
|
||||
from aircox.management.sound_file import SoundFile
|
||||
from aircox.controllers.sound_file import SoundFile
|
||||
|
||||
|
||||
@pytest.fixture
|
265
aircox/tests/controllers/test_sound_monitor.py
Normal file
265
aircox/tests/controllers/test_sound_monitor.py
Normal file
@ -0,0 +1,265 @@
|
||||
from concurrent import futures
|
||||
import pytest
|
||||
|
||||
from django.utils import timezone as tz
|
||||
|
||||
from aircox.conf import settings
|
||||
from aircox.models import Sound
|
||||
from aircox.controllers import sound_monitor
|
||||
from aircox.test import Interface, interface
|
||||
|
||||
|
||||
now = tz.datetime.now()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def event():
|
||||
return Interface(src_path="/tmp/src_path", dest_path="/tmp/dest_path")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def interfaces():
|
||||
items = {
|
||||
"SoundFile": Interface.inject(
|
||||
sound_monitor,
|
||||
"SoundFile",
|
||||
{
|
||||
"sync": None,
|
||||
},
|
||||
),
|
||||
"time": Interface.inject(
|
||||
sound_monitor,
|
||||
"time",
|
||||
{
|
||||
"sleep": None,
|
||||
},
|
||||
),
|
||||
"datetime": Interface.inject(sound_monitor, "datetime", {"now": now}),
|
||||
}
|
||||
yield items
|
||||
for item in items.values():
|
||||
item._irelease()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def task(interfaces):
|
||||
return sound_monitor.Task()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def delete_task(interfaces):
|
||||
return sound_monitor.DeleteTask()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def move_task(interfaces):
|
||||
return sound_monitor.MoveTask()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def modified_task(interfaces):
|
||||
return sound_monitor.ModifiedTask()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def monitor_handler(interfaces):
|
||||
pool = Interface(
|
||||
None,
|
||||
{
|
||||
"submit": lambda imeta, *a, **kw: Interface(
|
||||
None,
|
||||
{
|
||||
"add_done_callback": None,
|
||||
"done": False,
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
return sound_monitor.MonitorHandler("/tmp", pool=pool, sync_kw=13)
|
||||
|
||||
|
||||
class TestTask:
|
||||
def test___init__(self, task):
|
||||
assert task.timestamp is not None
|
||||
|
||||
def test_ping(self, task):
|
||||
task.timestamp = None
|
||||
task.ping()
|
||||
assert task.timestamp >= now
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test___call__(self, logger, task, event):
|
||||
task.log_msg = "--{event.src_path}--"
|
||||
sound_file = task(event, logger=logger, kw=13)
|
||||
assert sound_file._trace("sync", kw=True) == {"kw": 13}
|
||||
assert logger._trace("info", args=True) == (
|
||||
task.log_msg.format(event=event),
|
||||
)
|
||||
|
||||
|
||||
class TestDeleteTask:
|
||||
@pytest.mark.django_db
|
||||
def test___call__(self, delete_task, logger, task, event):
|
||||
sound_file = delete_task(event, logger=logger)
|
||||
assert sound_file._trace("sync", kw=True) == {"deleted": True}
|
||||
|
||||
|
||||
class TestMoveTask:
|
||||
@pytest.mark.django_db
|
||||
def test__call___with_sound(self, move_task, sound, event, logger):
|
||||
event.src_path = sound.file.name
|
||||
sound_file = move_task(event, logger=logger)
|
||||
assert isinstance(sound_file._trace("sync", kw="sound"), Sound)
|
||||
assert sound_file.path == sound.file.name
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test__call___no_sound(self, move_task, event, logger):
|
||||
sound_file = move_task(event, logger=logger)
|
||||
assert sound_file._trace("sync", kw=True) == {}
|
||||
assert sound_file.path == event.dest_path
|
||||
|
||||
|
||||
class TestModifiedTask:
|
||||
def test_wait(self, modified_task):
|
||||
dt_now = now + modified_task.timeout_delta - tz.timedelta(hours=10)
|
||||
datetime = Interface.inject(sound_monitor, "datetime", {"now": dt_now})
|
||||
|
||||
def sleep(imeta, n):
|
||||
datetime._imeta.funcs[
|
||||
"now"
|
||||
] = modified_task.timestamp + tz.timedelta(hours=10)
|
||||
|
||||
time = Interface.inject(sound_monitor, "time", {"sleep": sleep})
|
||||
modified_task.wait()
|
||||
assert time._trace("sleep", args=True)
|
||||
|
||||
datetime._imeta.release()
|
||||
|
||||
def test__call__(self, modified_task, event):
|
||||
interface(modified_task, {"wait": None})
|
||||
modified_task(event)
|
||||
assert modified_task.calls["wait"]
|
||||
|
||||
|
||||
class TestMonitorHandler:
|
||||
def test_on_created(self, monitor_handler, event):
|
||||
monitor_handler._submit = monitor_handler.pool.submit
|
||||
monitor_handler.on_created(event)
|
||||
trace_args, trace_kwargs = monitor_handler.pool._trace("submit")
|
||||
assert isinstance(trace_args[0], sound_monitor.CreateTask)
|
||||
assert trace_args[1:] == (event, "new")
|
||||
assert trace_kwargs == monitor_handler.sync_kw
|
||||
|
||||
def test_on_deleted(self, monitor_handler, event):
|
||||
monitor_handler._submit = monitor_handler.pool.submit
|
||||
monitor_handler.on_deleted(event)
|
||||
trace_args, _ = monitor_handler.pool._trace("submit")
|
||||
assert isinstance(trace_args[0], sound_monitor.DeleteTask)
|
||||
assert trace_args[1:] == (event, "del")
|
||||
|
||||
def test_on_moved(self, monitor_handler, event):
|
||||
monitor_handler._submit = monitor_handler.pool.submit
|
||||
monitor_handler.on_moved(event)
|
||||
trace_args, trace_kwargs = monitor_handler.pool._trace("submit")
|
||||
assert isinstance(trace_args[0], sound_monitor.MoveTask)
|
||||
assert trace_args[1:] == (event, "mv")
|
||||
assert trace_kwargs == monitor_handler.sync_kw
|
||||
|
||||
def test_on_modified(self, monitor_handler, event):
|
||||
monitor_handler._submit = monitor_handler.pool.submit
|
||||
monitor_handler.on_modified(event)
|
||||
trace_args, trace_kwargs = monitor_handler.pool._trace("submit")
|
||||
assert isinstance(trace_args[0], sound_monitor.ModifiedTask)
|
||||
assert trace_args[1:] == (event, "up")
|
||||
assert trace_kwargs == monitor_handler.sync_kw
|
||||
|
||||
def test__submit(self, monitor_handler, event):
|
||||
handler = Interface()
|
||||
handler, created = monitor_handler._submit(
|
||||
handler, event, "prefix", kw=13
|
||||
)
|
||||
assert created
|
||||
assert handler.future._trace("add_done_callback")
|
||||
assert monitor_handler.pool._trace("submit") == (
|
||||
(handler, event),
|
||||
{"kw": 13},
|
||||
)
|
||||
|
||||
key = f"prefix:{event.src_path}"
|
||||
assert monitor_handler.jobs.get(key) == handler
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def monitor_interfaces():
|
||||
items = {
|
||||
"atexit": Interface.inject(
|
||||
sound_monitor, "atexit", {"register": None, "leave": None}
|
||||
),
|
||||
"observer": Interface.inject(
|
||||
sound_monitor,
|
||||
"Observer",
|
||||
{
|
||||
"schedule": None,
|
||||
"start": None,
|
||||
},
|
||||
),
|
||||
}
|
||||
yield items
|
||||
for item in items.values():
|
||||
item.release()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def monitor():
|
||||
yield sound_monitor.SoundMonitor()
|
||||
|
||||
|
||||
class SoundMonitor:
|
||||
def test_report(self, monitor, program, logger):
|
||||
monitor.report(program, "component", "content", logger=logger)
|
||||
msg = f"{program}, component: content"
|
||||
assert logger._trace("info", args=True) == (msg,)
|
||||
|
||||
def test_scan(self, monitor, program, logger):
|
||||
interface = Interface(None, {"scan_for_program": None})
|
||||
monitor.scan_for_program = interface.scan_for_program
|
||||
dirs = monitor.scan(logger)
|
||||
|
||||
assert logger._traces("info") == (
|
||||
"scan all programs...",
|
||||
f"#{program.id} {program.title}",
|
||||
)
|
||||
assert dirs == [program.abspath]
|
||||
assert interface._traces("scan_for_program") == (
|
||||
((program, settings.SOUND_ARCHIVES_SUBDIR), {"logger": logger})(
|
||||
(program, settings.SOUND_EXCERPTS_SUBDIR), {"logger": logger}
|
||||
)
|
||||
)
|
||||
|
||||
def test_monitor(self, monitor, monitor_interfaces, logger):
|
||||
def sleep(*args, **kwargs):
|
||||
monitor.stop()
|
||||
|
||||
time = Interface.inject(sound_monitor, "time", {"sleep": sleep})
|
||||
monitor.monitor(logger=logger)
|
||||
time._irelease()
|
||||
|
||||
observers = monitor_interfaces["observer"].instances
|
||||
observer = observers and observers[0]
|
||||
assert observer
|
||||
schedules = observer._traces("schedule")
|
||||
for (handler, *_), kwargs in schedules:
|
||||
assert isinstance(handler, sound_monitor.MonitorHandler)
|
||||
assert isinstance(handler.pool, futures.ThreadPoolExecutor)
|
||||
assert (handler.subdir, handler.type) in (
|
||||
(settings.SOUND_ARCHIVES_SUBDIR, Sound.TYPE_ARCHIVE),
|
||||
(settings.SOUND_EXCERPTS_SUBDIR, Sound.TYPE_EXCERPT),
|
||||
)
|
||||
|
||||
assert observer._trace("start")
|
||||
|
||||
atexit = monitor_interfaces["atexit"]
|
||||
assert atexit._trace("register")
|
||||
assert atexit._trace("unregister")
|
||||
|
||||
assert observers
|
123
aircox/tests/controllers/test_sound_stats.py
Normal file
123
aircox/tests/controllers/test_sound_stats.py
Normal file
@ -0,0 +1,123 @@
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
from aircox.test import Interface
|
||||
from aircox.controllers import sound_stats
|
||||
|
||||
|
||||
sox_output = """
|
||||
DC offset 0.000000\n
|
||||
Min level 0.000000\n
|
||||
Max level 0.000000\n
|
||||
Pk lev dB -inf\n
|
||||
RMS lev dB -inf\n
|
||||
RMS Pk dB -inf\n
|
||||
RMS Tr dB -inf\n
|
||||
Crest factor 1.00\n
|
||||
Flat factor 179.37\n
|
||||
Pk count 1.86G\n
|
||||
Bit-depth 0/0\n
|
||||
Num samples 930M\n
|
||||
Length s 19383.312\n
|
||||
Scale max 1.000000\n
|
||||
Window s 0.050\n
|
||||
"""
|
||||
sox_values = {
|
||||
"DC offset": 0.0,
|
||||
"Min level": 0.0,
|
||||
"Max level": 0.0,
|
||||
"Pk lev dB": float("-inf"),
|
||||
"RMS lev dB": float("-inf"),
|
||||
"RMS Pk dB": float("-inf"),
|
||||
"RMS Tr dB": float("-inf"),
|
||||
"Flat factor": 179.37,
|
||||
"length": 19383.312,
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sox_interfaces():
|
||||
process = Interface(
|
||||
None, {"communicate": ("", sox_output.encode("utf-8"))}
|
||||
)
|
||||
subprocess = Interface.inject(
|
||||
sound_stats, "subprocess", {"Popen": lambda *_, **__: process}
|
||||
)
|
||||
yield {"process": process, "subprocess": subprocess}
|
||||
subprocess._irelease()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sox_stats(sox_interfaces):
|
||||
return sound_stats.SoxStats()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def stats():
|
||||
return sound_stats.SoundStats("/tmp/audio.wav", sample_length=10)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def stats_interfaces(stats):
|
||||
def iw(path, **kw):
|
||||
kw["path"] = path
|
||||
kw.setdefault("length", stats.sample_length * 2)
|
||||
return kw
|
||||
|
||||
SxS = sound_stats.SoxStats
|
||||
sound_stats.SoxStats = iw
|
||||
yield iw
|
||||
sound_stats.SoxStats = SxS
|
||||
|
||||
|
||||
class TestSoxStats:
|
||||
def test_parse(self, sox_stats):
|
||||
values = sox_stats.parse(sox_output)
|
||||
assert values == sox_values
|
||||
|
||||
def test_analyse(self, sox_stats, sox_interfaces):
|
||||
sox_stats.analyse("fake_path", 1, 2)
|
||||
assert sox_interfaces["subprocess"]._trace("Popen") == (
|
||||
(["sox", "fake_path", "-n", "trim", "1", "2", "stats"],),
|
||||
{"stdout": subprocess.PIPE, "stderr": subprocess.PIPE},
|
||||
)
|
||||
assert sox_stats.values == sox_values
|
||||
|
||||
|
||||
class TestSoundStats:
|
||||
def test_get_file_stats(self, stats):
|
||||
file_stats = {"a": 134}
|
||||
stats.stats = [file_stats]
|
||||
assert stats.get_file_stats() is file_stats
|
||||
|
||||
def test_get_file_stats_none(self, stats):
|
||||
stats.stats = []
|
||||
assert stats.get_file_stats() is None
|
||||
|
||||
def test_analyse(self, stats, stats_interfaces):
|
||||
stats.analyse()
|
||||
assert stats.stats == [
|
||||
{"path": stats.path, "length": stats.sample_length * 2},
|
||||
{"path": stats.path, "at": 0, "length": stats.sample_length},
|
||||
{"path": stats.path, "at": 10, "length": stats.sample_length},
|
||||
]
|
||||
|
||||
def test_analyse_no_sample_length(self, stats, stats_interfaces):
|
||||
stats.sample_length = 0
|
||||
stats.analyse()
|
||||
assert stats.stats == [{"length": 0, "path": stats.path}]
|
||||
|
||||
def test_check(self, stats):
|
||||
good = [{"val": i} for i in range(0, 11)]
|
||||
bad = [{"val": i} for i in range(-10, 0)] + [
|
||||
{"val": i} for i in range(11, 20)
|
||||
]
|
||||
stats.stats = good + bad
|
||||
calls = {}
|
||||
stats.resume = lambda *_: calls.setdefault("resume", True)
|
||||
stats.check("val", 0, 10)
|
||||
|
||||
assert calls == {"resume": True}
|
||||
assert all(i < len(good) for i in stats.good)
|
||||
assert all(i >= len(good) for i in stats.bad)
|
64
aircox/tests/models/test_episode.py
Normal file
64
aircox/tests/models/test_episode.py
Normal file
@ -0,0 +1,64 @@
|
||||
import datetime
|
||||
import pytest
|
||||
|
||||
from aircox.conf import settings
|
||||
from aircox import models
|
||||
|
||||
|
||||
class TestEpisode:
|
||||
@pytest.mark.django_db
|
||||
def test_program(self, episode):
|
||||
assert episode.program == episode.parent.program
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_podcasts(self, episode, podcasts):
|
||||
podcasts = {
|
||||
podcast.pk: podcast
|
||||
for podcast in podcasts
|
||||
if podcast.episode == episode
|
||||
}
|
||||
for data in episode.podcasts:
|
||||
podcast = podcasts[data["pk"]]
|
||||
assert data["name"] == podcast.name
|
||||
assert data["page_url"] == episode.get_absolute_url()
|
||||
assert data["page_title"] == episode.title
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_absolute_url_is_published(self, episode):
|
||||
episode.status = models.Episode.STATUS_PUBLISHED
|
||||
assert episode.get_absolute_url() != episode.parent.get_absolute_url()
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_absolute_url_not_published(self, episode):
|
||||
episode.status = models.Episode.STATUS_DRAFT
|
||||
assert episode.get_absolute_url() == episode.parent.get_absolute_url()
|
||||
|
||||
@pytest.mark.django_db(transaction=False)
|
||||
def test_save_raises_parent_is_none(self, episode):
|
||||
with pytest.raises(ValueError):
|
||||
episode.parent = None
|
||||
episode.save()
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_default_title(self, programs):
|
||||
program = programs[0]
|
||||
date = datetime.date(2023, 5, 17)
|
||||
result = models.Episode.get_default_title(program, date)
|
||||
assert program.title in result
|
||||
assert date.strftime(settings.EPISODE_TITLE_DATE_FORMAT) in result
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_init_kwargs_from(self, program):
|
||||
date = datetime.date(2023, 3, 14)
|
||||
date_str = date.strftime(settings.EPISODE_TITLE_DATE_FORMAT)
|
||||
|
||||
kwargs = models.Episode.get_init_kwargs_from(program, date)
|
||||
title = kwargs["title"]
|
||||
assert program.title in title
|
||||
assert date_str in title
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_init_kwargs_from_with_title(self, program):
|
||||
title = "test title"
|
||||
kwargs = models.Episode.get_init_kwargs_from(program, title=title)
|
||||
assert title == kwargs["title"]
|
Reference in New Issue
Block a user