diff --git a/program/tests/test_calendar_program.py b/program/tests/test_calendar_program.py
new file mode 100644
index 0000000000000000000000000000000000000000..4b2654729ffcce8d07cec494a7512e99602fcc19
--- /dev/null
+++ b/program/tests/test_calendar_program.py
@@ -0,0 +1,118 @@
+import pytest
+
+from conftest import create_daily_schedule
+
+pytestmark = pytest.mark.django_db
+
+
+def url(include_virtual=False, start=None, end=None):
+    if include_virtual and start and end:
+        return f"/api/v1/program/calendar/?include_virtual=true&start={start}&end={end}"
+    elif start and end:
+        return f"/api/v1/program/calendar/?start={start}&end={end}"
+    elif include_virtual:
+        return "/api/v1/program/calendar/?include_virtual=true"
+    else:
+        return "/api/v1/program/calendar/"
+
+
+def assert_episodes(episodes) -> None:
+    """asserts the episodes are valid."""
+
+    assert len(episodes) == 1
+
+    assert episodes[0]["id"]
+    assert episodes[0]["timeslotId"]
+
+
+def assert_program(program, show, fallback_show=None) -> None:
+    """asserts the program are valid and correspond to the given show and fallback show."""
+
+    assert len(program) == 1 if fallback_show is None else 3
+
+    assert program[0]["end"]
+    assert program[0]["id"]
+    assert program[0]["start"]
+
+    if fallback_show is None:
+        assert program[0]["showId"] == show.id
+        assert program[0]["timeslotId"]
+    else:
+        assert program[0]["showId"] == fallback_show.id
+
+        assert program[1]["showId"] == show.id
+        assert program[1]["timeslotId"]
+
+
+def assert_shows(shows, show, fallback_show=None) -> None:
+    """asserts the shows are valid correspond to the given show and fallback show."""
+
+    assert len(shows) == 1 if fallback_show is None else 2
+
+    if fallback_show is None:
+        assert shows[0]["id"] == show.id
+        assert shows[0]["name"] == show.name
+    else:
+        assert shows[0]["id"] == show.id
+        assert shows[0]["name"] == show.name
+
+        assert shows[1]["id"] == fallback_show.id
+        assert shows[1]["name"] == fallback_show.name
+
+
+def assert_timeslots(timeslots, show) -> None:
+    """asserts the timeslots are valid correspond to the given show."""
+
+    assert len(timeslots) == 1
+
+    assert timeslots[0]["id"]
+    assert timeslots[0]["noteId"]
+    assert timeslots[0]["end"]
+    assert timeslots[0]["start"]
+    assert timeslots[0]["showId"] == show.id
+
+
+def test_calendar(admin_api_client, api_client, daily_rrule, show):
+    create_daily_schedule(admin_api_client, daily_rrule, show)
+
+    response = api_client.get(url())
+
+    assert response.status_code == 200
+
+    for key, value in response.json().items():
+        match key:
+            case "episodes":
+                assert_episodes(value)
+            case "program":
+                assert_program(value, show)
+            case "shows":
+                assert_shows(value, show)
+            case "timeslots":
+                assert_timeslots(value, show)
+
+
+def test_calendar_include_virtual(
+    admin_api_client,
+    api_client,
+    daily_rrule,
+    show,
+    fallback_show,
+    radio_settings,
+):
+    create_daily_schedule(admin_api_client, daily_rrule, show)
+
+    response = api_client.get(url(include_virtual=True))
+
+    assert response.status_code == 200
+
+    for key, value in response.json().items():
+        match key:
+            case "episodes":
+                assert_episodes(value)
+            case "program":
+                print(value)
+                assert_program(value, show, fallback_show)
+            case "shows":
+                assert_shows(value, show, fallback_show)
+            case "timeslots":
+                assert_timeslots(value, show)