This commit has no recorded session.
diff
commit ed2ed59f655e6994a03cc453085d207c41ab3cf9Author: Colin Powell <colin@unbl.ink>Date: Tue Jun 9 12:48:01 2026 -0400 [scrobbles] Fix tests around visbility@@ -88,7 +88,7 @@ fetching and simple saving. *** Metadata sources **** Scraper -* Backlog [0/14] :vrobbler:project:personal:+* Backlog [1/16] :vrobbler:project:personal: ** TODO [#C] Create small utility to clean up tracks scrobbled with wonky playback times :vrobbler:personal:bug:music:scrobbles: :PROPERTIES: :ID: 702462cf-d54b-48c6-8a7c-78b8de751deb@@ -534,7 +534,27 @@ async with the POST data stored in the log["raw_data"] and used by the celery en to go try to enrich the media instance. Should this enrichment fail, tag the scrobble as "enrichment-failed" log a warning and move on. +** TODO [#A] Allow updating all a user's scrobble visibility at once :scrobbles:sharing:feature:+:PROPERTIES:+:ID: 9ed2ec65-bf69-4300-965c-6a7d3ef7ea03+:END:++*** Description++We now have the ability to share or unshare scrobbles and create private links. We should add a toggle+in the user's settings that will bulk make all their scrobbles public or private, so that a user+can either share everything, or lock their account down.++This should not affect scrobbles that are in the "Shared" visibility state.++Additionally, users's should have links in their settings to see what scrobbles+are either public, shared or private. Probably this could be done with a+?visbility=<> filter on the /scrobbles/ page. +** DONE [#A] Fix broken tests with new sharing and add tests :scrobbles:sharing:tests:+:PROPERTIES:+:ID: 10ecd169-eaee-8554-d4ee-f1d34bfad99f+:END: * Version 48.3 [1/1] ** DONE [#A] Fix bug in missing sqids dep :dependencies:project: :PROPERTIES:@@ -8,7 +8,8 @@ from django.urls import reverse from django.utils import timezone from music.models import Album, Artist, Track from podcasts.models import PodcastEpisode-from scrobbles.models import Scrobble+from scrobbles.models import Scrobble, ShareViewLog+from scrobbles.sqids import encode_scrobble_share from tasks.models import Task @@ -512,6 +513,7 @@ def test_scrobble_detail_view_with_notes_as_flat_list(client): task=task, media_type="Task", user=user,+ visibility="public", log={ "notes": ["First note", "Second note"], "description": "Test description",@@ -534,6 +536,7 @@ def test_scrobble_detail_view_with_notes_as_dict_timestamps(client): task=task, media_type="Task", user=user,+ visibility="public", log={ "notes": [ {"2024-01-01 10:00:00": "Note at first timestamp"},@@ -562,6 +565,7 @@ def test_scrobble_detail_view_with_notes_and_labels(client): task=task, media_type="Task", user=user,+ visibility="public", log={ "notes": [ {"2024-01-01 10:00:00": "Note with label"},@@ -739,3 +743,293 @@ def test_gps_webhook_creates_location(client, valid_auth_token): ) assert response.status_code == 200 assert "scrobble_id" in response.data+++@pytest.mark.django_db+def test_share_view_shared_visibility(client):+ user = get_user_model().objects.create_user(+ username="shareuser", password="testpass"+ )+ task = Task.objects.create(title="Test Task")+ scrobble = Scrobble.objects.create(+ task=task, media_type="Task", user=user, visibility="shared",+ timestamp=timezone.now(),+ )+ url = reverse(+ "scrobbles:shared-detail",+ kwargs={"sqid": encode_scrobble_share(scrobble.id, scrobble.share_token_version)},+ )+ response = client.get(url)+ assert response.status_code == 200+++@pytest.mark.django_db+def test_share_view_public_visibility(client):+ user = get_user_model().objects.create_user(+ username="shareuser2", password="testpass"+ )+ task = Task.objects.create(title="Test Task")+ scrobble = Scrobble.objects.create(+ task=task, media_type="Task", user=user, visibility="public",+ timestamp=timezone.now(),+ )+ url = reverse(+ "scrobbles:shared-detail",+ kwargs={"sqid": encode_scrobble_share(scrobble.id, scrobble.share_token_version)},+ )+ response = client.get(url)+ assert response.status_code == 200+++@pytest.mark.django_db+def test_share_view_private_visibility_returns_404(client):+ user = get_user_model().objects.create_user(+ username="shareuser3", password="testpass"+ )+ task = Task.objects.create(title="Test Task")+ scrobble = Scrobble.objects.create(+ task=task, media_type="Task", user=user, visibility="private",+ timestamp=timezone.now(),+ )+ url = reverse(+ "scrobbles:shared-detail",+ kwargs={"sqid": encode_scrobble_share(scrobble.id, scrobble.share_token_version)},+ )+ response = client.get(url)+ assert response.status_code == 404+++@pytest.mark.django_db+def test_share_view_invalid_sqid_returns_404(client):+ url = reverse("scrobbles:shared-detail", kwargs={"sqid": "InvalidSqid123"})+ response = client.get(url)+ assert response.status_code == 404+++@pytest.mark.django_db+def test_share_view_expired_token_returns_404(client):+ user = get_user_model().objects.create_user(+ username="shareuser4", password="testpass"+ )+ task = Task.objects.create(title="Test Task")+ scrobble = Scrobble.objects.create(+ task=task, media_type="Task", user=user, visibility="shared",+ timestamp=timezone.now(),+ )+ old_sqid = encode_scrobble_share(scrobble.id, scrobble.share_token_version)+ scrobble.regenerate_share_token()+ url = reverse("scrobbles:shared-detail", kwargs={"sqid": old_sqid})+ response = client.get(url)+ assert response.status_code == 404+++@pytest.mark.django_db+def test_share_view_increments_count_and_logs_view(client):+ user = get_user_model().objects.create_user(+ username="shareuser5", password="testpass"+ )+ task = Task.objects.create(title="Test Task")+ scrobble = Scrobble.objects.create(+ task=task, media_type="Task", user=user, visibility="shared",+ timestamp=timezone.now(),+ )+ assert scrobble.share_view_count == 0++ url = reverse(+ "scrobbles:shared-detail",+ kwargs={"sqid": encode_scrobble_share(scrobble.id, scrobble.share_token_version)},+ )+ response = client.get(url)+ assert response.status_code == 200++ scrobble.refresh_from_db()+ assert scrobble.share_view_count == 1+ assert ShareViewLog.objects.filter(scrobble=scrobble).count() == 1++ log_entry = ShareViewLog.objects.filter(scrobble=scrobble).first()+ assert log_entry.ip_address == "127.0.0.1"+ assert log_entry.user_agent == ""+ assert log_entry.referrer == ""+++@pytest.mark.django_db+def test_explore_view_shows_only_public_scrobbles(client):+ user = get_user_model().objects.create_user(+ username="exploreuser", password="testpass"+ )+ public_task = Task.objects.create(title="Public Task Title")+ shared_task = Task.objects.create(title="Shared Task Title")+ private_task = Task.objects.create(title="Private Task Title")+ ts = timezone.now()+ public_scrobble = Scrobble.objects.create(+ task=public_task, media_type="Task", user=user, visibility="public",+ timestamp=ts,+ )+ Scrobble.objects.create(+ task=shared_task, media_type="Task", user=user, visibility="shared",+ timestamp=ts,+ )+ Scrobble.objects.create(+ task=private_task, media_type="Task", user=user, visibility="private",+ timestamp=ts,+ )++ url = reverse("scrobbles:explore")+ response = client.get(url)+ assert response.status_code == 200++ content = response.content.decode()+ assert "Public Task Title" in content+ assert "Shared Task Title" not in content+ assert "Private Task Title" not in content+++@pytest.mark.django_db+def test_change_visibility_owner_can_change(client):+ user = get_user_model().objects.create_user(+ username="visuser", password="testpass"+ )+ task = Task.objects.create(title="Test Task")+ scrobble = Scrobble.objects.create(+ task=task, media_type="Task", user=user, visibility="private",+ timestamp=timezone.now(),+ )++ client.force_login(user)+ url = reverse("scrobbles:change-visibility", kwargs={"uuid": scrobble.uuid})+ response = client.post(url, {"visibility": "shared"})+ assert response.status_code == 302++ scrobble.refresh_from_db()+ assert scrobble.visibility == "shared"+++@pytest.mark.django_db+def test_change_visibility_non_owner_gets_404(client):+ owner = get_user_model().objects.create_user(+ username="owner", password="testpass"+ )+ other = get_user_model().objects.create_user(+ username="other", password="testpass"+ )+ task = Task.objects.create(title="Test Task")+ scrobble = Scrobble.objects.create(+ task=task, media_type="Task", user=owner, visibility="private",+ timestamp=timezone.now(),+ )++ client.force_login(other)+ url = reverse("scrobbles:change-visibility", kwargs={"uuid": scrobble.uuid})+ response = client.post(url, {"visibility": "shared"})+ assert response.status_code == 404++ scrobble.refresh_from_db()+ assert scrobble.visibility == "private"+++@pytest.mark.django_db+def test_change_visibility_anonymous_redirects_to_login(client):+ user = get_user_model().objects.create_user(+ username="anontest", password="testpass"+ )+ task = Task.objects.create(title="Test Task")+ scrobble = Scrobble.objects.create(+ task=task, media_type="Task", user=user, visibility="private",+ timestamp=timezone.now(),+ )+ url = reverse("scrobbles:change-visibility", kwargs={"uuid": scrobble.uuid})+ response = client.post(url, {"visibility": "shared"})+ assert response.status_code == 302+ assert "/login/" in response.url+++@pytest.mark.django_db+def test_regenerate_share_token_invalidates_old_sqid(client):+ user = get_user_model().objects.create_user(+ username="regentest", password="testpass"+ )+ task = Task.objects.create(title="Test Task")+ scrobble = Scrobble.objects.create(+ task=task, media_type="Task", user=user, visibility="shared",+ timestamp=timezone.now(),+ )+ old_sqid = encode_scrobble_share(scrobble.id, scrobble.share_token_version)++ client.force_login(user)+ url = reverse("scrobbles:regenerate-share-token", kwargs={"uuid": scrobble.uuid})+ response = client.post(url)+ assert response.status_code == 302++ scrobble.refresh_from_db()+ assert scrobble.share_token_version == 1++ old_url = reverse("scrobbles:shared-detail", kwargs={"sqid": old_sqid})+ old_response = client.get(old_url)+ assert old_response.status_code == 404++ new_sqid = encode_scrobble_share(scrobble.id, scrobble.share_token_version)+ new_url = reverse("scrobbles:shared-detail", kwargs={"sqid": new_sqid})+ new_response = client.get(new_url)+ assert new_response.status_code == 200+++@pytest.mark.django_db+def test_share_analytics_owner_can_view(client):+ user = get_user_model().objects.create_user(+ username="analyticsuser", password="testpass"+ )+ task = Task.objects.create(title="Test Task")+ scrobble = Scrobble.objects.create(+ task=task, media_type="Task", user=user, visibility="shared",+ timestamp=timezone.now(),+ )++ client.force_login(user)+ url = reverse("scrobbles:share-analytics", kwargs={"uuid": scrobble.uuid})+ response = client.get(url)+ assert response.status_code == 200+++@pytest.mark.django_db+def test_share_analytics_non_owner_gets_404(client):+ owner = get_user_model().objects.create_user(+ username="analyticsowner", password="testpass"+ )+ other = get_user_model().objects.create_user(+ username="analyticsother", password="testpass"+ )+ task = Task.objects.create(title="Test Task")+ scrobble = Scrobble.objects.create(+ task=task, media_type="Task", user=owner, visibility="shared",+ timestamp=timezone.now(),+ )++ client.force_login(other)+ url = reverse("scrobbles:share-analytics", kwargs={"uuid": scrobble.uuid})+ response = client.get(url)+ assert response.status_code == 404+++@pytest.mark.django_db+def test_share_analytics_shows_view_logs(client):+ user = get_user_model().objects.create_user(+ username="analyticsviews", password="testpass"+ )+ task = Task.objects.create(title="Test Task")+ scrobble = Scrobble.objects.create(+ task=task, media_type="Task", user=user, visibility="shared",+ timestamp=timezone.now(),+ )++ sqid = encode_scrobble_share(scrobble.id, scrobble.share_token_version)+ share_url = reverse("scrobbles:shared-detail", kwargs={"sqid": sqid})+ client.get(share_url)+ client.get(share_url)++ client.force_login(user)+ url = reverse("scrobbles:share-analytics", kwargs={"uuid": scrobble.uuid})+ response = client.get(url)+ assert response.status_code == 200+ content = response.content.decode()+ assert "127.0.0.1" in content+ assert ShareViewLog.objects.filter(scrobble=scrobble).count() == 2