# Mobilizon 5.2.4: a group and its events. alice_mobilizon follows the group, which announces the events its members # organise; an event arrives as an Event with its dates and place; she joins it and leaves; comments both ways; an edit # and comments closed; deletes; a group post; an event kept for decePubClient's e2e; the unfollow. An event made through its API without options has its comments closed # (`commentsEnabled: false`), which PrivaPub honours, so this one opens them. Mobilizon's API is GraphQL (/api), signed in by its login mutation; what it holds is read from # its PostGIS (database mobilizon). MZ=https://mobilizon.test:6443 mz_sql() { podman exec pasture-postgis psql -U pasture -d mobilizon -tAc "$1" 2>/dev/null; } # mz [variables as JSON]: a GraphQL request as mzuser, its JSON answer mz() { curl -sk --resolve mobilizon.test:6443:127.0.0.1 "$MZ/api" -H "Authorization: Bearer $MZT" -H 'Content-Type: application/json' \ -d "$(python3 -c 'import json, sys; print(json.dumps({"query": sys.argv[1], "variables": json.loads(sys.argv[2])}))' "$1" "${2:-{\}}")" } p_home_has() { curl -s -H "$PH" "$P/api/v1/timelines/home?limit=40" | j "print(next((o['id'] for o in ((s.get('reblog') or s) for s in d) if '$1' in (o['content'] or '') or '$1' in ((o.get('privapub') or {}).get('title') or '')), ''))"; } echo "mobilizon" [ -s "$here/.state/mobilizon.token" ] && ok "Mobilizon credentials for mzuser" || { ko "Mobilizon credentials"; return 1; } credentials=$(cat "$here/.state/mobilizon.token") MZT=$(curl -sk --resolve mobilizon.test:6443:127.0.0.1 "$MZ/api" -H 'Content-Type: application/json' \ -d "{\"query\":\"mutation{login(email:\\\"${credentials%%:*}\\\",password:\\\"${credentials#*:}\\\"){accessToken}}\"}" | j "print(d['data']['login']['accessToken'])") [ -n "$MZT" ] && ok "mzuser signs in to Mobilizon's GraphQL API" || { ko "mzuser cannot sign in"; return 1; } PT=$(privapub_token alice_mobilizon) PH="Authorization: Bearer $PT" [ -n "$PT" ] && ok "PrivaPub token for alice_mobilizon" || { ko "PrivaPub token for alice_mobilizon"; return 1; } echo " the group and its follower" me=$(mz '{loggedPerson{id}}' | j "print(d['data']['loggedPerson']['id'])") mz 'mutation($n:String!){createGroup(preferredUsername:$n,name:"Pasture events",summary:"

events in the pasture

",visibility:PUBLIC,openness:OPEN){id}}' \ '{"n":"pastureevents"}' >/dev/null group=$(mz '{group(preferredUsername:"pastureevents"){id}}' | j "print(d['data']['group']['id'])") [ -n "$group" ] && ok "mzuser runs the group pastureevents" || ko "the group could not be made" group_on_p=$(curl -s -H "$PH" "$P/api/v2/search?q=pastureevents@mobilizon.test&resolve=true&type=accounts" | j "print(d['accounts'][0]['id'])") [ -n "$group_on_p" ] && ok "PrivaPub resolves @pastureevents@mobilizon.test" || ko "PrivaPub cannot resolve the group" # a run before this one left the follow: unfollow first, so the follow below is a new request curl -s -o /dev/null -X POST -H "$PH" "$P/api/v1/accounts/$group_on_p/unfollow"; sleep 3 curl -s -o /dev/null -X POST -H "$PH" "$P/api/v1/accounts/$group_on_p/follow" until_true 45 '[ "$(curl -s -H "$PH" "$P/api/v1/accounts/relationships?id[]=$group_on_p" | j "print(d[0][\"following\"])")" = "True" ]' \ && ok "alice_mobilizon follows the group (Accept arrived)" || ko "the group's Accept never arrived" echo " events" title="A picnic in the pasture $(date +%s)" begins=$(date -u -d '+3 days' +%Y-%m-%dT18:00:00Z); ends=$(date -u -d '+3 days' +%Y-%m-%dT21:00:00Z) event=$(mz 'mutation($t:String!,$b:DateTime!,$e:DateTime!,$o:ID!,$g:ID){createEvent(title:$t,description:"

bring bread

",beginsOn:$b,endsOn:$e,organizerActorId:$o,attributedToId:$g,visibility:PUBLIC,joinOptions:FREE,options:{commentModeration:ALLOW_ALL},physicalAddress:{description:"Villa Borghese",locality:"Roma",country:"Italia",geom:"12.4922;41.9142"}){id uuid url}}' \ "{\"t\":\"$title\",\"b\":\"$begins\",\"e\":\"$ends\",\"o\":\"$me\",\"g\":\"$group\"}") event_id=$(echo "$event" | j "print(d['data']['createEvent']['id'])"); event_url=$(echo "$event" | j "print(d['data']['createEvent']['url'])") [ -n "$event_id" ] && ok "mzuser organises an event for the group" || ko "the event could not be made: $(echo "$event" | cut -c1-200)" until_true 60 '[ -n "$(p_home_has "$title")" ]' && ok "the group's event reaches alice_mobilizon's home" || ko "the event never arrived" e_on_p=$(p_home_has "$title") event_json=$(curl -s -H "$PH" "$P/api/v1/statuses/$e_on_p") [ "$(echo "$event_json" | j "e = (d.get('privapub') or {}).get('event') or {}; print(e.get('start', '')[:16], e.get('end', '')[:16])")" = "${begins:0:16} ${ends:0:16}" ] \ && ok "it arrives as an event with its start and end" || ko "the event's dates are missing ($(echo "$event_json" | j "print((d.get('privapub') or {}).get('event'))"))" echo "$event_json" | j "e = (d.get('privapub') or {}).get('event') or {}; print(any('Borghese' in ((p.get('name') or '') + (p.get('address') or '')) and p.get('latitude') for p in e.get('places', [])))" | grep -q True \ && ok "and with its place, located" || ko "the event's place is missing" echo " participation" # alice joins the event (owner decision 2026-10-05): a Join Mobilizon takes at once for a free event, then a Leave participation() { curl -s -H "$PH" "$P/api/v1/statuses/$e_on_p" | j "print(((d.get('privapub') or {}).get('event') or {}).get('participation'))"; } mz_participant() { mz_sql "select p.role from participants p join actors a on a.id = p.actor_id join events e on e.id = p.event_id where a.url like '%alice_mobilizon%' and e.id = $event_id"; } curl -s -o /dev/null -X POST -H "$PH" "$P/api/privapub/v1/statuses/$e_on_p/join" until_true 45 '[ "$(mz_participant)" = "participant" ]' && ok "alice_mobilizon's Join makes her a participant on Mobilizon" || ko "the Join never made a participant ($(mz_participant))" until_true 45 '[ "$(participation)" = "accepted" ]' && ok "Mobilizon's Accept reaches PrivaPub" || ko "the participation stays $(participation)" curl -s -o /dev/null -X POST -H "$PH" "$P/api/privapub/v1/statuses/$e_on_p/leave" until_true 45 '[ -z "$(mz_participant)" ]' && ok "her Leave takes her off the event on Mobilizon" || ko "she is still a participant after leaving" [ "$(participation)" = "None" ] && ok "and PrivaPub forgets it" || ko "PrivaPub still shows a participation" echo " comments" curl -s -o /dev/null -X POST -H "$PH" "$P/api/v1/statuses" -d "status=a PrivaPub comment on the picnic&in_reply_to_id=$e_on_p&visibility=public" until_true 45 '[ "$(mz_sql "select count(*) from comments where text like '"'%a PrivaPub comment on the picnic%'"' and deleted_at is null")" -ge 1 ]' \ && ok "alice_mobilizon's reply becomes a comment on the event" || ko "the comment never reached Mobilizon" comment=$(mz 'mutation($e:ID!){createComment(eventId:$e,text:"

a Mobilizon comment on the picnic

"){id}}' "{\"e\":\"$event_id\"}" | j "print(d['data']['createComment']['id'])") until_true 45 '[ "$(curl -s -H "$PH" "$P/api/v1/statuses/$e_on_p/context" | j "print(any(\"a Mobilizon comment on the picnic\" in s[\"content\"] for s in d[\"descendants\"]))")" = "True" ]' \ && ok "mzuser's comment threads under the event on PrivaPub" || ko "Mobilizon's comment missing on PrivaPub" echo " edits and deletes" mz 'mutation($e:ID!,$t:String!){updateEvent(eventId:$e,title:$t){id}}' "{\"e\":\"$event_id\",\"t\":\"$title, moved indoors\"}" >/dev/null until_true 45 'curl -s -H "$PH" "$P/api/v1/statuses/$e_on_p" | grep -q "moved indoors"' && ok "the event's edit reaches PrivaPub" || ko "the event's edit not applied" mz 'mutation($e:ID!){updateEvent(eventId:$e,options:{commentModeration:CLOSED}){id}}' "{\"e\":\"$event_id\"}" >/dev/null until_true 45 '[ "$(curl -s -H "$PH" "$P/api/v1/statuses/$e_on_p" | j "print((d.get(\"privapub\") or {}).get(\"locked\"))")" = "True" ]' \ && ok "closing its comments locks the event on PrivaPub" || ko "the event's closed comments not applied" [ "$(curl -s -o /dev/null -w '%{http_code}' -X POST -H "$PH" "$P/api/v1/statuses" -d "status=too late&in_reply_to_id=$e_on_p&visibility=public")" = "422" ] \ && ok "and a reply to it is refused" || ko "a reply to the closed event was taken" mz 'mutation($c:ID!){deleteComment(commentId:$c){id}}' "{\"c\":\"$comment\"}" >/dev/null until_true 45 '[ "$(curl -s -H "$PH" "$P/api/v1/statuses/$e_on_p/context" | j "print(any(\"a Mobilizon comment on the picnic\" in s[\"content\"] for s in d[\"descendants\"]))")" = "False" ]' \ && ok "mzuser's deleted comment leaves PrivaPub" || ko "the deleted comment stays on PrivaPub" mz 'mutation($e:ID!){deleteEvent(eventId:$e){id}}' "{\"e\":\"$event_id\"}" >/dev/null until_true 45 '[ "$(curl -s -o /dev/null -w "%{http_code}" -H "$PH" "$P/api/v1/statuses/$e_on_p")" = "404" ]' \ && ok "the deleted event leaves PrivaPub" || ko "the deleted event stays on PrivaPub" echo " group posts" post_title="News from the pasture $(date +%s)" mz 'mutation($g:ID!,$t:String!){createPost(attributedToId:$g,title:$t,body:"

the picnic moved indoors

",visibility:PUBLIC){id}}' \ "{\"g\":\"$group\",\"t\":\"$post_title\"}" >/dev/null until_true 60 '[ -n "$(p_home_has "$post_title")" ]' && ok "the group's post reaches alice_mobilizon's home, with its title" || ko "the group's post never arrived" echo " an event kept for decePub's tests" # decePubClient's e2e joins it from its card (tests/e2e/Actions/EventTests.cs); the one a run before this one kept goes kept="$here/.state/mobilizon-kept-event.json" [ -s "$kept" ] && mz 'mutation($e:ID!){deleteEvent(eventId:$e){id}}' "{\"e\":\"$(j "print(d['id'])" < "$kept")\"}" >/dev/null kept_title="A concert kept for decePub $(date +%s)" kept_begins=$(date -u -d '+10 days' +%Y-%m-%dT19:00:00Z); kept_ends=$(date -u -d '+10 days' +%Y-%m-%dT22:00:00Z) mz 'mutation($t:String!,$b:DateTime!,$e:DateTime!,$o:ID!,$g:ID){createEvent(title:$t,description:"

come and listen

",beginsOn:$b,endsOn:$e,organizerActorId:$o,attributedToId:$g,visibility:PUBLIC,joinOptions:FREE,options:{commentModeration:ALLOW_ALL},physicalAddress:{description:"Piazza del Popolo",locality:"Roma",country:"Italia",geom:"12.4763;41.9109"}){id url}}' \ "{\"t\":\"$kept_title\",\"b\":\"$kept_begins\",\"e\":\"$kept_ends\",\"o\":\"$me\",\"g\":\"$group\"}" | j "print(json.dumps(d['data']['createEvent']))" > "$kept" until_true 60 '[ -n "$(p_home_has "$kept_title")" ]' && ok "an event kept for decePub's tests reaches PrivaPub" || ko "the kept event never arrived" echo " unfollow" curl -s -o /dev/null -X POST -H "$PH" "$P/api/v1/accounts/$group_on_p/unfollow" until_true 45 '[ "$(mz_sql "select count(*) from followers f join actors a on a.id = f.actor_id where a.url like '"'%alice_mobilizon%'"'")" = "0" ]' \ && ok "alice_mobilizon's unfollow reaches the group" || ko "the group still counts alice_mobilizon" echo " statistics" stats_check mobilizon.test mobilizon