# Lemmy 1.0: communities both ways (FEP-1b12), threads with titles, comments both ways, votes up and down both ways, # private messages both ways, a moderator's removal (P7), statistics. Lemmy is driven through its v4 API. LM=https://lemmy.test:6443 LT=$(cat "$here/.state/lemmy.token" 2>/dev/null) # lm [json]: a Lemmy v4 API call as lemmyuser lm() { local method=$1 path=$2 body=${3:-} if [ -n "$body" ]; then site lemmy.test -s -X "$method" "$LM/api/v4/$path" -H "Authorization: Bearer $LT" -H 'Content-Type: application/json' -d "$body" else 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_by_ap() { lm_posts "$1" | j "print(json.dumps(next((p for p in d if p['ap_id']=='$2'), None)))"; } echo "lemmy" [ -n "$LT" ] && ok "Lemmy token for lemmyuser" || { ko "Lemmy token"; return 1; } LAT=$(privapub_token alice_lemmy) LAH="Authorization: Bearer $LAT" [ -n "$LAT" ] && ok "PrivaPub token for alice_lemmy" || { ko "PrivaPub token for alice_lemmy"; return 1; } jwt=$(privapub_root) alice_id=$(curl -s -H "$LAH" "$P/api/v1/accounts/verify_credentials" | j "print(d['id'])") dogs="dogs$(date +%s)" cats="cats$(date +%s)" echo " communities" dogs_gid=$(curl -s -X POST $P/clientapi/group/insert -H 'Content-Type: application/json' -H "Authorization: Bearer $jwt" \ -d "{\"avatarId\":\"$alice_id\",\"userName\":\"$dogs\",\"name\":\"Pasture dogs\",\"description\":\"a PrivaPub community\",\"isCommunity\":true}" | j "print(d['id'])") dogs_on_lm=$(lm GET "resolve_object?q=!$dogs@privapub.test" | j "print(d['community']['id'])") [ -n "$dogs_on_lm" ] && ok "Lemmy resolves a PrivaPub community" || ko "Lemmy cannot resolve the PrivaPub community" 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'])") cats_on_p=$(curl -s -H "$LAH" "$P/api/v2/search?q=@$cats@lemmy.test&resolve=true&type=accounts" | j "print(d['accounts'][0]['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'])") dogs_on_p=$(curl -s -H "$LAH" "$P/api/v1/accounts/lookup?acct=$dogs" | j "print(d['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" curl -s -o /dev/null -X POST $P/clientapi/post/insert -H 'Content-Type: application/json' -H "Authorization: Bearer $jwt" \ -d "{\"avatarId\":\"$alice_id\",\"title\":\"A PrivaPub community thread\",\"text\":\"posted into our own community\",\"groupId\":\"$dogs_gid\"}" dogs_post=$(podman exec pasture-mongo mongosh --quiet PrivaPub --eval 'print(db.Post.findOne({Text:/posted into our own community/}, {}, {sort:{_id:-1}}).ObjectURI)') until_true 30 '[ "$(lm_post_by_ap "$dogs_on_lm" "$dogs_post" | j "print(d and d[\"name\"])")" = "A PrivaPub community thread" ]' \ && ok "a titled post in the PrivaPub community reaches Lemmy through its announce" || ko "PrivaPub community post missing on Lemmy" # Lemmy 1.0 holds its user's thread in a remote community as pending until the community announces it back, and clears # the flag even though it then answers that Announce 400 ("Object is not remote"): the echo is what publishes it. until_true 30 '[ "$(lm_post_by_ap "$dogs_on_lm" "$lm_thread" | j "print(d and d[\"federation_pending\"])")" = "False" ]' \ && 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') p_thread_id=$(echo "$p_thread" | j "print(d['id'])"); p_thread_uri=$(echo "$p_thread" | j "print(d['uri'])") 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" p_thread_on_lm=$(lm_post_by_ap "$cats_on_lm" "$p_thread_uri" | j "print(d['id'])") echo " comments" 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" echo " votes" # Lemmy sends a vote only to the community, which relays it inside an Announce: P7 trusts those for the community's own posts. lm POST post/like "{\"post_id\":$p_thread_on_lm,\"is_upvote\":true}" >/dev/null if until_true 15 '[ "$(curl -s -H "$LAH" "$P/api/v1/statuses/$p_thread_id" | j "print(d[\"favourites_count\"])")" = "1" ]'; then ok "Lemmy's upvote counts as a like on PrivaPub" else xf "Lemmy's upvote counts as a like on PrivaPub (votes relayed by a community are P7)" fi lm POST post/like "{\"post_id\":$p_thread_on_lm,\"is_upvote\":false}" >/dev/null if until_true 15 '[ "$(curl -s -H "$LAH" "$P/api/v1/statuses/$p_thread_id" | j "print(d[\"favourites_count\"], ((d.get(\"privapub\") or {}).get(\"votes\") or {}).get(\"down\"))")" = "0 1" ]'; then ok "Lemmy's change to a downvote reaches PrivaPub" else xf "Lemmy's change to a downvote reaches PrivaPub (votes relayed by a community are P7)" fi 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" echo " moderation" lm POST post/remove "{\"post_id\":$lm_cats_id,\"removed\":true,\"reason\":\"pasture\"}" >/dev/null sleep 10 if 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; then ok "a moderator's removal reaches PrivaPub" else xf "a moderator's removal reaches PrivaPub (the Lemmy moderation set is P7)" fi echo " statistics" stats_check lemmy.test lemmy