# settings_form <view json> <python statements changing f>: the settings form made from a settings view, then changed
settings_form(){ python3 -c "
import json,sys
v=json.loads(sys.argv[1])
f={k: v.get(k) for k in ['title','shortDescription','description','contactEmail','contactAccountId','privacyPolicy','termsOfService','termsEffectiveAt','publishActivity']}
f['rules']=[{'id': r['id'], 'text': r['text'], 'hint': r.get('hint')} for r in v.get('rules', []) if not r.get('isDeleted')]
m=v.get('media') or {}
f['media']={k: m.get(k) for k in ['quotaBytesPerRoot','maxImageBytes','maxVideoBytes','maxSeconds']}
# noise_png <path> <side>: a PNG of random pixels, which compresses to more than a kilobyte
noise_png(){ python3 -c "
import os,struct,zlib
w=h=$2
raw=b''.join(b'\x00'+os.urandom(3*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('$1','wb').write(png)";}
echo"instance"
run=$(date +%s)
privapub_root >/dev/null # the pasture's root, made if this runs first
&& ok "the settings are saved, the rules given their ids"|| ko "the settings were not saved: $(echo"$saved"| head -c 300)"
v1=$(curl -s "$P/api/v1/instance")
["$(echo"$v1"| j "print(d['title'] == '$title' and d['short_description'] == 'A test field for $run.' and d['email'] == 'staff@privapub.test' and d['contact_account']['id'] == '$alice_id' and [r['text'] for r in d['rules']] == ['No spam $run', 'Be kind $run'])")"= True ]\
&& ok "/api/v1/instance says so"|| ko "/api/v1/instance: $(echo"$v1"| j "print(d['title'], d['email'], d['rules'])")"
v2=$(curl -s "$P/api/v2/instance")
["$(echo"$v2"| j "print(d['title'] == '$title' and d['description'] == 'A test field for $run.' and d['contact']['email'] == 'staff@privapub.test' and d['contact']['account']['username'] == 'alice_instance' and [r['id'] for r in d['rules']] == ['$r1', '$r2'] and d['rules'][0]['hint'] == 'Ads included')")"= True ]\
&& ok "/api/v2/instance says so"|| ko "/api/v2/instance: $(echo"$v2"| j "print(d['title'], d['contact'], d['rules'])")"
["$(curl -s "$P/nodeinfo/2.1"| j "print(d['metadata']['nodeName'] == '$title' and d['metadata']['nodeDescription'] == 'A test field for $run.')")"= True ]\
&& ok "NodeInfo names the server and describes it"|| ko "NodeInfo: $(curl -s "$P/nodeinfo/2.1"| j "print(d['metadata'])")"
["$(echo"$ext"| j "print('<strong>the pasture</strong>' in d['content'] and '<script' not in d['content'] and d['updated_at'][:10] == '$(date -u +%F)')")"= True ]\
&& ok "the extended description is rendered from Markdown, sanitized, dated today"|| ko "the extended description: $ext"
["$(curl -s "$P/api/v1/instance/privacy_policy"| j "print('<em>little</em>' in d['content'] and d['updated_at'][:10] == '$(date -u +%F)')")"= True ]\
&& ok "the privacy policy is rendered and dated"|| ko "the privacy policy: $(curl -s "$P/api/v1/instance/privacy_policy")"
["$(echo"$tos"| j "print(d['effective_date'] == '2026-10-01' and d['effective'] is True and 'Be kind ($run)' in d['content'] and d['succeeded_by'] is None)")"= True ]\
&& ok "the terms of service answer in Mastodon 4.4's shape"|| ko "the terms of service: $tos"
&& ok "the terms answer for their date and no other"|| ko "the terms by date answer wrongly"
curl -s "$P/oauth/login"| grep -q "Sign in to $title"&& ok "the OAuth sign-in page names the server"|| ko "the OAuth sign-in page does not name the server"
["$(code_of "$ADMIN" POST /clientapi/admin/settings -d "$(settings_form "$saved""f['contactAccountId']='0123456789abcdef01234567'")")"=422]\
&& ok "a contact that is no persona here is refused"|| ko "a contact that is no persona was taken"
["$(code_of "$ADMIN" POST /clientapi/admin/settings -d "$(settings_form "$saved""f['title']='x'*65")")"=400]\
&& ok "a title over 64 characters is refused"|| ko "a title over 64 characters was taken"
edited=$(api "$ADMIN" POST /clientapi/admin/settings -d "$(settings_form "$saved""f['rules']=[{'id': '$r1', 'text': 'No spam at all $run'}, {'text': 'Third $run'}]")")
["$(echo"$edited"| j "print([(r['id'] == '$r1', r['text'], r['isDeleted']) for r in d['rules']] == [(True, 'No spam at all $run', False), (False, 'Third $run', False), (False, 'Be kind $run', True)])")"= True ]\
&& ok "an edited rule keeps its id, a new one gets one, one left out is kept deleted"|| ko "the rules after an edit: $(echo"$edited"| j "print(d['rules'])")"
["$(curl -s "$P/api/v1/instance/rules"| j "print('$r2' not in [r['id'] for r in d] and len(d) == 2)")"= True ]\
&& ok "a deleted rule is no longer listed"|| ko "a deleted rule is still listed"
api "$ADMIN" POST /clientapi/admin/domainblocks/insert -d "{\"domain\":\"$shown\",\"suspend\":true,\"publicComment\":\"spam $run\",\"published\":true,\"obfuscate\":true}" >/dev/null
api "$ADMIN" POST /clientapi/admin/domainblocks/insert -d "{\"domain\":\"$quiet\",\"suspend\":false,\"publicComment\":\"quiet $run\"}" >/dev/null
obfuscated=$(python3 -c "
d='$shown'; v=len(d)//4
print(''.join('*' if v < i < len(d)-v and c != '.' else c for i,c in enumerate(d)))")
["$(echo"$blocks"| j "print([b for b in d if b['digest'] == '$digest'] == [{'domain': '$obfuscated', 'digest': '$digest', 'severity': 'suspend', 'comment': 'spam $run'}])")"= True ]\
&& ok "a published block is listed, its name obfuscated, with its digest"|| ko "the published block: $blocks"
["$(echo"$blocks"| j "print(not any('quiet $run' == b['comment'] for b in d))")"= True ]&& ok "an unpublished block stays out"|| ko "an unpublished block is listed"
api "$ADMIN" POST "/clientapi/admin/domainblocks/delete?domain=$shown" >/dev/null
api "$ADMIN" POST "/clientapi/admin/domainblocks/delete?domain=$quiet" >/dev/null
["$(curl -s "$P/api/v1/instance/domain_blocks"| j "print(any(b['digest'] == '$digest' for b in d))")"= False ]&& ok "and leaves the list when deleted"|| ko "a deleted block is still listed"
&& ok "a report keeps the rules that exist (a deleted one too) and becomes a violation"|| ko "the report: $report"
["$(api "$ADMIN" GET "/clientapi/moderator/reports?resolved=false"| j "print([sorted(r['rules']) for r in d if r['id'] == '$report_id'] == [sorted(['No spam at all $run', 'Be kind $run'])])")"= True ]\
&& ok "the moderators read the rules' texts"|| ko "the moderators do not see the rules: $(api "$ADMIN" GET "/clientapi/moderator/reports?resolved=false"| head -c 400)"
["$(code_of "$ADMIN" POST "/clientapi/moderator/reports/$report_id/resolve")"=200]&& ok "and resolve it"|| ko "the report could not be resolved"
announcement=$(api "$ADMIN" POST /clientapi/admin/announcements/insert -d "{\"text\":\"The pasture is *open* $run\",\"isPublished\":true}")
aid=$(echo"$announcement"| j "print(d['id'])")
[ -n "$aid"]&&["$(echo"$announcement"| j "print(d['isShown'] and '<em>open</em>' in d['html'])")"= True ]&& ok "the administrator publishes an announcement"|| ko "no announcement: $announcement"
["$(curl -s -H "$AH""$P/api/v1/announcements"| j "print([(a['read'], '<em>open</em>' in a['content'], a['reactions']) for a in d if a['id'] == '$aid'] == [(False, True, [])])")"= True ]\
&& ok "a persona reads it, unread"|| ko "the persona's announcements: $(curl -s -H "$AH""$P/api/v1/announcements")"
["$(curl -s "$P/api/v1/announcements"| j "print([('read' in a) for a in d if a['id'] == '$aid'] == [False])")"= True ]\
&& ok "an anonymous reader gets it without a read state"|| ko "the anonymous announcements: $(curl -s "$P/api/v1/announcements")"
["$(curl -s -o /dev/null -w '%{http_code}' -X POST -H "$AH""$P/api/v1/announcements/$aid/dismiss")"=200]\
&&["$(curl -s -H "$AH""$P/api/v1/announcements"| j "print([a['read'] for a in d if a['id'] == '$aid'])")"="[True]"]\
&&["$(curl -s -H "$BH""$P/api/v1/announcements"| j "print([a['read'] for a in d if a['id'] == '$aid'])")"="[False]"]\
&& ok "dismissed by one persona, still unread for the other"|| ko "dismissing is not per persona"
curl -s -o /dev/null -X PUT -H "$AH""$P/api/v1/announcements/$aid/reactions/$thumbs"
["$(curl -s -H "$AH""$P/api/v1/announcements"| j "print([a['reactions'] for a in d if a['id'] == '$aid'] == [[{'name': '👍', 'count': 1, 'me': True}]])")"= True ]\
&&["$(curl -s -H "$BH""$P/api/v1/announcements"| j "print([a['reactions'][0]['me'] for a in d if a['id'] == '$aid'])")"="[False]"]\
&& ok "a reaction is counted, and known as its own to the persona alone"|| ko "the reaction: $(curl -s -H "$AH""$P/api/v1/announcements")"
["$(curl -s -o /dev/null -w '%{http_code}' -X PUT -H "$AH""$P/api/v1/announcements/$aid/reactions/abc")"=422]\
&& ok "a reaction that is no emoji is refused (422)"|| ko "a reaction that is no emoji was taken"
api "$ADMIN" POST /clientapi/admin/announcements/update -d "{\"id\":\"$aid\",\"text\":\"The pasture is *open*, edited $run\",\"isPublished\":true}" >/dev/null
["$(curl -s -H "$AH""$P/api/v1/announcements"| j "print([a['reactions'] for a in d if a['id'] == '$aid'])")"="[[]]"]&& ok "and taken back"|| ko "the reaction stayed"
api "$ADMIN" POST "/clientapi/admin/announcements/delete?id=$aid" >/dev/null
["$(curl -s -H "$AH""$P/api/v1/announcements"| j "print(any(a['id'] == '$aid' for a in d))")"= False ]&& ok "a deleted announcement is gone"|| ko "a deleted announcement is still listed"
wait"$sse" 2>/dev/null
grep -q '^event: announcement$'"$work/sse"&& grep -q "edited $run""$work/sse"&& ok "the user stream tells it made and edited"|| ko "no announcement event on the user stream"
grep -q '^event: announcement.reaction$'"$work/sse"&& ok "the user stream tells its reactions"|| ko "no announcement.reaction event"
grep -A1 '^event: announcement.delete$'"$work/sse"| grep -q "data: $aid"&& ok "the user stream tells it deleted"|| ko "no announcement.delete event"
echo" media limits"
noise_png "$work/noise.png"64
limited=$(api "$ADMIN" POST /clientapi/admin/settings -d "$(settings_form "$edited""f['media']['maxImageBytes']=2097152")")
&& ok "an image limit from the settings is what the instance API advertises"|| ko "the image limit did not reach the instance API"
quota=$(api "$ADMIN" POST /clientapi/admin/settings -d "$(settings_form "$limited""f['media']['quotaBytesPerRoot']=1024")")
["$(echo"$quota"| j "print(d['media']['effectiveQuotaBytesPerRoot'])")"=1024]&& ok "a quota of 1 KB takes effect"|| ko "the quota: $(echo"$quota"| j "print(d['media'])")"
&& ok "an uploaded thumbnail is the instance's"|| ko "the thumbnail: $(echo"$thumb"| head -c 300)"
["$(pfetch -o /dev/null -w '%{http_code} %{content_type}'"$turl")"="200 image/jpeg"]&& ok "served, re-encoded as a JPEG"|| ko "the thumbnail is not served"
&& ok "removed, the default picture is back and the old one trashed"|| ko "the thumbnail could not be removed"
echo" relays from the settings"
relays=$(api "$ADMIN" GET /clientapi/admin/relays)
["$(echo"$relays"| j "print(sorted(r['address'] for r in d if r['source'] == 'configuration') == ['https://aoderelay.test/actor', 'https://relay.test/actor'])")"= True ]\
&& ok "the configured relays are listed as the configuration's"|| ko "the relays: $relays"
["$(code_of "$ADMIN" POST "/clientapi/admin/relays/delete?address=https://relay.test/actor")"=409]\
&& ok "a configured relay can't be removed from the page (409)"|| ko "a configured relay was removed from the page"
extra="https://relay.test/inbox"
["$(code_of "$ADMIN" POST /clientapi/admin/relays/insert -d "{\"address\":\"$extra\"}")"=202]&& ok "a relay is added through the settings"|| ko "the relay was not added"
until_true 30'[ "$(relay_state "$extra")" = 1 ]'&& ok "and subscribed to, accepted"\
||{["$(relay_state "$extra")"=0]&& ok "and subscribed to (requested; the relay answers a second subscription of the server's slowly)"|| ko "the relay added was never asked ($(relay_state "$extra"))";}
["$(api "$ADMIN" GET /clientapi/admin/relays | j "print([r['source'] for r in d if r['address'] == '$extra'])")"="['settings']"]\
&& ok "listed as the settings'"|| ko "the added relay is not listed as the settings'"
&& ok "and the relay's configured subscription stays (no Undo for an actor still named)"|| ko "removing the added address ended the configured subscription"
echo" activity"
["$(curl -s "$P/api/v1/instance/activity")"="[]"]&& ok "the weekly activity is not published by default"|| ko "the activity was published unasked"
curl -s -o /dev/null -X POST -H "$AH""$P/api/v1/statuses" -d "status=a post this week $run&visibility=public"
api "$ADMIN" POST /clientapi/admin/settings -d "$(settings_form "$reset""f['publishActivity']=True")" >/dev/null
["$(curl -s "$P/api/v1/instance/activity"| j "print(len(d) == 12 and int(d[0]['statuses']) >= 1 and d[0]['logins'] == '0' and d[0]['registrations'].isdigit() and int(d[0]['week']) > int(d[1]['week']))")"= True ]\
&& ok "published, twelve weeks, this one first, counting its posts"|| ko "the activity: $(curl -s "$P/api/v1/instance/activity"| head -c 300)"
echo" the staff's log"
log=$(api "$ADMIN" GET "/clientapi/moderator/log?limit=100")
&& ok "each by the administrator's login, the command line's, or nobody's"|| ko "the log names someone else: $(echo"$log"| j "print(sorted({str(i.get('by')) for i in d['items']}))")"
echo"$log"| grep -qE '127\.0\.0\.1|11\.42\.|::1'&& ko "the log holds an address"|| ok "and holds no address"
page=$(api "$ADMIN" GET "/clientapi/moderator/log?limit=2")
next=$(echo"$page"| j "print(d['next'])")
first=$(echo"$page"| j "print(' '.join(i['id'] for i in d['items']))")
[ -n "$next"]&&["$(api "$ADMIN" GET "/clientapi/moderator/log?limit=2&before=$next"| j "print(len(d['items']) == 2 and not set(i['id'] for i in d['items']) & set('$first'.split()) and all(i['id'] < '$next' for i in d['items']))")"= True ]\
&& ok "paged newest first"|| ko "the log does not page"
echo" back to the defaults"
restored=$(api "$ADMIN" POST /clientapi/admin/settings -d "$(settings_form "$before""pass")")
["$(curl -s "$P/api/v1/instance"| j "print(d['title'] == '$(echo"$before"| j "print(d['title'])")' and d['rules'] == [] and d['contact_account'] is None)")"= True ]\