site lemmy.test -s -X "$method""$LM/api/v4/$path" -H "Authorization: Bearer $LT"
fi
}
# the posts Lemmy holds in a community, and the one whose ap_id is given
lm_posts(){ lm GET "post/list?community_id=$1&sort=new&limit=50"| j "print(json.dumps([p['post'] for p in (d.get('items') or d.get('posts') or [])]))";}
lm POST community/follow "{\"community_id\":$dogs_on_lm,\"follow\":true}" >/dev/null
until_true 30'[ "$(lm GET "community?id=$dogs_on_lm" | j "print(((d[\"community_view\"].get(\"community_actions\") or {}).get(\"follow_state\") or \"\").lower())")" = "accepted" ]'\
&& ok "lemmyuser follows the PrivaPub community (Accept arrived)"|| ko "Lemmy's community follow not accepted"
cats_on_lm=$(lm POST community "{\"name\":\"$cats\",\"title\":\"Pasture cats\"}"| j "print(d['community_view']['community']['id'])")
[ -n "$cats_on_p"]&& ok "PrivaPub resolves a Lemmy community"|| ko "PrivaPub cannot resolve the Lemmy community"
curl -s -o /dev/null -X POST -H "$LAH""$P/api/v1/accounts/$cats_on_p/follow"
until_true 30'[ "$(curl -s -H "$LAH" "$P/api/v1/accounts/relationships?id[]=$cats_on_p" | j "print(d[0][\"following\"])")" = "True" ]'&& ok "alice follows the Lemmy community (Accept arrived)"|| ko "PrivaPub's community follow not accepted"
echo" threads"
lm_thread=$(lm POST post "{\"name\":\"A Lemmy thread\",\"body\":\"posted from Lemmy into PrivaPub\",\"community_id\":$dogs_on_lm}"| j "print(d['post_view']['post']['ap_id'])")
until_true 30'[ "$(curl -s -H "$LAH" "$P/api/v1/accounts/$dogs_on_p/statuses" | j "print(any(s[\"uri\"]==\"$lm_thread\" and (s.get(\"privapub\") or {}).get(\"title\")==\"A Lemmy thread\" for s in d))")" = "True" ]'\
&& ok "a Lemmy thread in the PrivaPub community arrives with its title"|| ko "Lemmy's thread missing or untitled on PrivaPub"
&& ok "the PrivaPub community's announce back publishes Lemmy's thread there"|| ko "Lemmy's thread stays federation_pending"
lm_cats_thread=$(lm POST post "{\"name\":\"Cats only\",\"body\":\"a Lemmy community post\",\"community_id\":$cats_on_lm}"| j "print(d['post_view']['post']['ap_id'])")
until_true 30'curl -s -H "$LAH" "$P/api/v1/timelines/home" | j "print(any(s[\"uri\"]==\"$lm_cats_thread\" or (s.get(\"reblog\") or {}).get(\"uri\")==\"$lm_cats_thread\" for s in d))" | grep -q True'\
&& ok "the Lemmy community's announce brings its thread to alice's home"|| ko "Lemmy community thread missing from alice's home"
p_thread=$(curl -s -X POST -H "$LAH"$P/api/v1/statuses --data-urlencode "status=A PrivaPub thread
@$cats@lemmy.test posted from PrivaPub" -d 'visibility=public')
until_true 30'[ "$(lm_post_by_ap "$cats_on_lm" "$p_thread_uri")" != "null" ]'&& ok "alice's thread lands in the Lemmy community"|| ko "PrivaPub thread missing from the Lemmy community"
lm POST comment "{\"post_id\":$p_thread_on_lm,\"content\":\"a Lemmy comment on PrivaPub\"}" >/dev/null
until_true 30'[ "$(curl -s -H "$LAH" "$P/api/v1/statuses/$p_thread_id/context" | j "print(any(\"a Lemmy comment on PrivaPub\" in s[\"content\"] for s in d[\"descendants\"]))")" = "True" ]'\
&& ok "Lemmy's comment threads under alice's post"|| ko "Lemmy's comment missing on PrivaPub"
lm_cats_on_p=$(curl -s -H "$LAH""$P/api/v1/timelines/home"| j "print(next((s.get('reblog') or s)['id'] for s in d if (s.get('reblog') or s)['uri']=='$lm_cats_thread'))")
curl -s -o /dev/null -X POST -H "$LAH"$P/api/v1/statuses -d "status=a PrivaPub comment on Lemmy&in_reply_to_id=$lm_cats_on_p&visibility=public"
lm_cats_id=$(lm GET "resolve_object?q=$lm_cats_thread"| j "print(d['post']['id'])")
until_true 30'[ "$(lm GET "comment/list?post_id=$lm_cats_id" | j "print(any(\"a PrivaPub comment on Lemmy\" in c[\"comment\"][\"content\"] for c in (d.get(\"items\") or d.get(\"comments\") or [])))")" = "True" ]'\
&& ok "alice's reply becomes a comment on Lemmy"|| ko "PrivaPub reply missing as a Lemmy comment"
curl -s -o /dev/null -X POST -H "$LAH""$P/api/v1/statuses/$lm_cats_on_p/favourite"
until_true 30'[ "$(lm GET "post?id=$lm_cats_id" | j "print(d[\"post_view\"][\"post\"].get(\"upvotes\"))")" = "2" ]'&& ok "alice's like is an upvote on Lemmy"|| ko "like not counted as an upvote on Lemmy"
echo" private messages"
alice_on_lm=$(lm GET "resolve_object?q=@alice_lemmy@privapub.test"| j "print(d['person']['id'])")
lm POST private_message "{\"content\":\"a secret from Lemmy\",\"recipient_id\":$alice_on_lm}" >/dev/null
until_true 30'curl -s -H "$LAH" "$P/api/v1/conversations" | grep -q "a secret from Lemmy"'&& ok "Lemmy's private message arrives as a DM"|| ko "Lemmy's private message missing on PrivaPub"
curl -s -o /dev/null -X POST -H "$LAH"$P/api/v1/statuses -d 'status=@lemmyuser@lemmy.test a secret from PrivaPub&visibility=direct'
until_true 30'lm GET "account/notification/list?limit=50" | grep -q "a secret from PrivaPub"'&& ok "alice's DM arrives as a Lemmy private message"|| ko "DM missing on Lemmy"
lm POST post/lock "{\"post_id\":$lm_cats_id,\"locked\":true,\"reason\":\"pasture\"}" >/dev/null
until_true 45'[ "$(p_locked)" = "True" ]'&& ok "a moderator's lock reaches PrivaPub"|| ko "a moderator's lock did not reach PrivaPub"
["$(p_reply "too late")"="422"]&& ok "a reply to the locked thread is refused"|| ko "a reply to the locked thread was taken"
lm POST post/lock "{\"post_id\":$lm_cats_id,\"locked\":false,\"reason\":\"pasture\"}" >/dev/null
until_true 45'[ "$(p_locked)" = "False" ]'&& ok "a moderator's unlock reaches PrivaPub"|| ko "a moderator's unlock did not reach PrivaPub"
lm POST community/ban_user "{\"community_id\":$cats_on_lm,\"person_id\":$alice_on_lm,\"ban\":true,\"reason\":\"pasture\"}" >/dev/null
until_true 45'[ "$(p_banned)" = "True" ]'&& ok "the community's ban of alice_lemmy reaches PrivaPub (blocked_by)"|| ko "the community's ban did not reach PrivaPub"
["$(p_reply "while banned")"="422"]&& ok "a banned persona's reply in the community is refused"|| ko "a banned persona's reply was taken"
lm POST community/ban_user "{\"community_id\":$cats_on_lm,\"person_id\":$alice_on_lm,\"ban\":false,\"reason\":\"pasture\"}" >/dev/null
until_true 45'[ "$(p_banned)" = "False" ]'&& ok "the community's unban reaches PrivaPub"|| ko "the community's unban did not reach PrivaPub"
# Lemmy takes a report only from a person or a service, about one post or comment, addressed to its community (owner
# decision 2026-10-06): alice's report of lemmyuser's thread and comment leaves from PrivaPub's reporter, one Flag each,
# and never names her. Lemmy sends nothing back; what it keeps is read from its database.
lm POST comment "{\"post_id\":$lm_cats_id,\"content\":\"a Lemmy comment to report\"}" >/dev/null
lm_comment_on_p(){ curl -s -H "$LAH""$P/api/v1/statuses/$lm_cats_on_p/context"| j "print(next((s['id'] for s in d['descendants'] if 'a Lemmy comment to report' in s['content']), ''))";}
until_true 45'[ -n "$(lm_comment_on_p)" ]'&& ok "lemmyuser's comment to report reaches PrivaPub"|| ko "lemmyuser's comment never reached PrivaPub"
&& ok "PrivaPub says alice's report was forwarded"|| ko "PrivaPub did not forward alice's report"
lm_reports(){ podman exec pasture-postgres psql -U pasture -d lemmy -tAc "select count(*) from $1 r join person p on p.id = r.creator_id where r.reason = '$reason' and p.ap_id = '$P_REPORTER'";}
until_true 45'[ "$(lm_reports post_report)" = "1" ]'&& ok "Lemmy keeps the thread's report from PrivaPub's reporter, with alice's words"|| ko "Lemmy kept no report of the thread"
until_true 45'[ "$(lm_reports comment_report)" = "1" ]'&& ok "Lemmy keeps the comment's report from PrivaPub's reporter, with alice's words"|| ko "Lemmy kept no report of the comment"
["$(podman exec pasture-postgres psql -U pasture -d lemmy -tAc "select count(*) from post_report r join person p on p.id = r.creator_id where p.ap_id like '%alice_lemmy%'")"="0"]\
&& ok "no report on Lemmy names alice"|| ko "a report on Lemmy names alice"
until_true 45'curl -s -H "$LAH" "$P/api/v1/timelines/home" | j "print(any((s.get(\"reblog\") or s)[\"uri\"]==\"$lm_cats_thread\" for s in d))" | grep -q False'\
&& ok "a moderator's removal reaches PrivaPub"|| ko "a moderator's removal did not reach PrivaPub"