import pytest from django.contrib.auth import get_user_model from django.core.cache import cache from django.core.cache.utils import make_template_fragment_key from django.db import connection from django.test.utils import CaptureQueriesContext from django.utils import timezone from music.models import Artist, Track from scrobbles.models import Scrobble from tasks.models import Task @pytest.fixture(autouse=True) def clear_cache(): cache.clear() yield cache.clear() @pytest.mark.django_db def test_home_renders_summary_and_timeline(client): user = get_user_model().objects.create_user( username="homeuser", password="testpass" ) artist = Artist.objects.create(name="Sublime") track = Track.objects.create(title="Same in the End", artist_fk=artist) task = Task.objects.create(title="Ship the thing") now = timezone.now() Scrobble.objects.create( user=user, track=track, media_type="Track", timestamp=now, played_to_completion=True, ) Scrobble.objects.create( user=user, task=task, media_type="Task", timestamp=now - timezone.timedelta(minutes=10), ) client.force_login(user) response = client.get("/") assert response.status_code == 200 content = response.content.decode() assert "Same in the End" in content assert "Ship the thing" in content assert "Track" in content assert "Task" in content assert "Timeline" in content assert "Details" in content assert "Browse" in content assert "media-browser-menu" in content @pytest.mark.django_db def test_home_does_not_render_empty_types(client): user = get_user_model().objects.create_user( username="homeuser2", password="testpass" ) task = Task.objects.create(title="Only task") now = timezone.now() Scrobble.objects.create(user=user, task=task, media_type="Task", timestamp=now) client.force_login(user) response = client.get("/") content = response.content.decode() assert "Only task" in content assert "No tracks today" not in content assert "No food today" not in content assert "No videos today" not in content @pytest.mark.django_db def test_home_type_section_cached_skips_query(client): user = get_user_model().objects.create_user( username="homecache", password="testpass" ) task = Task.objects.create(title="Cached task") now = timezone.now() Scrobble.objects.create(user=user, task=task, media_type="Task", timestamp=now) client.force_login(user) # First request renders everything (cache cold) with CaptureQueriesContext(connection) as cold: response = client.get("/") assert response.status_code == 200 assert "Cached task" in response.content.decode() # Second request should serve the type-section fragment from cache and # skip its DB query entirely with CaptureQueriesContext(connection) as warm: response = client.get("/") assert response.status_code == 200 assert "Cached task" in response.content.decode() assert len(warm) < len(cold) @pytest.mark.django_db def test_scrobble_save_invalidates_home_fragments(client): user = get_user_model().objects.create_user( username="invalidate", password="testpass" ) task = Task.objects.create(title="First") now = timezone.now() scrobble = Scrobble.objects.create( user=user, task=task, media_type="Task", timestamp=now ) timeline_key = make_template_fragment_key( "home_timeline", [user.id, now.strftime("%Y-%m-%d")] ) type_key = make_template_fragment_key( "home_type_section", [user.id, now.strftime("%Y-%m-%d"), "Task"] ) cache.set(timeline_key, "cached") cache.set(type_key, "cached") assert cache.get(timeline_key) == "cached" assert cache.get(type_key) == "cached" Scrobble.objects.create( user=user, task=Task.objects.create(title="Second"), media_type="Task", timestamp=timezone.now(), ) assert cache.get(timeline_key) is None assert cache.get(type_key) is None @pytest.mark.django_db def test_scrobble_delete_invalidates_home_fragments(client): user = get_user_model().objects.create_user( username="invalidedel", password="testpass" ) task = Task.objects.create(title="Doomed") now = timezone.now() scrobble = Scrobble.objects.create( user=user, task=task, media_type="Task", timestamp=now ) timeline_key = make_template_fragment_key( "home_timeline", [user.id, now.strftime("%Y-%m-%d")] ) cache.set(timeline_key, "cached") assert cache.get(timeline_key) == "cached" scrobble.delete() assert cache.get(timeline_key) is None @pytest.mark.django_db def test_home_week_view_uses_grouped_tables(client): user = get_user_model().objects.create_user( username="homeweek", password="testpass" ) task = Task.objects.create(title="Week task") now = timezone.now() Scrobble.objects.create(user=user, task=task, media_type="Task", timestamp=now) client.force_login(user) response = client.get("/", {"date": "this_week"}) assert response.status_code == 200 content = response.content.decode() assert "Week task" in content assert "Timeline" not in content @pytest.mark.django_db def test_home_media_badges_colored_initials_by_default(client): user = get_user_model().objects.create_user( username="badgeinit", password="testpass" ) task = Task.objects.create(title="Badge task") Scrobble.objects.create( user=user, task=task, media_type="Task", timestamp=timezone.now() ) client.force_login(user) content = client.get("/").content.decode() assert 'class="badge media-badge' in content assert ">T" in content assert "\u2705" not in content @pytest.mark.django_db def test_home_media_badges_use_emojis_when_enabled(client): user = get_user_model().objects.create_user( username="badgeemoji", password="testpass" ) user.profile.timeline_use_emojis = True user.profile.save() task = Task.objects.create(title="Emoji task") Scrobble.objects.create( user=user, task=task, media_type="Task", timestamp=timezone.now() ) client.force_login(user) content = client.get("/").content.decode() assert "\u2705" in content assert ">T" not in content