diff --git a/program/tests/__init__.py b/program/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..eeef18a2d317dfa39aea5752ac257bc20d0e04bd --- /dev/null +++ b/program/tests/__init__.py @@ -0,0 +1,100 @@ +import datetime + +from django.contrib.auth.models import User +from django.utils.text import slugify +from django.utils.timezone import now +from program.models import Note, RRule, Schedule, Show, TimeSlot + + +class SteeringTestCaseMixin: + base_url = "/api/v1" + + def _url(self, *paths, **kwargs): + url = "/".join(str(p) for p in paths) + "/" + return f"{self.base_url}/{url.format(**kwargs)}" + + def _get_client(self, user=None): + client = self.client_class() + if user: + client.force_authenticate(user=user) + return client + + +class UserMixin: + user_admin: User + user_common: User + + def setUp(self): + self.user_admin = User.objects.create_superuser( + "admin", "admin@aura.radio", password="admin" + ) + self.user_common = User.objects.create_user( + "herbert", "herbert@aura.radio", password="herbert" + ) + + +class ShowMixin: + def _create_show(self, name: str, **kwargs): + kwargs["name"] = name + kwargs.setdefault("slug", slugify(name)) + kwargs.setdefault("short_description", f"The {name} show") + owners = kwargs.pop("owners", []) + show = Show.objects.create(**kwargs) + if owners: + show.owners.set(owners) + return show + + +class ScheduleMixin: + def _get_rrule(self): + rrule = RRule.objects.first() + if rrule is None: + rrule = RRule.objects.create(name="once", freq=0) + return rrule + + def _create_schedule(self, show: Show, **kwargs): + _first_date = kwargs.get("first_date", now().date()) + kwargs["show"] = show + kwargs.setdefault("first_date", _first_date) + kwargs.setdefault("start_time", "08:00") + kwargs.setdefault("last_date", _first_date + datetime.timedelta(days=365)) + kwargs.setdefault("end_time", "09:00") + kwargs.setdefault("rrule", self._get_rrule()) + return Schedule.objects.create(**kwargs) + + +class TimeSlotMixin: + def _create_timeslot(self, schedule: Schedule, **kwargs): + _start = kwargs.get("start", now()) + kwargs.setdefault("schedule", schedule) + kwargs.setdefault("show", schedule.show) + kwargs.setdefault("start", _start) + kwargs.setdefault("end", _start + datetime.timedelta(hours=1)) + return TimeSlot.objects.create(**kwargs) + + +class NoteMixin: + def _create_note(self, timeslot: TimeSlot, **kwargs): + note_count = Note.objects.all().count() + _title = kwargs.get("title", f"a random note #{note_count}") + kwargs["timeslot"] = timeslot + kwargs["title"] = _title + kwargs.setdefault("slug", slugify(_title)) + return Note.objects.create(**kwargs) + + def _create_random_note_content(self, **kwargs): + note_count = Note.objects.all().count() + _title = kwargs.get("title", f"a random note #{note_count}") + kwargs["title"] = _title + kwargs.setdefault("slug", slugify(_title)) + kwargs.setdefault("content", "some random content") + kwargs.setdefault("contributors", []) + return kwargs + + +class ProgramModelMixin(ShowMixin, ScheduleMixin, TimeSlotMixin, NoteMixin): + pass + + +class BaseMixin(UserMixin, ProgramModelMixin, SteeringTestCaseMixin): + pass diff --git a/program/tests/test_notes.py b/program/tests/test_notes.py new file mode 100644 index 0000000000000000000000000000000000000000..d21fdadcaabcc7bca81efd6da49eda400df4e325 --- /dev/null +++ b/program/tests/test_notes.py @@ -0,0 +1,124 @@ +from rest_framework.test import APITransactionTestCase + +from program import tests +from program.models import Schedule, Show + + +class NoteViewTestCase(tests.BaseMixin, APITransactionTestCase): + reset_sequences = True + + show_beatbetrieb: Show + schedule_beatbetrieb: Schedule + show_musikrotation: Show + schedule_musikrotation: Schedule + + def setUp(self) -> None: + super().setUp() + self.show_beatbetrieb = self._create_show("Beatbetrieb") + self.schedule_beatbetrieb = self._create_schedule(self.show_beatbetrieb) + self.show_musikrotation = self._create_show("Musikrotation", owners=[self.user_common]) + self.schedule_musikrotation = self._create_schedule( + self.show_musikrotation, start_time="10:00", end_time="12:00" + ) + + def test_everyone_can_read_notes(self): + self._create_note(self._create_timeslot(schedule=self.schedule_beatbetrieb)) + self._create_note(self._create_timeslot(schedule=self.schedule_musikrotation)) + res = self._get_client().get(self._url("notes")) + self.assertEqual(len(res.data), 2) + + def test_common_users_can_create_notes_for_owned_shows(self): + ts = self._create_timeslot(schedule=self.schedule_musikrotation) + client = self._get_client(self.user_common) + endpoint = self._url("notes") + res = client.post( + endpoint, self._create_random_note_content(timeslot=ts.id), format="json" + ) + self.assertEqual(res.status_code, 201) + + def test_common_users_cannot_create_notes_for_foreign_shows(self): + ts = self._create_timeslot(schedule=self.schedule_beatbetrieb) + client = self._get_client(self.user_common) + endpoint = self._url("notes") + res = client.post( + endpoint, self._create_random_note_content(timeslot=ts.id), format="json" + ) + self.assertEqual(res.status_code, 404) + + def test_common_user_can_update_owned_shows(self): + ts = self._create_timeslot(schedule=self.schedule_musikrotation) + note = self._create_note(ts) + client = self._get_client(self.user_common) + new_note_content = self._create_random_note_content(title="meh") + res = client.put(self._url("notes", note.id), new_note_content, format="json") + self.assertEqual(res.status_code, 200) + + def test_common_user_cannot_update_notes_of_foreign_shows(self): + ts = self._create_timeslot(schedule=self.schedule_beatbetrieb) + note = self._create_note(ts) + client = self._get_client(self.user_common) + new_note_content = self._create_random_note_content(title="meh") + res = client.put(self._url("notes", note.id), new_note_content, format="json") + self.assertEqual(res.status_code, 404) + + def test_admin_can_create_notes_for_all_timeslots(self): + timeslot = self._create_timeslot(schedule=self.schedule_musikrotation) + client = self._get_client(self.user_admin) + res = client.post( + self._url("notes"), + self._create_random_note_content(timeslot=timeslot.id), + format="json", + ) + self.assertEqual(res.status_code, 201) + + def test_notes_can_be_created_through_nested_routes(self): + client = self._get_client(self.user_admin) + + # /shows/{pk}/notes/ + ts1 = self._create_timeslot(schedule=self.schedule_musikrotation) + url = self._url("shows", self.show_musikrotation.id, "notes") + note = self._create_random_note_content(title="meh", timeslot=ts1.id) + res = client.post(url, note, format="json") + self.assertEqual(res.status_code, 201) + + # /shows/{pk}/timeslots/{pk}/note/ + ts2 = self._create_timeslot(schedule=self.schedule_musikrotation) + url = self._url("shows", self.show_musikrotation, "timeslots", ts2.id, "note") + note = self._create_random_note_content(title="cool") + res = client.post(url, note, format="json") + self.assertEqual(res.status_code, 201) + + def test_notes_can_be_filtered_through_nested_routes_and_query_params(self): + client = self._get_client() + + ts1 = self._create_timeslot(schedule=self.schedule_musikrotation) + ts2 = self._create_timeslot(schedule=self.schedule_beatbetrieb) + ts3 = self._create_timeslot(schedule=self.schedule_beatbetrieb) + n1 = self._create_note(timeslot=ts1) + n2 = self._create_note(timeslot=ts2) + n3 = self._create_note(timeslot=ts3) + + def _get_ids(res): + return set(ts["id"] for ts in res.data) + + # /shows/{pk}/notes/ + query_res = client.get(self._url("notes") + f"?show={self.show_beatbetrieb.id}") + route_res = client.get(self._url("shows", self.show_beatbetrieb.id, "notes")) + ids = {n2.id, n3.id} + self.assertEqual(_get_ids(query_res), ids) + self.assertEqual(_get_ids(route_res), ids) + + query_res = client.get(self._url("notes") + f"?show={self.show_musikrotation.id}") + route_res = client.get(self._url("shows", self.show_musikrotation.id, "notes")) + ids = {n1.id} + self.assertEqual(_get_ids(query_res), ids) + self.assertEqual(_get_ids(route_res), ids) + + # /shows/{pk}/timeslots/{pk}/note/ + query_res = client.get(self._url("notes") + f"?timeslot={ts2.id}") + route_res = client.get( + self._url("shows", self.show_beatbetrieb.id, "timeslots", ts2.id, "note") + ) + ids = {n2.id} + self.assertEqual(_get_ids(query_res), ids) + self.assertEqual(_get_ids(route_res), ids)