# The server's own settings (completion program R5), edited as the administrator through /clientapi/admin: what the # instance API (v1 and v2), NodeInfo, the instance actor, the OAuth sign-in page and the instance's rules, description, # privacy policy and terms say; published domain blocks (and an unpublished one that stays out); a report naming rules # reaching the moderators with their texts; an announcement listed, dismissed by one persona, reacted to and streamed; # media limits from the settings (a 1 KB quota refusing an upload, then reset); the thumbnail; a relay added through the # settings and removed (the configured ones can't be); the weekly activity once published; the staff's log listing all # of it, without an address. It puts every setting back as it found it at the end, so other scenarios see the usual # instance. Needs the relay and aoderelay peers (batch A); mastodon is not used. . "$here/peers/relay.sh" api() { local jwt=$1 method=$2 path=$3; shift 3; curl -s -X "$method" -H "Authorization: Bearer $jwt" -H 'Content-Type: application/json' "$P$path" "$@"; } code_of() { local jwt=$1 method=$2 path=$3; shift 3; curl -s -o /dev/null -w '%{http_code}' -X "$method" -H "Authorization: Bearer $jwt" -H 'Content-Type: application/json' "$P$path" "$@"; } # settings_form : 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']} $2 print(json.dumps(f))" "$1"; } relay_state() { podman exec pasture-mongo mongosh --quiet PrivaPub --eval 'var s=db.RelaySubscription.findOne({Configured:"'"$1"'"}); print(s ? s.State : "none")'; } # noise_png : 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 podman exec -w /app pasture-privapub /app/PrivaPub admin promote "$ROOT_USER" >/dev/null 2>&1 || true ADMIN=$(curl -s -X POST "$P/clientapi/user/login" -H 'Content-Type: application/json' -d "{\"userName\":\"$ROOT_USER\",\"password\":\"$ROOT_PASS\"}" | j "print(d['token'])") [ -n "$ADMIN" ] && ok "the pasture's root signs in as the administrator" || { ko "no administrator's login"; return 1; } AT=$(privapub_token alice_instance); BT=$(privapub_token bob_instance) AH="Authorization: Bearer $AT"; BH="Authorization: Bearer $BT" alice_id=$(curl -s -H "$AH" "$P/api/v1/accounts/verify_credentials" | j "print(d['id'])") bob_id=$(curl -s -H "$BH" "$P/api/v1/accounts/verify_credentials" | j "print(d['id'])") [ -n "$alice_id" ] && [ -n "$bob_id" ] && ok "two personas for the scenario" || { ko "no personas for the scenario"; return 1; } before=$(api "$ADMIN" GET /clientapi/admin/settings) [ -n "$(echo "$before" | j "print(d['title'])")" ] && ok "the administrator reads the settings" || { ko "the settings could not be read"; return 1; } [ "$(code_of "$AT" GET /clientapi/admin/settings)" != 200 ] && ok "a persona's token reads no settings" || ko "a persona's token read the settings" echo " the server described" title="Pasture Commons $run" form=$(settings_form "$before" " f.update(title='$title', shortDescription='A test field for $run.', description='Welcome to **the pasture** $run.', contactEmail='staff@privapub.test', contactAccountId='$alice_id', privacyPolicy='We keep *little* about you ($run).', termsOfService='Be kind ($run).', termsEffectiveAt='2026-10-01T00:00:00Z') f['rules']=[{'text': 'No spam $run', 'hint': 'Ads included'}, {'text': 'Be kind $run'}]") saved=$(api "$ADMIN" POST /clientapi/admin/settings -d "$form") r1=$(echo "$saved" | j "print([r['id'] for r in d['rules'] if r['text'] == 'No spam $run'][0])") r2=$(echo "$saved" | j "print([r['id'] for r in d['rules'] if r['text'] == 'Be kind $run'][0])") [ -n "$r1" ] && [ -n "$r2" ] && [ "$(echo "$saved" | j "print(d['title'], d['contactAccount']['userName'])")" = "$title alice_instance" ] \ && 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'])")" actor=$(pfetch -H 'Accept: application/activity+json' https://privapub.test/peasants/privapub) [ "$(echo "$actor" | j "print(d['name'] == '$title' and 'A test field for $run.' in d['summary'])")" = True ] \ && ok "the instance actor carries the name and the short description" || ko "the instance actor: $(echo "$actor" | j "print(d['name'], d['summary'])")" [ "$(curl -s "$P/api/v1/instance/rules" | j "print([(r['id'], r['text']) for r in d] == [('$r1', 'No spam $run'), ('$r2', 'Be kind $run')])")" = True ] \ && ok "/instance/rules lists them in order" || ko "/instance/rules: $(curl -s "$P/api/v1/instance/rules")" ext=$(curl -s "$P/api/v1/instance/extended_description") [ "$(echo "$ext" | j "print('the pasture' in d['content'] and 'little' 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")" tos=$(curl -s "$P/api/v1/instance/terms_of_service") [ "$(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" [ "$(curl -s -o /dev/null -w '%{http_code}' "$P/api/v1/instance/terms_of_service/2026-10-01")" = 200 ] \ && [ "$(curl -s -o /dev/null -w '%{http_code}' "$P/api/v1/instance/terms_of_service/2020-01-01")" = 404 ] \ && 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" echo " public domain blocks" shown="blocked-$run.example"; quiet="quiet-$run.example" 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)))") digest=$(printf '%s' "$shown" | sha256sum | cut -d' ' -f1) blocks=$(curl -s "$P/api/v1/instance/domain_blocks") [ "$(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" echo " rules in reports" report=$(curl -s -X POST -H "$AH" "$P/api/v1/reports" -d "account_id=$bob_id" -d "rule_ids[]=$r1" -d "rule_ids[]=$r2" -d "rule_ids[]=no-such-rule" --data-urlencode "comment=rules $run") report_id=$(echo "$report" | j "print(d['id'])") [ "$(echo "$report" | j "print(sorted(d['rule_ids']) == sorted(['$r1', '$r2']) and d['category'] == 'violation')")" = True ] \ && 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" echo " announcements" curl -sN --max-time 12 -H "$BH" "$P/api/v1/streaming/user" > "$work/sse" 2>/dev/null & sse=$! sleep 1 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 'open' 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'], 'open' 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" thumbs=$(python3 -c "import urllib.parse; print(urllib.parse.quote('👍'))") 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 -o /dev/null -X DELETE -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'])")" = "[[]]" ] && 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")") [ "$(curl -s "$P/api/v2/instance" | j "print(d['configuration']['media_attachments']['image_size_limit'])")" = 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'])")" [ "$(curl -s -o /dev/null -w '%{http_code}' -X POST -H "$AH" "$P/api/v2/media" -F "file=@$work/noise.png;type=image/png")" = 422 ] \ && ok "an upload over it is refused (422)" || ko "an upload over the quota was taken" reset=$(api "$ADMIN" POST /clientapi/admin/settings -d "$(settings_form "$quota" "f['media']={}")") [ "$(echo "$reset" | j "print(d['media']['effectiveQuotaBytesPerRoot'] == d['media']['configuredQuotaBytesPerRoot'] and d['media']['effectiveMaxImageBytes'] == d['media']['configuredMaxImageBytes'])")" = True ] \ && [ "$(curl -s -o /dev/null -w '%{http_code}' -X POST -H "$AH" "$P/api/v2/media" -F "file=@$work/noise.png;type=image/png")" = 200 ] \ && ok "reset, the configured limits are back and the upload goes through" || ko "the limits did not reset" echo " the thumbnail" noise_png "$work/thumb.png" 96 thumb=$(curl -s -X POST -H "Authorization: Bearer $ADMIN" -F "file=@$work/thumb.png;type=image/png" "$P/clientapi/admin/settings/thumbnail") turl=$(echo "$thumb" | j "print(d['thumbnailUrl'])") [ "$(echo "$thumb" | j "print(d['thumbnailIsDefault'])")" = False ] && [ "$(curl -s "$P/api/v1/instance" | j "print(d['thumbnail'])")" = "$turl" ] \ && [ "$(curl -s "$P/api/v2/instance" | j "print(d['thumbnail']['url'])")" = "$turl" ] \ && 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" [ "$(podman exec pasture-mongo mongosh --quiet PrivaPub --eval "print(db.MediaAttachment.countDocuments({ServerUse: 'thumbnail', TrashedAt: null}))")" = 1 ] \ && ok "its row is held by the server" || ko "the thumbnail's row is not held" [ "$(api "$ADMIN" DELETE /clientapi/admin/settings/thumbnail | j "print(d['thumbnailIsDefault'])")" = True ] \ && [ "$(curl -s "$P/api/v1/instance" | j "print(d['thumbnail'].endswith('/media/missing-header.png'))")" = True ] \ && [ "$(podman exec pasture-mongo mongosh --quiet PrivaPub --eval "print(db.MediaAttachment.countDocuments({ServerUse: 'thumbnail', TrashedAt: null}))")" = 0 ] \ && 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'" [ "$(code_of "$ADMIN" POST "/clientapi/admin/relays/delete?address=$extra")" = 202 ] && until_true 15 '[ "$(relay_state "$extra")" = none ]' \ && ok "removed, its subscription goes" || ko "the added relay could not be removed" sleep 3 [ "$(relay_state https://relay.test/actor)" = 1 ] && relay_subscribers | grep -qx privapub.test \ && 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") missing=$(echo "$log" | j " seen={i['action'] for i in d['items']} print(' '.join(sorted({'root.promote','settings.update','rule.insert','rule.update','rule.delete','domain_block.insert','domain_block.delete','report.resolve','announcement.insert','announcement.update','announcement.delete','settings.thumbnail','relay.insert','relay.delete'} - seen)))") [ -z "$missing" ] && [ -n "$log" ] && ok "it lists every act of the scenario" || ko "the log misses: $missing" [ "$(echo "$log" | j "print(all(i.get('by') in ('$ROOT_USER', 'cli') for i in d['items']))")" = True ] \ && [ "$(echo "$log" | j "print(any(i['action'] == 'root.promote' and i.get('by') == 'cli' for i in d['items']))")" = True ] \ && ok "each by the administrator's login, or the command line's" || ko "the log names someone else" 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 ] \ && [ "$(curl -s -o /dev/null -w '%{http_code}' "$P/api/v1/instance/terms_of_service")" = 404 ] \ && [ "$(curl -s "$P/api/v1/instance/activity")" = "[]" ] \ && [ "$(curl -s "$P/nodeinfo/2.1" | j "print(d['metadata']['nodeName'])")" = "$(echo "$before" | j "print(d['title'])")" ] \ && ok "every setting is as the scenario found it" || ko "the settings were not put back: $(echo "$restored" | head -c 300)"