[ -n "$gts_on_pp"]&& ok "PrivaPub resolves @gtsuser@gts.test"|| ko "PrivaPub cannot resolve gtsuser"
echo"follows"
gcurl -s -o /dev/null -X POST -H "$GH"$G/api/v1/accounts/$alice_on_gts/follow
until_true 20'[ "$(gcurl -s -H "$GH" "$G/api/v1/accounts/relationships?id[]=$alice_on_gts" | j "print(d[0][\"following\"])")" = "True" ]'&& ok "gtsuser follows alice (Accept arrived)"|| ko "gtsuser's follow of alice not accepted"
curl -s -o /dev/null -X POST -H "$PH"$P/api/v1/accounts/$gts_on_pp/follow
until_true 10'[ "$(curl -s -H "$PH" "$P/api/v1/accounts/relationships?id[]=$gts_on_pp" | j "print(d[0][\"requested\"])")" = "True" ]'&& ok "alice's follow of locked gtsuser waits as a request"|| ko "alice's follow is not shown as requested"
until_true 20'gcurl -s -H "$GH" "$G/api/v1/follow_requests" | j "print(any(a[\"id\"]==\"$alice_on_gts\" for a in d))" | grep -q True'&& ok "the request reaches gtsuser's follow requests"|| ko "follow request missing on GoToSocial"
gcurl -s -o /dev/null -X POST -H "$GH"$G/api/v1/follow_requests/$alice_on_gts/authorize
until_true 20'[ "$(curl -s -H "$PH" "$P/api/v1/accounts/relationships?id[]=$gts_on_pp" | j "print(d[0][\"following\"])")" = "True" ]'&& ok "alice follows gtsuser (Accept arrived)"|| ko "alice's follow of gtsuser not accepted"
echo"posts"
pp_status=$(curl -s -X POST -H "$PH"$P/api/v1/statuses -d 'status=Hello from PrivaPub #pasture&visibility=public')
until_true 20'on_gts "$pp_uri" | j "print(len(d[\"statuses\"]))" | grep -q 1'&& ok "alice's post reaches GoToSocial"|| ko "alice's post missing on GoToSocial"
gts_post=$(gcurl -s -X POST -H "$GH"$G/api/v1/statuses -d 'status=Hello from GoToSocial&visibility=public'| j "print(d['id'])")
until_true 20'curl -s -H "$PH" "$P/api/v1/timelines/home" | grep -q "Hello from GoToSocial"'&& ok "gtsuser's post reaches alice's home"|| ko "gtsuser's post missing on PrivaPub"
["$(curl -s -H "$PH""$P/api/v1/timelines/home"| j "print(next(s['edited_at'] for s in d if 'Hello from GoToSocial' in s['content']))")"="None"]&& ok "an unedited remote post carries no edited_at"|| ko "unedited remote post reports an edit"
cw_status=$(curl -s -X POST -H "$PH"$P/api/v1/statuses -d 'status=behind a warning&spoiler_text=spoilers&visibility=public')
until_true 20'on_gts "$cw_uri" | j "print(any(s[\"spoiler_text\"]==\"spoilers\" and s[\"sensitive\"] for s in d[\"statuses\"]))" | grep -q True'&& ok "content warning survives to GoToSocial"|| ko "content warning lost"
gcurl -s -o /dev/null -X POST -H "$GH"$G/api/v1/statuses -d "status=@alice@privapub.test nice to meet you&in_reply_to_id=$pp_on_gts&visibility=public"
until_true 20'curl -s -H "$PH" "$P/api/v1/notifications" | j "print(any(n[\"type\"]==\"mention\" for n in d))" | grep -q True'&& ok "gtsuser's reply notifies alice"|| ko "reply did not notify alice"
until_true 10'[ "$(curl -s -H "$PH" "$P/api/v1/statuses/$pp_post/context" | j "print(len(d[\"descendants\"]))")" -ge 1 ]'&& ok "the reply threads under alice's post"|| ko "reply not threaded"
echo"likes and boosts"
gts_on_pp_post=$(curl -s -H "$PH""$P/api/v1/timelines/home"| j "print(next(s['id'] for s in d if 'Hello from GoToSocial' in s['content']))")
curl -s -o /dev/null -X POST -H "$PH"$P/api/v1/statuses/$gts_on_pp_post/favourite
curl -s -o /dev/null -X POST -H "$PH"$P/api/v1/statuses/$gts_on_pp_post/reblog
until_true 20'gcurl -s -H "$GH" "$G/api/v1/notifications" | j "print(any(n[\"type\"]==\"favourite\" for n in d))" | grep -q True'&& ok "alice's like reaches GoToSocial"|| ko "like not received"
until_true 20'gcurl -s -H "$GH" "$G/api/v1/notifications" | j "print(any(n[\"type\"]==\"reblog\" for n in d))" | grep -q True'&& ok "alice's boost reaches GoToSocial"|| ko "boost not received"
gcurl -s -o /dev/null -X POST -H "$GH"$G/api/v1/statuses/$pp_on_gts/favourite
gcurl -s -o /dev/null -X POST -H "$GH"$G/api/v1/statuses/$pp_on_gts/reblog
until_true 20'[ "$(curl -s -H "$PH" "$P/api/v1/statuses/$pp_post" | j "print(d[\"favourites_count\"])")" = "1" ]'&& ok "gtsuser's like counts on PrivaPub"|| ko "like not counted"
until_true 20'curl -s -H "$PH" "$P/api/v1/notifications" | j "print(any(n[\"type\"]==\"reblog\" for n in d))" | grep -q True'&& ok "gtsuser's boost notifies alice"|| ko "boost not notified"
echo"direct messages"
curl -s -o /dev/null -X POST -H "$PH"$P/api/v1/statuses -d 'status=@gtsuser@gts.test a secret&visibility=direct'
until_true 20'gcurl -s -H "$GH" "$G/api/v1/conversations" | grep -q "a secret"'&& ok "alice's DM reaches gtsuser"|| ko "DM missing on GoToSocial"
gcurl -s -o /dev/null -X POST -H "$GH"$G/api/v1/statuses -d 'status=@alice@privapub.test a secret back&visibility=direct'
until_true 20'curl -s -H "$PH" "$P/api/v1/conversations" | grep -q "a secret back"'&& ok "gtsuser's DM reaches alice"|| ko "DM missing on PrivaPub"
until_true 5'! gcurl -s -H "$GH" "$G/api/v1/timelines/public?local=false" | grep -q "a secret"'&& ok "the DM is not public on GoToSocial"|| ko "DM leaked to a public timeline"
echo"polls"
pp_poll=$(curl -s -X POST -H "$PH"$P/api/v1/statuses -d 'status=tea or coffee&visibility=public&poll[options][]=tea&poll[options][]=coffee&poll[expires_in]=3600'| j "print(d['uri'])")
until_true 20'on_gts "$pp_poll" | j "print(len(d[\"statuses\"][0][\"poll\"][\"options\"]))" | grep -q 2'&& ok "alice's poll reaches GoToSocial as a poll"|| ko "poll missing on GoToSocial"
gcurl -s -o /dev/null -X POST -H "$GH""$G/api/v1/polls/$pp_poll_on_gts/votes" -d 'choices[]=1'
pp_poll_id=$(curl -s -H "$PH""$P/api/v1/timelines/home"| j "print(next(s['id'] for s in d if s['poll'] and s['uri']=='$pp_poll'))")
until_true 20'[ "$(curl -s -H "$PH" "$P/api/v1/polls/$pp_poll_id" | j "print(d[\"options\"][1][\"votes_count\"])")" = "1" ]'&& ok "gtsuser's vote counts on PrivaPub"|| ko "vote not counted on PrivaPub"
gts_poll=$(gcurl -s -X POST -H "$GH"$G/api/v1/statuses -d 'status=red or blue&visibility=public&poll[options][]=red&poll[options][]=blue&poll[expires_in]=3600'| j "print(d['id'])")
until_true 20'curl -s -H "$PH" "$P/api/v1/timelines/home" | j "print(any(s[\"poll\"] and s[\"content\"].find(\"red or blue\")>=0 for s in d))" | grep -q True'&& ok "gtsuser's poll reaches alice as a poll"|| ko "poll missing on PrivaPub"
gts_poll_on_pp=$(curl -s -H "$PH""$P/api/v1/timelines/home"| j "print(next(s['poll']['id'] for s in d if s['poll'] and 'red or blue' in s['content']))")
curl -s -o /dev/null -X POST -H "$PH""$P/api/v1/polls/$gts_poll_on_pp/votes" -d 'choices[]=0'
until_true 20'[ "$(gcurl -s -H "$GH" "$G/api/v1/statuses/$gts_poll" | j "print(d[\"poll\"][\"options\"][0][\"votes_count\"])")" = "1" ]'&& ok "alice's vote counts on GoToSocial"|| ko "vote not counted on GoToSocial"
echo"quotes"
quote_target=$(curl -s -H "$PH""$P/api/v1/timelines/home"| j "print(next(s['id'] for s in d if 'red or blue' in s['content']))")
&& ok "GoToSocial's author-only quote policy is respected"|| ko "quoted a post whose author forbids it"
echo"link previews"
linked=$(curl -s -X POST -H "$PH"$P/api/v1/statuses --data-urlencode 'status=have a look https://gts.test/@gtsuser' -d 'visibility=public'| j "print(d['id'])")
until_true 45'[ -n "$(curl -s -H "$PH" "$P/api/v1/statuses/$linked" | j "print((d[\"card\"] or {}).get(\"title\") or \"\")")" ]'&& ok "the server builds a card for a linked page"|| ko "no card for the linked page"
echo"edits and deletes"
curl -s -o /dev/null -X PUT -H "$PH"$P/api/v1/statuses/$pp_post -d 'status=Hello from PrivaPub, edited'
until_true 20'gcurl -s -H "$GH" "$G/api/v1/statuses/$pp_on_gts" | grep -q "edited"'&& ok "alice's edit reaches GoToSocial"|| ko "edit not applied"
gts_edit=$(gcurl -s -X POST -H "$GH"$G/api/v1/statuses -d 'status=GoToSocial before the edit&visibility=public'| j "print(d['id'])")
until_true 20'curl -s -H "$PH" "$P/api/v1/timelines/home" | grep -q "GoToSocial before the edit"'||true
gts_edit_on_pp=$(curl -s -H "$PH""$P/api/v1/timelines/home"| j "print(next(s['id'] for s in d if 'GoToSocial before the edit' in s['content']))")
pp_reply=$(curl -s -X POST -H "$PH"$P/api/v1/statuses -d "status=@gtsuser@gts.test answering from PrivaPub&in_reply_to_id=$gts_edit_on_pp&visibility=public"| j "print(d['uri'])")
until_true 20'[ "$(on_gts "$pp_reply" | j "print(d[\"statuses\"][0][\"in_reply_to_id\"] == \"$gts_edit\")")" = "True" ]'&& ok "alice's reply threads under gtsuser's post"|| ko "outbound reply not threaded on GoToSocial"
gcurl -s -o /dev/null -X PUT -H "$GH"$G/api/v1/statuses/$gts_edit -d 'status=GoToSocial after the edit'
until_true 20'[ "$(curl -s -H "$PH" "$P/api/v1/statuses/$gts_edit_on_pp/history" | j "print(len(d))")" = "2" ]'&& ok "gtsuser's edit reaches PrivaPub with its history"|| ko "GoToSocial's edit not applied"
["$(curl -s -H "$PH""$P/api/v1/statuses/$gts_edit_on_pp"| j "print(d['edited_at'] is not None)")"="True"]&& ok "the edited post carries edited_at"|| ko "no edited_at on the edited post"
curl -s -o /dev/null -X POST -H "$PH"$P/api/v1/statuses/$gts_edit_on_pp/favourite
curl -s -o /dev/null -X POST -H "$PH"$P/api/v1/statuses/$gts_edit_on_pp/reblog
curl -s -o /dev/null -X POST -H "$PH"$P/api/v1/statuses/$gts_edit_on_pp/unfavourite
curl -s -o /dev/null -X POST -H "$PH"$P/api/v1/statuses/$gts_edit_on_pp/unreblog
until_true 20'[ "$(gcurl -s -H "$GH" "$G/api/v1/statuses/$gts_edit" | j "print(d[\"favourites_count\"], d[\"reblogs_count\"])")" = "0 0" ]'&& ok "alice's unlike and unboost reach GoToSocial"|| ko "undo of like or boost not applied on GoToSocial"
pp_undo=$(curl -s -X POST -H "$PH"$P/api/v1/statuses -d 'status=like me and take it back&visibility=public'| j "print(d['id'] + ' ' + d['uri'])")
gcurl -s -o /dev/null -X POST -H "$GH"$G/api/v1/statuses/$pp_undo_on_gts/unfavourite
gcurl -s -o /dev/null -X POST -H "$GH"$G/api/v1/statuses/$pp_undo_on_gts/unreblog
until_true 20'[ "$(curl -s -H "$PH" "$P/api/v1/statuses/$pp_undo_id" | j "print(d[\"favourites_count\"], d[\"reblogs_count\"])")" = "0 0" ]'&& ok "gtsuser's unlike and unboost reach PrivaPub"|| ko "GoToSocial's undo of like or boost not applied"
echo"media"
make_png "$work/red.png"
pp_media=$(curl -s -X POST -H "$PH""$P/api/v2/media" -F "file=@$work/red.png;type=image/png" -F 'description=a red square'| j "print(d['id'])")
pp_media_uri=$(curl -s -X POST -H "$PH"$P/api/v1/statuses -d "status=a picture for GoToSocial&visibility=public&media_ids[]=$pp_media"| j "print(d['uri'])")
until_true 20'[ "$(on_gts "$pp_media_uri" | j "print(d[\"statuses\"][0][\"media_attachments\"][0][\"description\"])")" = "a red square" ]'&& ok "an image with alt text reaches GoToSocial"|| ko "image or alt text missing on GoToSocial"
g_media=$(gcurl -s -X POST -H "$GH""$G/api/v2/media" -F "file=@$work/red.png;type=image/png" -F 'description=a red square from GoToSocial'| j "print(d['id'])")
gcurl -s -o /dev/null -X POST -H "$GH"$G/api/v1/statuses -d "status=a picture from GoToSocial&visibility=public&media_ids[]=$g_media"
until_true 20'curl -s -H "$PH" "$P/api/v1/timelines/home" | j "print(any(\"a picture from GoToSocial\" 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 GoToSocial\" for s in d))" | grep -q True'\
&& ok "an image with alt text from GoToSocial arrives through our proxy"|| ko "GoToSocial's image missing, unproxied or without alt text"
-d "{\"avatarId\":\"$alice_id\",\"title\":\"A community thread\",\"text\":\"posted into the community\",\"groupId\":\"$community\"}"
community_post=$(podman exec pasture-mongo mongosh --quiet PrivaPub --eval 'print(db.Post.findOne({Text:/posted into the community/}).ObjectURI)')
until_true 20'[ "$(on_gts "$community_post" | j "print(len(d[\"statuses\"]))")" = "1" ]'&& ok "the community's announce brings its post to GoToSocial"|| ko "community post missing on GoToSocial"
gcurl -s -o /dev/null -X POST -H "$GH"$G/api/v1/accounts/$circle_on_gts/follow
until_true 15'[ "$(gcurl -s -H "$GH" "$G/api/v1/accounts/relationships?id[]=$circle_on_gts" | j "print(d[0][\"requested\"])")" = "True" ]'&& ok "gtsuser's request to join a circle waits for its owner"|| ko "joining the circle was not held for approval"
until_true 20'[ "$(gcurl -s -H "$GH" "$G/api/v1/accounts/relationships?id[]=$circle_on_gts" | j "print(d[0][\"following\"])")" = "True" ]'&& ok "the owner's approval makes gtsuser a circle member"|| ko "circle approval did not reach GoToSocial"
gcurl -s -o /dev/null -X POST -H "$GH"$G/api/v1/accounts/$locked_on_gts/follow
until_true 20'[ "$(curl -s -H "$LH" "$P/api/v1/follow_requests" | j "print(len(d))")" = "1" ]'&& ok "gtsuser's follow waits on a locked persona"|| ko "no follow request on the locked persona"
["$(gcurl -s -H "$GH""$G/api/v1/accounts/relationships?id[]=$locked_on_gts"| j "print(d[0][\"requested\"])")"="True"]&& ok "GoToSocial shows the follow as requested"|| ko "GoToSocial does not show the request"
curl -s -o /dev/null -X POST -H "$LH""$P/api/v1/follow_requests/$requester/reject"
until_true 20'[ "$(gcurl -s -H "$GH" "$G/api/v1/accounts/relationships?id[]=$locked_on_gts" | j "print(d[0][\"requested\"] or d[0][\"following\"])")" = "False" ]'&& ok "a rejected request is withdrawn on GoToSocial"|| ko "rejection not applied on GoToSocial"
gcurl -s -o /dev/null -X POST -H "$GH"$G/api/v1/accounts/$locked_on_gts/follow
curl -s -o /dev/null -X POST -H "$LH""$P/api/v1/follow_requests/$requester/authorize"
until_true 20'[ "$(gcurl -s -H "$GH" "$G/api/v1/accounts/relationships?id[]=$locked_on_gts" | j "print(d[0][\"following\"])")" = "True" ]'&& ok "an authorized request makes gtsuser a follower"|| ko "authorization not applied on GoToSocial"
echo"smoke"
"$here/../smoke/mastodon-api.sh""$P""$PT" >/dev/null 2>&1&& ok "the deploy's Mastodon smoke check passes, signed in"|| ko "the Mastodon smoke check fails"