[tests] Add efficiency tests for scrobbles

This commit is contained in:
2026-03-21 14:09:21 -04:00
parent 3a02bcad9d
commit 7666958974

View File

@ -0,0 +1,196 @@
import pytest
from django.contrib.auth import get_user_model
from django.test.utils import CaptureQueriesContext
from django.db import connection
from django.utils import timezone
from music.aggregators import batch_live_charts, live_charts
from scrobbles.models import Scrobble, Track, Artist
from videos.models import Video
User = get_user_model()
@pytest.mark.django_db
def test_scrobble_queryset_with_related_reduces_queries():
"""Test that Scrobble.objects.with_related() reduces query count."""
user = User.objects.create_user(username="perfuser", password="testpass123")
artist = Artist.objects.create(name="Test Artist")
track = Track.objects.create(
title="Test Track", artist=artist, base_run_time_seconds=180
)
num_scrobbles = 10
for i in range(num_scrobbles):
Scrobble.objects.create(
user=user,
track=track,
media_type=Scrobble.MediaType.TRACK,
played_to_completion=True,
timestamp=timezone.now() - timezone.timedelta(days=i),
)
with CaptureQueriesContext(connection) as context:
scrobbles = list(Scrobble.objects.with_related().filter(user=user))
for s in scrobbles:
_ = s.track.title
_ = s.track.artist.name
print(
f"\nwith_related() queries for {num_scrobbles} scrobbles: {len(context.captured_queries)}"
)
for q in context.captured_queries:
print(f" {q['sql'][:100]}...")
assert len(context.captured_queries) < num_scrobbles
@pytest.mark.django_db
def test_batch_live_charts_vs_individual_calls():
"""Test that batch_live_charts uses fewer queries than individual live_charts calls."""
user = User.objects.create_user(username="batchuser", password="testpass123")
artist = Artist.objects.create(name="Test Artist")
track = Track.objects.create(
title="Test Track", artist=artist, base_run_time_seconds=180
)
for i in range(5):
Scrobble.objects.create(
user=user,
track=track,
media_type=Scrobble.MediaType.TRACK,
played_to_completion=True,
timestamp=timezone.now() - timezone.timedelta(days=i),
)
with CaptureQueriesContext(connection) as batch_context:
batch_result = batch_live_charts(user=user, media_type="Track", limit=15)
with CaptureQueriesContext(connection) as individual_context:
individual_result = {
"today": list(
live_charts(user=user, media_type="Track", chart_period="today")
),
"last7": list(
live_charts(user=user, media_type="Track", chart_period="last7")
),
"last30": list(
live_charts(user=user, media_type="Track", chart_period="last30")
),
"year": list(
live_charts(user=user, media_type="Track", chart_period="year")
),
"all": list(live_charts(user=user, media_type="Track", chart_period="all")),
}
print(f"\nbatch_live_charts queries: {len(batch_context.captured_queries)}")
print(f"individual live_charts queries: {len(individual_context.captured_queries)}")
assert len(batch_context.captured_queries) <= len(
individual_context.captured_queries
)
@pytest.mark.django_db
def test_as_dict_by_type_uses_with_related():
"""Test that as_dict_by_type uses with_related() for reduced queries."""
user = User.objects.create_user(username="dictuser", password="testpass123")
artist = Artist.objects.create(name="Test Artist")
track = Track.objects.create(
title="Test Track", artist=artist, base_run_time_seconds=180
)
for i in range(5):
Scrobble.objects.create(
user=user,
track=track,
media_type=Scrobble.MediaType.TRACK,
played_to_completion=True,
timestamp=timezone.now() - timezone.timedelta(days=i),
)
scrobbles = Scrobble.objects.filter(user=user)
with CaptureQueriesContext(connection) as context:
result = Scrobble.as_dict_by_type(scrobbles)
print(f"\nas_dict_by_type queries: {len(context.captured_queries)}")
for q in context.captured_queries:
print(f" {q['sql'][:100]}...")
assert len(context.captured_queries) < 10
assert "Track" in result
assert len(result["Track"]) == 5
@pytest.mark.django_db
def test_person_scrobble_junction_efficient_count():
"""Test that PersonScrobble junction table enables efficient counts."""
from people.models import Person, PersonScrobble
creator = User.objects.create_user(username="creator2", password="testpass123")
other_user = User.objects.create_user(username="other2", password="testpass123")
person = Person.objects.create(name="Test Person", created_by=creator, user=creator)
artist = Artist.objects.create(name="Test Artist")
track = Track.objects.create(
title="Test Track", artist=artist, base_run_time_seconds=180
)
for i in range(10):
scrobble = Scrobble.objects.create(
user=other_user,
track=track,
media_type=Scrobble.MediaType.TRACK,
played_to_completion=True,
log={"with_people_ids": [person.id]},
)
count = person.get_scrobble_count(user=other_user)
assert count == 10
with CaptureQueriesContext(connection) as context:
assert person.get_scrobble_count(user=other_user) == 10
print(f"\nPersonScrobble count queries: {len(context.captured_queries)}")
for q in context.captured_queries:
print(f" {q['sql'][:100]}...")
assert len(context.captured_queries) <= 3
@pytest.mark.django_db
def test_video_scrobbles_with_related():
"""Test prefetching works for video scrobbles."""
user = User.objects.create_user(username="videouser", password="testpass123")
video = Video.objects.create(
title="Test Video",
video_type=Video.VideoType.MOVIE,
base_run_time_seconds=5400,
)
for i in range(3):
Scrobble.objects.create(
user=user,
video=video,
media_type=Scrobble.MediaType.VIDEO,
played_to_completion=True,
timestamp=timezone.now() - timezone.timedelta(days=i),
)
with CaptureQueriesContext(connection) as context:
scrobbles = list(Scrobble.objects.with_related().filter(user=user))
for s in scrobbles:
_ = s.video.title
_ = s.video.video_type
print(f"\nVideo scrobbles with_related() queries: {len(context.captured_queries)}")
for q in context.captured_queries:
print(f" {q['sql'][:100]}...")
assert len(context.captured_queries) < 5