Files
vrobbler/tests/scrobbles_tests/test_home.py
Colin Powell 529e6fdfaf
Some checks failed
ci / test (push) Failing after 1m53s
ci / build-and-deploy (push) Has been skipped
[release] Bump to version 66.0
- Do a deep dive on making homepage clearer and more efficient
2026-08-24 10:16:15 -04:00

196 lines
6.3 KiB
Python

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</span>" 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</span>" not in content