From 1c920a9c5993b9af79e648efa8ad895ea5789378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aar=C3=B3n=20Ayerdis=20Espinoza?= Date: Mon, 5 Oct 2026 15:00:58 -0600 Subject: [PATCH 1/2] Listing the owner's non-public likes in the liked collection --- testbed/core/tests/test_api.py | 15 +++++++++++++++ testbed/core/views/api.py | 6 +----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/testbed/core/tests/test_api.py b/testbed/core/tests/test_api.py index edf0253..52f1782 100644 --- a/testbed/core/tests/test_api.py +++ b/testbed/core/tests/test_api.py @@ -569,3 +569,18 @@ def test_liked_items_are_the_like_objects(basic_auth_context, mock_request): {"@context": [ACTIVITY_STREAM_CONTEXT, PREVIOUSLY_TERM], **remote_data, "id": remote.object_url}, ] assert b"AUTHOR PRIVATE CONTENT" not in response.content + + +# LOLA §6.4: the owner's token reads the owner's whole liked collection, whatever each Like's visibility +@pytest.mark.django_db +def test_liked_lists_the_owners_non_public_likes(): + owner = IsolatedActorFactory(prefix="liked_all") + likes = [ + LikeActivityFactory(actor=owner, remote=True, visibility=visibility) + for visibility in ("public", "followers-only", "private") + ] + + response = lola_client(owner).get(reverse("liked-collection", kwargs={"pk": owner.id})) + + listed = {item["id"] for item in response.data["orderedItems"]} + assert listed == {like.object_url for like in likes} diff --git a/testbed/core/views/api.py b/testbed/core/views/api.py index 2c7a087..8264525 100644 --- a/testbed/core/views/api.py +++ b/testbed/core/views/api.py @@ -212,13 +212,9 @@ def liked_collection(request, pk, actor): The objects of the actor's Likes (ActivityPub §5.5), each built by build_like_object_json_ld: the same object the Like activity carries, so a non-public local Note is served as its id only. """ - # Get all LikeActivity objects for this actor in reverse chronological order + # Every Like the actor made, whatever its visibility. The strict gate above admits only this actor's bound token likes_qs = LikeActivity.objects.filter(actor=actor).order_by("-timestamp", "-id") - # Apply visibility filtering - only include likes of public objects for privacy - # TODO: This could be enhanced with trust controls - likes_qs = likes_qs.filter(visibility="public") - # Build standardized authentication context for JSON-LD building auth_context = build_auth_context(request) From 096947fbeb215394bdb463c1cc25967368fd6a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aar=C3=B3n=20Ayerdis=20Espinoza?= Date: Mon, 5 Oct 2026 15:06:00 -0600 Subject: [PATCH 2/2] Provisioning likes across all three visibilities --- testbed/core/tests/test_provisioning.py | 15 +++++++++++++-- testbed/core/utils/provisioning.py | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/testbed/core/tests/test_provisioning.py b/testbed/core/tests/test_provisioning.py index 9542920..efa5c12 100644 --- a/testbed/core/tests/test_provisioning.py +++ b/testbed/core/tests/test_provisioning.py @@ -7,9 +7,9 @@ from django.urls import reverse from rest_framework.test import APIClient -from testbed.core.models import Actor, Blocked, Following, Note +from testbed.core.models import Actor, Blocked, Following, LikeActivity, Note from testbed.core.tests.helpers import lola_client, source_actor_for -from testbed.core.utils.provisioning import provision_actor_content +from testbed.core.utils.provisioning import LIKE_COUNT, VISIBILITIES, provision_actor_content # production refuses `seed`, so what a signup provisions for itself is all its collections will ever hold @@ -37,6 +37,17 @@ def test_provisioned_notes_span_months(): assert max(published) - min(published) > timedelta(days=90) +# Provisioned Likes span every visibility. A new account exercises the owner's full liked collection (LOLA §6.4) +@pytest.mark.django_db +def test_every_provisioned_like_reaches_its_owner(): + actor = source_actor_for() + visibilities = set(LikeActivity.objects.filter(actor=actor).values_list("visibility", flat=True)) + liked = lola_client(actor).get(reverse("liked-collection", kwargs={"pk": actor.pk})).data + + assert visibilities == set(VISIBILITIES) + assert liked["totalItems"] == LIKE_COUNT + + @pytest.mark.django_db def test_scope_unlocks_more_of_the_outbox(): actor = source_actor_for() diff --git a/testbed/core/utils/provisioning.py b/testbed/core/utils/provisioning.py index c347237..c34d564 100644 --- a/testbed/core/utils/provisioning.py +++ b/testbed/core/utils/provisioning.py @@ -82,7 +82,7 @@ def provision_actor_content(actor): likes.append(LikeActivity( actor=actor, timestamp=liked_at, - visibility="public", + visibility=VISIBILITIES[(number - 1) % len(VISIBILITIES)], object_url=f"{author_url}/notes/{number}", object_data={ "type": "Note",