peers/mastodon.sh runs Mastodon's web and sidekiq containers on a shared Postgres and Redis (peers/shared.sh). They trust Caddy's CA through SSL_CERT_FILE and reach private addresses through ALLOWED_PRIVATE_ADDRESSES. Its users and tokens come from tootctl and rails runner. scenarios/mastodon.sh adds 49 checks, as listed in docs/INTEROP.md: follows, posts, replies, likes and boosts with undos, DMs, polls, FEP-044f quotes both ways, media through the proxy, edits, deletes, locked follows, a circle request, reports, blocks and statistics. Two are expected failures: - inbound Block (P7); - circle posts. Mastodon 4.7 loses the recipient of deliveries to its numeric /ap/users/<id>/inbox and then drops a post that names no local account. The fix on our side changes what a circle reveals, so it waits for the owner. GoToSocial and Mastodon together: 86 passed, 0 failed. The pasture now copies Caddy's root certificate reliably, readable by the peers, and rebuilds the bundle each time. The CA directory is mounted shared (:z), because a private :Z label locks out every container but the last. pfetch reaches PrivaPub's own https URIs through Caddy. PRIVAPUB_ENV passes settings to PrivaPub, which scenarios/crawler.sh uses to check the opt-in crawler against Mastodon: it visits, describes and reads the peers list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ELjqpznMFMNrJoJUj6K5p2
184 lines
19 KiB
Bash
184 lines
19 KiB
Bash
# Mastodon 4.7: discovery, follows, posts, CW, replies both ways, likes and boosts with their undos, DMs, polls,
|
|
# FEP-044f quotes both ways, edits and deletes both ways, media with alt text, locked follows, blocks, statistics.
|
|
M=https://mastodon.test:6443
|
|
mcurl() { curl -sk --resolve mastodon.test:6443:127.0.0.1 "$@"; }
|
|
. "$here/peers/mastodon.sh"
|
|
|
|
echo "mastodon"
|
|
MT=$(mastodon_token)
|
|
MH="Authorization: Bearer $MT"
|
|
[ -n "$MT" ] && ok "Mastodon token for mastouser" || { ko "Mastodon token"; return 1; }
|
|
AT=$(privapub_token alice_masto)
|
|
AH="Authorization: Bearer $AT"
|
|
[ -n "$AT" ] && ok "PrivaPub token for alice_masto" || { ko "PrivaPub token for alice_masto"; return 1; }
|
|
# what Mastodon holds of an account, newest first; and the one status whose uri is given
|
|
m_statuses() { mcurl -H "$MH" "$M/api/v1/accounts/$1/statuses?limit=40"; }
|
|
m_status_by_uri() { m_statuses "$1" | j "print(json.dumps(next((s for s in d if s['uri']=='$2'), None)))"; }
|
|
|
|
echo " discovery"
|
|
alice_on_m=$(mcurl -H "$MH" "$M/api/v2/search?q=@alice_masto@privapub.test&resolve=true&type=accounts" | j "print(d['accounts'][0]['id'])")
|
|
[ -n "$alice_on_m" ] && ok "Mastodon resolves @alice_masto@privapub.test" || ko "Mastodon cannot resolve alice_masto"
|
|
masto_on_p=$(curl -s -H "$AH" "$P/api/v2/search?q=mastouser@mastodon.test&resolve=true&type=accounts" | j "print(d['accounts'][0]['id'])")
|
|
[ -n "$masto_on_p" ] && ok "PrivaPub resolves @mastouser@mastodon.test" || ko "PrivaPub cannot resolve mastouser"
|
|
|
|
echo " follows"
|
|
mcurl -o /dev/null -X POST -H "$MH" "$M/api/v1/accounts/$alice_on_m/follow"
|
|
until_true 30 '[ "$(mcurl -H "$MH" "$M/api/v1/accounts/relationships?id[]=$alice_on_m" | j "print(d[0][\"following\"])")" = "True" ]' && ok "mastouser follows alice_masto (Accept arrived)" || ko "Mastodon's follow not accepted"
|
|
curl -s -o /dev/null -X POST -H "$AH" "$P/api/v1/accounts/$masto_on_p/follow"
|
|
until_true 30 '[ "$(curl -s -H "$AH" "$P/api/v1/accounts/relationships?id[]=$masto_on_p" | j "print(d[0][\"following\"])")" = "True" ]' && ok "alice_masto follows mastouser (Accept arrived)" || ko "PrivaPub's follow not accepted"
|
|
|
|
echo " posts"
|
|
a_post=$(curl -s -X POST -H "$AH" $P/api/v1/statuses -d 'status=Hello Mastodon from PrivaPub&visibility=public')
|
|
a_post_id=$(echo "$a_post" | j "print(d['id'])"); a_post_uri=$(echo "$a_post" | j "print(d['uri'])")
|
|
until_true 30 '[ "$(m_status_by_uri "$alice_on_m" "$a_post_uri")" != "null" ]' && ok "alice_masto's post reaches Mastodon" || ko "post missing on Mastodon"
|
|
a_post_on_m=$(m_status_by_uri "$alice_on_m" "$a_post_uri" | j "print(d['id'])")
|
|
m_post=$(mcurl -X POST -H "$MH" "$M/api/v1/statuses" -d 'status=Hello PrivaPub from Mastodon&visibility=public' | j "print(d['id'])")
|
|
until_true 30 'curl -s -H "$AH" "$P/api/v1/timelines/home" | grep -q "Hello PrivaPub from Mastodon"' && ok "mastouser's post reaches alice_masto's home" || ko "Mastodon's post missing on PrivaPub"
|
|
m_post_on_p=$(curl -s -H "$AH" "$P/api/v1/timelines/home" | j "print(next(s['id'] for s in d if 'Hello PrivaPub from Mastodon' in s['content']))")
|
|
cw_uri=$(curl -s -X POST -H "$AH" $P/api/v1/statuses -d 'status=behind a warning&spoiler_text=spoilers&visibility=public' | j "print(d['uri'])")
|
|
until_true 30 '[ "$(m_status_by_uri "$alice_on_m" "$cw_uri" | j "print(d and d[\"spoiler_text\"]==\"spoilers\" and d[\"sensitive\"])")" = "True" ]' && ok "a content warning survives to Mastodon" || ko "content warning lost on Mastodon"
|
|
fo_uri=$(curl -s -X POST -H "$AH" $P/api/v1/statuses -d 'status=only for followers&visibility=private' | j "print(d['uri'])")
|
|
until_true 30 '[ "$(m_status_by_uri "$alice_on_m" "$fo_uri" | j "print(d and d[\"visibility\"])")" = "private" ]' && ok "a followers-only post reaches Mastodon as private" || ko "followers-only post missing or widened on Mastodon"
|
|
[ "$(pfetch -o /dev/null -w '%{http_code}' -H 'Accept: application/activity+json' "$fo_uri")" = "404" ] && ok "the followers-only post is not served unsigned" || ko "followers-only post served unsigned"
|
|
|
|
echo " replies"
|
|
mcurl -o /dev/null -X POST -H "$MH" "$M/api/v1/statuses" -d "status=@alice_masto@privapub.test replying from Mastodon&in_reply_to_id=$a_post_on_m&visibility=public"
|
|
until_true 30 'curl -s -H "$AH" "$P/api/v1/notifications" | j "print(any(n[\"type\"]==\"mention\" for n in d))" | grep -q True' && ok "mastouser's reply notifies alice_masto" || ko "reply did not notify"
|
|
until_true 15 '[ "$(curl -s -H "$AH" "$P/api/v1/statuses/$a_post_id/context" | j "print(len(d[\"descendants\"]))")" -ge 1 ]' && ok "the reply threads under alice_masto's post" || ko "reply not threaded on PrivaPub"
|
|
curl -s -o /dev/null -X POST -H "$AH" $P/api/v1/statuses -d "status=@mastouser@mastodon.test replying from PrivaPub&in_reply_to_id=$m_post_on_p&visibility=public"
|
|
until_true 30 '[ "$(mcurl -H "$MH" "$M/api/v1/statuses/$m_post/context" | j "print(len(d[\"descendants\"]))")" -ge 1 ]' && ok "alice_masto's reply threads under mastouser's post" || ko "outbound reply not threaded on Mastodon"
|
|
|
|
echo " likes and boosts"
|
|
curl -s -o /dev/null -X POST -H "$AH" "$P/api/v1/statuses/$m_post_on_p/favourite"
|
|
curl -s -o /dev/null -X POST -H "$AH" "$P/api/v1/statuses/$m_post_on_p/reblog"
|
|
until_true 30 '[ "$(mcurl -H "$MH" "$M/api/v1/statuses/$m_post" | j "print(d[\"favourites_count\"])")" = "1" ]' && ok "alice_masto's like counts on Mastodon" || ko "like not counted on Mastodon"
|
|
until_true 30 '[ "$(mcurl -H "$MH" "$M/api/v1/statuses/$m_post" | j "print(d[\"reblogs_count\"])")" = "1" ]' && ok "alice_masto's boost counts on Mastodon" || ko "boost not counted on Mastodon"
|
|
curl -s -o /dev/null -X POST -H "$AH" "$P/api/v1/statuses/$m_post_on_p/unfavourite"
|
|
curl -s -o /dev/null -X POST -H "$AH" "$P/api/v1/statuses/$m_post_on_p/unreblog"
|
|
until_true 30 '[ "$(mcurl -H "$MH" "$M/api/v1/statuses/$m_post" | j "print(d[\"favourites_count\"], d[\"reblogs_count\"])")" = "0 0" ]' && ok "alice_masto's unlike and unboost reach Mastodon" || ko "undo of like or boost not applied on Mastodon"
|
|
mcurl -o /dev/null -X POST -H "$MH" "$M/api/v1/statuses/$a_post_on_m/favourite"
|
|
mcurl -o /dev/null -X POST -H "$MH" "$M/api/v1/statuses/$a_post_on_m/reblog"
|
|
until_true 30 '[ "$(curl -s -H "$AH" "$P/api/v1/statuses/$a_post_id" | j "print(d[\"favourites_count\"], d[\"reblogs_count\"])")" = "1 1" ]' && ok "mastouser's like and boost count on PrivaPub" || ko "like or boost not counted on PrivaPub"
|
|
mcurl -o /dev/null -X POST -H "$MH" "$M/api/v1/statuses/$a_post_on_m/unfavourite"
|
|
mcurl -o /dev/null -X POST -H "$MH" "$M/api/v1/statuses/$a_post_on_m/unreblog"
|
|
until_true 30 '[ "$(curl -s -H "$AH" "$P/api/v1/statuses/$a_post_id" | j "print(d[\"favourites_count\"], d[\"reblogs_count\"])")" = "0 0" ]' && ok "mastouser's unlike and unboost reach PrivaPub" || ko "undo of like or boost not applied on PrivaPub"
|
|
|
|
echo " direct messages"
|
|
curl -s -o /dev/null -X POST -H "$AH" $P/api/v1/statuses -d 'status=@mastouser@mastodon.test a secret for Mastodon&visibility=direct'
|
|
until_true 30 'mcurl -H "$MH" "$M/api/v1/conversations" | grep -q "a secret for Mastodon"' && ok "alice_masto's DM reaches mastouser" || ko "DM missing on Mastodon"
|
|
mcurl -o /dev/null -X POST -H "$MH" "$M/api/v1/statuses" -d 'status=@alice_masto@privapub.test a secret for PrivaPub&visibility=direct'
|
|
until_true 30 'curl -s -H "$AH" "$P/api/v1/conversations" | grep -q "a secret for PrivaPub"' && ok "mastouser's DM reaches alice_masto" || ko "DM missing on PrivaPub"
|
|
! curl -s "$P/api/v1/timelines/public" | grep -q "a secret for PrivaPub" && ok "the DM is not on PrivaPub's public timeline" || ko "DM leaked to the public timeline"
|
|
|
|
echo " polls"
|
|
a_poll_uri=$(curl -s -X POST -H "$AH" $P/api/v1/statuses -d 'status=cats or dogs&visibility=public&poll[options][]=cats&poll[options][]=dogs&poll[expires_in]=3600' | j "print(d['uri'])")
|
|
until_true 30 '[ "$(m_status_by_uri "$alice_on_m" "$a_poll_uri" | j "print(len(d[\"poll\"][\"options\"]))")" = "2" ]' && ok "alice_masto's poll reaches Mastodon as a poll" || ko "poll missing on Mastodon"
|
|
a_poll_on_m=$(m_status_by_uri "$alice_on_m" "$a_poll_uri" | j "print(d['poll']['id'])")
|
|
mcurl -o /dev/null -X POST -H "$MH" "$M/api/v1/polls/$a_poll_on_m/votes" -d 'choices[]=1'
|
|
a_poll_id=$(curl -s -H "$AH" "$P/api/v1/accounts/verify_credentials" | j "print(d['id'])")
|
|
until_true 30 '[ "$(curl -s -H "$AH" "$P/api/v1/accounts/$a_poll_id/statuses" | j "print(next(s[\"poll\"][\"options\"][1][\"votes_count\"] for s in d if s[\"uri\"]==\"$a_poll_uri\"))")" = "1" ]' && ok "mastouser's vote counts on PrivaPub" || ko "vote not counted on PrivaPub"
|
|
m_poll=$(mcurl -X POST -H "$MH" "$M/api/v1/statuses" -d 'status=tea or coffee, Mastodon asks&visibility=public&poll[options][]=tea&poll[options][]=coffee&poll[expires_in]=3600' | j "print(d['id'])")
|
|
until_true 30 'curl -s -H "$AH" "$P/api/v1/timelines/home" | j "print(any(s[\"poll\"] and \"Mastodon asks\" in s[\"content\"] for s in d))" | grep -q True' && ok "mastouser's poll reaches PrivaPub as a poll" || ko "poll missing on PrivaPub"
|
|
m_poll_on_p=$(curl -s -H "$AH" "$P/api/v1/timelines/home" | j "print(next(s['poll']['id'] for s in d if s['poll'] and 'Mastodon asks' in s['content']))")
|
|
curl -s -o /dev/null -X POST -H "$AH" "$P/api/v1/polls/$m_poll_on_p/votes" -d 'choices[]=0'
|
|
until_true 30 '[ "$(mcurl -H "$MH" "$M/api/v1/statuses/$m_poll" | j "print(d[\"poll\"][\"options\"][0][\"votes_count\"])")" = "1" ]' && ok "alice_masto's vote counts on Mastodon" || ko "vote not counted on Mastodon"
|
|
|
|
echo " quotes"
|
|
q_target=$(mcurl -X POST -H "$MH" "$M/api/v1/statuses" -d 'status=quote me if you like&visibility=public' | j "print(d['id'])")
|
|
until_true 30 'curl -s -H "$AH" "$P/api/v1/timelines/home" | grep -q "quote me if you like"' || true
|
|
q_target_on_p=$(curl -s -H "$AH" "$P/api/v1/timelines/home" | j "print(next(s['id'] for s in d if 'quote me if you like' in s['content']))")
|
|
a_quote=$(curl -s -X POST -H "$AH" $P/api/v1/statuses -d "status=quoting Mastodon&visibility=public"ed_status_id=$q_target_on_p" | j "print(d['id'])")
|
|
until_true 30 '[ "$(curl -s -H "$AH" "$P/api/v1/statuses/$a_quote" | j "print((d.get(\"quote\") or {}).get(\"state\"))")" = "accepted" ]' && ok "Mastodon approves alice_masto's quote (FEP-044f)" || ko "quote of a Mastodon post not approved"
|
|
m_quote=$(mcurl -X POST -H "$MH" "$M/api/v1/statuses" -d "status=quoting PrivaPub&visibility=public"ed_status_id=$a_post_on_m" | j "print(d['id'])")
|
|
until_true 30 '[ "$(mcurl -H "$MH" "$M/api/v1/statuses/$m_quote" | j "print((d.get(\"quote\") or {}).get(\"state\"))")" = "accepted" ]' && ok "PrivaPub approves mastouser's quote (FEP-044f)" || ko "quote of a PrivaPub post not approved on Mastodon"
|
|
|
|
echo " media"
|
|
python3 -c "
|
|
import struct,zlib
|
|
w=h=8
|
|
raw=b''.join(b'\x00'+bytes([200,60,60])*w for _ in range(h))
|
|
png=b'\x89PNG\r\n\x1a\n'+b''.join(struct.pack('>I',len(c))+t+c+struct.pack('>I',zlib.crc32(t+c)&0xffffffff) for t,c in [(b'IHDR',struct.pack('>IIBBBBB',w,h,8,2,0,0,0)),(b'IDAT',zlib.compress(raw)),(b'IEND',b'')])
|
|
open('$work/red.png','wb').write(png)"
|
|
a_media=$(curl -s -X POST -H "$AH" "$P/api/v2/media" -F "file=@$work/red.png;type=image/png" -F 'description=a red square' | j "print(d['id'])")
|
|
a_media_uri=$(curl -s -X POST -H "$AH" $P/api/v1/statuses -d "status=a picture&visibility=public&media_ids[]=$a_media" | j "print(d['uri'])")
|
|
until_true 30 '[ "$(m_status_by_uri "$alice_on_m" "$a_media_uri" | j "print(d[\"media_attachments\"][0][\"description\"])")" = "a red square" ]' && ok "an image with alt text reaches Mastodon" || ko "image or alt text missing on Mastodon"
|
|
m_media=$(mcurl -X POST -H "$MH" "$M/api/v2/media" -F "file=@$work/red.png;type=image/png" -F 'description=a red square from Mastodon' | j "print(d['id'])")
|
|
until_true 10 '[ "$(mcurl -o /dev/null -w "%{http_code}" -H "$MH" "$M/api/v1/media/$m_media")" = "200" ]' || true
|
|
mcurl -o /dev/null -X POST -H "$MH" "$M/api/v1/statuses" -d "status=a picture from Mastodon&visibility=public&media_ids[]=$m_media"
|
|
until_true 30 'curl -s -H "$AH" "$P/api/v1/timelines/home" | j "print(any(\"a picture from Mastodon\" in s[\"content\"] and s[\"media_attachments\"] and \"/media/proxy/\" in s[\"media_attachments\"][0][\"url\"] and s[\"media_attachments\"][0][\"description\"]==\"a red square from Mastodon\" for s in d))" | grep -q True' \
|
|
&& ok "an image with alt text from Mastodon arrives through our proxy" || ko "Mastodon's image missing, unproxied or without alt text"
|
|
|
|
echo " edits and deletes"
|
|
curl -s -o /dev/null -X PUT -H "$AH" "$P/api/v1/statuses/$a_post_id" -d 'status=Hello Mastodon from PrivaPub, edited'
|
|
until_true 30 'mcurl -H "$MH" "$M/api/v1/statuses/$a_post_on_m" | grep -q "edited"' && ok "alice_masto's edit reaches Mastodon" || ko "edit not applied on Mastodon"
|
|
mcurl -o /dev/null -X PUT -H "$MH" "$M/api/v1/statuses/$m_post" -d 'status=Hello PrivaPub from Mastodon, edited'
|
|
until_true 30 '[ "$(curl -s -H "$AH" "$P/api/v1/statuses/$m_post_on_p/history" | j "print(len(d))")" = "2" ]' && ok "mastouser's edit reaches PrivaPub with its history" || ko "Mastodon's edit not applied on PrivaPub"
|
|
cw_on_m=$(m_status_by_uri "$alice_on_m" "$cw_uri" | j "print(d['id'])")
|
|
cw_id=$(curl -s -H "$AH" "$P/api/v1/accounts/$a_poll_id/statuses" | j "print(next(s['id'] for s in d if s['uri']=='$cw_uri'))")
|
|
curl -s -o /dev/null -X DELETE -H "$AH" "$P/api/v1/statuses/$cw_id"
|
|
until_true 30 '[ "$(mcurl -o /dev/null -w "%{http_code}" -H "$MH" "$M/api/v1/statuses/$cw_on_m")" = "404" ]' && ok "alice_masto's delete reaches Mastodon" || ko "delete not applied on Mastodon"
|
|
[ "$(pfetch -o /dev/null -w '%{http_code}' -H 'Accept: application/activity+json' "$cw_uri")" = "410" ] && ok "the deleted post answers 410" || ko "deleted post does not answer 410"
|
|
mcurl -o /dev/null -X DELETE -H "$MH" "$M/api/v1/statuses/$m_poll"
|
|
until_true 30 '! curl -s -H "$AH" "$P/api/v1/timelines/home" | grep -q "Mastodon asks"' && ok "mastouser's delete reaches PrivaPub" || ko "Mastodon's delete not applied on PrivaPub"
|
|
|
|
echo " locked follows"
|
|
mcurl -o /dev/null -X PATCH -H "$MH" "$M/api/v1/accounts/update_credentials" -d 'locked=true'
|
|
BT=$(privapub_token bob_masto); BH="Authorization: Bearer $BT"
|
|
masto_on_p_b=$(curl -s -H "$BH" "$P/api/v2/search?q=mastouser@mastodon.test&resolve=true&type=accounts" | j "print(d['accounts'][0]['id'])")
|
|
curl -s -o /dev/null -X POST -H "$BH" "$P/api/v1/accounts/$masto_on_p_b/follow"
|
|
until_true 30 'mcurl -H "$MH" "$M/api/v1/follow_requests" | j "print(any(a[\"acct\"]==\"bob_masto@privapub.test\" for a in d))" | grep -q True' && ok "bob_masto's follow waits as a request on locked mastouser" || ko "follow request missing on Mastodon"
|
|
bob_on_m=$(mcurl -H "$MH" "$M/api/v1/follow_requests" | j "print(next(a['id'] for a in d if a['acct']=='bob_masto@privapub.test'))")
|
|
mcurl -o /dev/null -X POST -H "$MH" "$M/api/v1/follow_requests/$bob_on_m/authorize"
|
|
until_true 30 '[ "$(curl -s -H "$BH" "$P/api/v1/accounts/relationships?id[]=$masto_on_p_b" | j "print(d[0][\"following\"])")" = "True" ]' && ok "mastouser's approval reaches PrivaPub" || ko "approval not applied on PrivaPub"
|
|
|
|
echo " circles"
|
|
jwt=$(privapub_root)
|
|
alice_id=$(curl -s -H "$AH" "$P/api/v1/accounts/verify_credentials" | j "print(d['id'])")
|
|
circle_name="circle$(date +%s)"
|
|
circle=$(curl -s -X POST $P/clientapi/group/insert -H 'Content-Type: application/json' -H "Authorization: Bearer $jwt" \
|
|
-d "{\"avatarId\":\"$alice_id\",\"userName\":\"$circle_name\",\"name\":\"a circle\",\"description\":\"just us\",\"isCommunity\":false}" | j "print(d['id'])")
|
|
circle_on_m=$(mcurl -H "$MH" "$M/api/v2/search?q=@$circle_name@privapub.test&resolve=true&type=accounts" | j "print(d['accounts'][0]['id'])")
|
|
mcurl -o /dev/null -X POST -H "$MH" "$M/api/v1/accounts/$circle_on_m/follow"
|
|
until_true 15 '[ "$(mcurl -H "$MH" "$M/api/v1/accounts/relationships?id[]=$circle_on_m" | j "print(d[0][\"requested\"])")" = "True" ]' && ok "mastouser's request to join a circle waits for its owner" || ko "joining the circle was not held for approval"
|
|
# Mastodon 4.7 names its actors by number (https://mastodon.test/ap/users/<id>), so the pending request says who asked
|
|
requester=$(podman exec pasture-mongo mongosh --quiet PrivaPub --eval "print(db.Follower.findOne({LocalActorId:'$circle', IsAccepted:false}).ActorURI)")
|
|
curl -s -o /dev/null -X POST $P/clientapi/group/approve -H 'Content-Type: application/json' -H "Authorization: Bearer $jwt" \
|
|
-d "{\"avatarId\":\"$alice_id\",\"groupId\":\"$circle\",\"memberActorURI\":\"$requester\"}"
|
|
until_true 30 '[ "$(mcurl -H "$MH" "$M/api/v1/accounts/relationships?id[]=$circle_on_m" | j "print(d[0][\"following\"])")" = "True" ]' && ok "the owner's approval makes mastouser a circle member" || ko "circle approval did not reach Mastodon"
|
|
circle_post=$(curl -s -X POST $P/clientapi/post/insert -H 'Content-Type: application/json' -H "Authorization: Bearer $jwt" \
|
|
-d "{\"avatarId\":\"$alice_id\",\"text\":\"only the circle sees this\",\"groupId\":\"$circle\"}")
|
|
circle_uri=$(podman exec pasture-mongo mongosh --quiet PrivaPub --eval 'print(db.Post.findOne({Text:/only the circle sees this/},{ObjectURI:1}).ObjectURI)')
|
|
# Mastodon 4.7 learns whose personal inbox a delivery reached only from /users/<name>/inbox, never from the numeric
|
|
# /ap/users/<id>/inbox it now advertises, and keeps a post naming no local account only for that recipient
|
|
# (InboxesController#account_required?, Create#addresses_local_accounts?). A circle post names only the circle.
|
|
m_stored() { podman exec pasture-mastodon bin/rails runner "puts Status.exists?(uri: '$1')" 2>/dev/null | tail -1; }
|
|
if until_true 15 '[ "$(m_stored "$circle_uri")" = "true" ]'; then
|
|
ok "a circle post reaches its Mastodon member"
|
|
else
|
|
xf "a circle post reaches its Mastodon member (Mastodon 4.7 loses the recipient of numeric-inbox deliveries; owner decision pending)"
|
|
fi
|
|
mastodon_user outsider
|
|
OT=$(mastodon_token outsider)
|
|
alice_on_m_o=$(mcurl -H "Authorization: Bearer $OT" "$M/api/v2/search?q=@alice_masto@privapub.test&resolve=true&type=accounts" | j "print(d['accounts'][0]['id'])")
|
|
[ "$(mcurl -H "Authorization: Bearer $OT" "$M/api/v1/accounts/$alice_on_m_o/statuses?limit=40" | j "print(any(s['uri']=='$circle_uri' for s in d))")" = "False" ] \
|
|
&& ok "another Mastodon user does not see the circle post" || ko "the circle post leaked to a non-member"
|
|
[ "$(pfetch -o /dev/null -w '%{http_code}' -H 'Accept: application/activity+json' "$circle_uri")" = "404" ] && ok "the circle post is not served unsigned" || ko "circle post served unsigned"
|
|
|
|
echo " reports"
|
|
curl -s -o /dev/null -X POST -H "$AH" "$P/api/v1/reports" -d "account_id=$masto_on_p&comment=pasture-report-$circle_name&forward=true"
|
|
until_true 30 'mcurl -H "$MH" "$M/api/v1/admin/reports" | grep -q "pasture-report-$circle_name"' && ok "a report reaches Mastodon's moderators" || ko "report missing on Mastodon"
|
|
[ "$(mcurl -H "$MH" "$M/api/v1/admin/reports" | j "print(next((r['account']['username'] for r in d if 'pasture-report-$circle_name' in r['comment']), ''))")" != "alice_masto" ] \
|
|
&& ok "the report does not come from the reporting persona" || ko "the report names the reporting persona"
|
|
|
|
echo " blocks and unfollows"
|
|
curl -s -o /dev/null -X POST -H "$AH" "$P/api/v1/accounts/$masto_on_p/unfollow"
|
|
until_true 30 '[ "$(mcurl -H "$MH" "$M/api/v1/accounts/relationships?id[]=$alice_on_m" | j "print(d[0][\"followed_by\"])")" = "False" ]' && ok "alice_masto's unfollow reaches Mastodon" || ko "unfollow not applied on Mastodon"
|
|
curl -s -o /dev/null -X POST -H "$AH" "$P/api/v1/accounts/$masto_on_p/block"
|
|
until_true 30 '[ "$(mcurl -H "$MH" "$M/api/v1/accounts/relationships?id[]=$alice_on_m" | j "print(d[0][\"blocked_by\"])")" = "True" ]' && ok "alice_masto's block reaches Mastodon" || ko "block not applied on Mastodon"
|
|
curl -s -o /dev/null -X POST -H "$AH" "$P/api/v1/accounts/$masto_on_p/unblock"
|
|
mcurl -o /dev/null -X POST -H "$MH" "$M/api/v1/accounts/$alice_on_m/block"
|
|
sleep 5; xf "Mastodon's block is enforced on PrivaPub (inbound Block is P7)"
|
|
|
|
echo " statistics"
|
|
stats_check mastodon.test mastodon
|