mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-18 17:10:40 +00:00
A fresh client sends the directory the search the app sends, and must find ci_group and ci_channel; then "ci seed" and "ci avatar" are followed page by page into seed.md, with each page's entries and size against maxServiceBodyLength. The message search stays. The seeded entries name the cases they serve again.
541 lines
21 KiB
Bash
Executable File
541 lines
21 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Test backend on the app runner: a chat relay and a directory service on the preset servers, with groups and
|
|
# channels registered and approved by a CLI peer, and searches that must find them, sent as a message and as the
|
|
# app sends them; and for SimpleX names, a local SMP server with a name resolver, names claimed by channels and by
|
|
# contacts that accept connections.
|
|
# Usage:
|
|
# scripts/ci/ios-sim/backend.sh start <backend zip> <work dir> writes the directory's short address to <work dir>/directory-link,
|
|
# the names server's address to <work dir>/names-server
|
|
# and what was seeded to <work dir>/seed.md
|
|
# scripts/ci/ios-sim/backend.sh stop <work dir> stops everything it started, prints the logs
|
|
# scripts/ci/ios-sim/backend.sh directory-stop <work dir> stops the directory only
|
|
# scripts/ci/ios-sim/backend.sh directory-start <work dir> starts it again
|
|
# scripts/ci/ios-sim/backend.sh listings <work dir> prints the directory's listed groups and channels
|
|
# Everything it starts keeps running between start and stop.
|
|
|
|
set -euo pipefail
|
|
|
|
directory_name="SimpleX Directory"
|
|
admin=ci_admin
|
|
search_text="ci seed"
|
|
avatar_search_text="ci avatar"
|
|
long_name=ci_long_name_entry_for_layout_truncation_checks
|
|
# maxServiceBodyLength in src/Simplex/Chat/Protocol.hs
|
|
max_service_body=10737
|
|
names_resolver_port=18080
|
|
|
|
# Runs one chat command in a CLI client that stays online for <secs> after it; the output is in $work/out.
|
|
chat() {
|
|
local db=$1 secs=$2 cmd=$3
|
|
shift 3
|
|
local status=0
|
|
"$bin/simplex-chat" -d "$work/$db" -e "$cmd" -t "$secs" --execute-log all "$@" < /dev/null > "$work/out" 2>&1 || status=$?
|
|
cat "$work/out" >> "$work/$db-cli.log"
|
|
echo "::group::$db: $(cut -c1-200 <<< "$cmd")"
|
|
cat "$work/out"
|
|
echo "::endgroup::"
|
|
return "$status"
|
|
}
|
|
|
|
admin() {
|
|
chat "$admin" "$1" "$2" --user-display-name "$admin"
|
|
}
|
|
|
|
# A command that resolves a SimpleX name: given -s, the client uses that server alone, with every role, names included.
|
|
# -s also saves it among the user's servers, and later runs would put queues on it that the directory can't reach
|
|
# (it routes to unknown servers through a preset proxy), so it is then kept for names only.
|
|
named() {
|
|
local db=$1 user=$2 secs=$3 cmd=$4
|
|
local status=0
|
|
chat "$db" "$secs" "$cmd" --user-display-name "$user" -s "$names_server" || status=$?
|
|
db_query "$db" "UPDATE protocol_servers SET role_storage = 0, role_proxy = 0, role_names = 1 WHERE host = 'localhost'"
|
|
return "$status"
|
|
}
|
|
|
|
expect_out() {
|
|
if ! grep -qE "$1" "$work/out"; then
|
|
echo "error: \"$1\" not in the output"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
expect_no_error() {
|
|
if grep -qiE "error" "$work/out"; then
|
|
echo "error: the command failed"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Each run keeps the client online for a few seconds, which also lets the handshakes it waits for progress.
|
|
admin_until() {
|
|
local pattern=$1 secs=$2 cmd=$3
|
|
local deadline=$((SECONDS + secs))
|
|
until admin 5 "$cmd" && grep -qE "$pattern" "$work/out"; do
|
|
if [ "$SECONDS" -ge "$deadline" ]; then
|
|
echo "error: \"$pattern\" not shown by \"$cmd\" after $secs s"
|
|
return 1
|
|
fi
|
|
done
|
|
}
|
|
|
|
db_query() {
|
|
sqlite3 "$work/$1_chat.db" "$2"
|
|
}
|
|
|
|
listings_query="SELECT gp.display_name AS name, gp.short_descr AS description, g.summary_current_members_count AS members, length(gp.image) AS image_characters
|
|
FROM sx_directory_group_regs r
|
|
JOIN groups g ON g.group_id = r.group_id
|
|
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
|
|
WHERE r.group_reg_status = 'active'
|
|
ORDER BY g.summary_current_members_count DESC, r.group_id"
|
|
|
|
start_relay() {
|
|
nohup "$bin/simplex-chat" -d "$work/relay" --relay --headless --user-display-name ci_relay < /dev/null > "$work/relay.log" 2>&1 &
|
|
echo $! > "$work/relay.pid"
|
|
local deadline=$((SECONDS + 120))
|
|
until grep -qE '^https://[^ ]+/r#' "$work/relay.log"; do
|
|
if ! kill -0 "$(cat "$work/relay.pid")" 2> /dev/null || [ "$SECONDS" -ge "$deadline" ]; then
|
|
echo "error: the relay did not create its address"
|
|
cat "$work/relay.log"
|
|
return 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
relay_link=$(grep -m 1 -oE '^https://[^ ]+/r#[^ ]+' "$work/relay.log")
|
|
echo "relay address: $relay_link"
|
|
}
|
|
|
|
# The address is created here, like the directory tests do, with auto-accept on before the service subscribes to it.
|
|
create_directory() {
|
|
chat directory 1 "/ad pq_ratchet=on" --create-bot-display-name "$directory_name"
|
|
directory_link=$(new_address)
|
|
chat directory 1 "/auto_accept on"
|
|
expect_out "^auto_accept on"
|
|
echo "$directory_link" > "$work/directory-link"
|
|
echo "directory address: $directory_link"
|
|
}
|
|
|
|
start_directory() {
|
|
# -y: the service adds its own tables to the database the CLI created, and would otherwise ask on stdin
|
|
nohup "$bin/simplex-directory-service" -d "$work/directory" -y --super-users "$1" < /dev/null >> "$work/directory.log" 2>&1 &
|
|
echo $! > "$work/directory.pid"
|
|
echo "$1" > "$work/super-user"
|
|
sleep 5
|
|
if ! kill -0 "$(cat "$work/directory.pid")" 2> /dev/null; then
|
|
echo "error: the directory service exited"
|
|
cat "$work/directory.log"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
stop_directory() {
|
|
local pid deadline
|
|
pid=$(cat "$work/directory.pid")
|
|
kill -INT "$pid" 2> /dev/null || true
|
|
deadline=$((SECONDS + 30))
|
|
while kill -0 "$pid" 2> /dev/null; do
|
|
if [ "$SECONDS" -ge "$deadline" ]; then
|
|
kill -KILL "$pid" 2> /dev/null || true
|
|
fi
|
|
sleep 1
|
|
done
|
|
}
|
|
|
|
# A super user is known by contact ID, which exists only after the admin connects, so the service starts
|
|
# with nobody and is restarted with the admin.
|
|
connect_admin() {
|
|
admin 10 "/c $directory_link"
|
|
expect_out "connection request sent!"
|
|
admin_until "Welcome to $directory_name" 180 "/tail @'$directory_name' 10"
|
|
local id
|
|
id=$(db_query directory "SELECT contact_id FROM contacts WHERE local_display_name = '$admin'")
|
|
stop_directory
|
|
start_directory "$id:$admin"
|
|
echo "directory restarted with super user $id:$admin"
|
|
}
|
|
|
|
# The directory sends each registration to super users as a message with the exact /approve command,
|
|
# and tells the owner "The group ID N (name) is approved".
|
|
approve() {
|
|
local name=$1
|
|
admin_until "/approve [0-9]+:$name [0-9]+" 300 "/tail @'$directory_name' 20"
|
|
local cmd
|
|
cmd=$(grep -oE "/approve [0-9]+:$name [0-9]+" "$work/out" | tail -n 1)
|
|
admin 5 "@'$directory_name' $cmd"
|
|
admin_until "\($name\) is approved" 120 "/tail @'$directory_name' 20"
|
|
}
|
|
|
|
# The profile the app sends to create a group or a channel (AddGroupView, AddChannelView), with an optional image file.
|
|
group_profile() {
|
|
local name=$1 descr=$2 prefs=$3 image_file=${4:-}
|
|
if [ -n "$image_file" ]; then
|
|
jq -cn --arg n "$name" --arg d "$descr" --argjson p "$prefs" --rawfile i "$image_file" '{displayName: $n, fullName: "", shortDescr: $d, image: $i, groupPreferences: $p}'
|
|
else
|
|
jq -cn --arg n "$name" --arg d "$descr" --argjson p "$prefs" '{displayName: $n, fullName: "", shortDescr: $d, groupPreferences: $p}'
|
|
fi
|
|
}
|
|
|
|
seed_group() {
|
|
local name=$1 descr=$2 image_file=${3:-}
|
|
admin 1 "/_group 1 $(group_profile "$name" "$descr" '{"history":{"enable":"on"}}' "$image_file")"
|
|
expect_out "group #$name .*is created"
|
|
admin 5 "/a $name '$directory_name' admin"
|
|
expect_out "invitation to join the group #$name sent to"
|
|
approve "$name"
|
|
}
|
|
|
|
setup_relay() {
|
|
admin 1 "/relays name=ci_relay $relay_link"
|
|
expect_out "^ok$"
|
|
relay_id=$(db_query "$admin" "SELECT chat_relay_id FROM chat_relays WHERE display_name = 'ci_relay' AND deleted = 0")
|
|
}
|
|
|
|
create_channel() {
|
|
local name=$1 descr=$2 image_file=${3:-}
|
|
admin 10 "/_public group 1 $relay_id $(group_profile "$name" "$descr" '{"history":{"enable":"on"},"support":{"enable":"off"}}' "$image_file")"
|
|
expect_out "group #$name .*is created"
|
|
local group_id
|
|
group_id=$(db_query "$admin" "SELECT group_id FROM groups WHERE local_display_name = '$name'")
|
|
admin_until "relay id [0-9]+: active" 180 "/_get relays #$group_id"
|
|
}
|
|
|
|
list_channel() {
|
|
local name=$1
|
|
admin 5 "/share chat #$name @'$directory_name'"
|
|
expect_out "link to join channel #$name"
|
|
approve "$name"
|
|
}
|
|
|
|
seed_channel() {
|
|
create_channel "$@"
|
|
list_channel "$1"
|
|
}
|
|
|
|
channel_link() {
|
|
admin 1 "/show link #$1" >&2
|
|
grep -m 1 -oE 'https://[^ ]+/c#[^ ]+' "$work/out"
|
|
}
|
|
|
|
# The short address that /ad printed.
|
|
new_address() {
|
|
local address
|
|
address=$(awk '/Your new chat address is created!/ {getline; getline; print; exit}' "$work/out")
|
|
case "$address" in
|
|
https://*/a#*) echo "$address" ;;
|
|
*)
|
|
echo "error: no short address in the output" >&2
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Avatars at the size the app makes them (avatar.swift), with different amounts of detail;
|
|
# and small ones for ci_group, ci_channel and ci_cards, so that several fit in one size-limited search response.
|
|
make_avatars() {
|
|
xcrun swiftc -O -o "$work/avatar" "$(dirname "$0")/avatar.swift"
|
|
local i
|
|
for i in 1 2 3 4 5 6; do
|
|
"$work/avatar" "$i" "0.$((i + 2))" "$work/avatar-$i.txt"
|
|
done
|
|
for i in 7 8 9; do
|
|
"$work/avatar" "$i" 0 "$work/avatar-$i.txt" 64
|
|
done
|
|
}
|
|
|
|
seed_avatars() {
|
|
local i
|
|
for i in 1 2 3; do
|
|
seed_group "ci_avatar_g$i" "CI avatar group $i" "$work/avatar-$i.txt"
|
|
seed_channel "ci_avatar_c$i" "CI avatar channel $i" "$work/avatar-$((i + 3)).txt"
|
|
done
|
|
}
|
|
|
|
# A local SMP server whose [NAMES] section points at names-resolver.py, which answers from names.json.
|
|
start_names() {
|
|
echo "{}" > "$work/names.json"
|
|
nohup python3 "$(dirname "$0")/names-resolver.py" "$names_resolver_port" "$work/names.json" < /dev/null > "$work/names-resolver.log" 2>&1 &
|
|
echo $! > "$work/names-resolver.pid"
|
|
export SMP_SERVER_CFG_PATH=$work/smp-server/etc SMP_SERVER_LOG_PATH=$work/smp-server/log
|
|
"$bin/smp-server" init -y --disable-store-log --no-password --fqdn localhost --disable-web > "$work/out" 2>&1 || {
|
|
cat "$work/out"
|
|
return 1
|
|
}
|
|
cat "$work/out"
|
|
names_server=$(grep -m 1 -oE 'smp://[^ ]+@localhost' "$work/out")
|
|
sed -i '' \
|
|
-e 's/^enable: off$/enable: on/' \
|
|
-e "s|^# resolver_endpoint: http://127.0.0.1:8000\$|resolver_endpoint: http://127.0.0.1:$names_resolver_port|" \
|
|
-e 's/^port = 5223,443$/port = 5223/' \
|
|
"$SMP_SERVER_CFG_PATH/smp-server.ini"
|
|
grep -E '^(enable: on|resolver_endpoint|port = )' "$SMP_SERVER_CFG_PATH/smp-server.ini"
|
|
nohup "$bin/smp-server" start < /dev/null > "$work/smp-server.log" 2>&1 &
|
|
echo $! > "$work/smp-server.pid"
|
|
local deadline=$((SECONDS + 60))
|
|
until nc -z 127.0.0.1 5223 2> /dev/null; do
|
|
if ! kill -0 "$(cat "$work/smp-server.pid")" 2> /dev/null || [ "$SECONDS" -ge "$deadline" ]; then
|
|
echo "error: the names server did not start"
|
|
cat "$work/smp-server.log" "$work/names-resolver.log"
|
|
return 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
echo "$names_server" > "$work/names-server"
|
|
echo "names server: $names_server"
|
|
}
|
|
|
|
register_name() {
|
|
local name=$1 contact=$2 channel=$3
|
|
jq --arg n "$name" --arg ct "$contact" --arg ch "$channel" \
|
|
'.[$n] = {simplexContact: ([$ct] | map(select(. != ""))), simplexChannel: ([$ch] | map(select(. != "")))}' \
|
|
"$work/names.json" > "$work/names.json.new"
|
|
mv "$work/names.json.new" "$work/names.json"
|
|
}
|
|
|
|
# A contact that claims a name and accepts every connection; it keeps running, so the app can connect to it.
|
|
start_contact() {
|
|
local db=$1 display_name=$2 name=$3
|
|
chat "$db" 1 "/ad" --user-display-name "$display_name"
|
|
local address
|
|
address=$(new_address)
|
|
chat "$db" 1 "/auto_accept on" --user-display-name "$display_name"
|
|
expect_out "^auto_accept on"
|
|
register_name "$name" "$address" ""
|
|
named "$db" "$display_name" 5 "/_set domain 1 $name"
|
|
expect_no_error
|
|
# --headless is for relays only; a command with a long -t keeps the client online, accepting contacts, for 6 hours
|
|
nohup "$bin/simplex-chat" -d "$work/$db" -e "/auto_accept on" -t 21600 --user-display-name "$display_name" < /dev/null > "$work/$db.log" 2>&1 &
|
|
echo $! > "$work/$db.pid"
|
|
}
|
|
|
|
# A name is claimed before its channel is listed: a profile change after approval hides it until approved again.
|
|
seed_names() {
|
|
local address link
|
|
create_channel cinews "CI news channel"
|
|
link=$(channel_link cinews)
|
|
register_name cinews.testing "" "$link"
|
|
named "$admin" "$admin" 5 "/public group access #cinews domain=cinews.testing"
|
|
expect_out "updated public group access: domain=cinews.testing"
|
|
list_channel cinews
|
|
seed_group cinews_digest "CI news digest"
|
|
seed_group cinews_weekly "CI news weekly"
|
|
|
|
create_channel ciboth "CI both channel"
|
|
admin 1 "/ad"
|
|
address=$(new_address)
|
|
link=$(channel_link ciboth)
|
|
register_name ciboth.testing "$address" "$link"
|
|
named "$admin" "$admin" 5 "/_set domain 1 ciboth.testing"
|
|
expect_no_error
|
|
named "$admin" "$admin" 5 "/public group access #ciboth domain=ciboth.testing"
|
|
expect_out "updated public group access: domain=ciboth.testing"
|
|
|
|
start_contact ci_alice cialice cialice.testing
|
|
start_contact ci_robert Robert cibobby.testing
|
|
}
|
|
|
|
# What the app will resolve, from a client that knows none of the names' owners.
|
|
probe_names() {
|
|
local target pattern
|
|
# -s gives its server the names role only to users that exist when the client starts
|
|
chat probe 1 "/u" --user-display-name ci_probe
|
|
for target in "#cinews.testing|ok to connect via relays" "@cialice.testing|contact address: ok to connect" "@cibobby.testing|contact address: ok to connect" "ciboth.testing|You can also connect to @ciboth"; do
|
|
pattern=${target#*|}
|
|
target=${target%%|*}
|
|
named probe ci_probe 5 "/_connect plan 1 $target"
|
|
expect_out "$pattern"
|
|
done
|
|
named probe ci_probe 5 "/_connect plan 1 #cinone.testing" || true
|
|
echo "names resolve: cinews, cialice, cibobby, ciboth; cinone is not registered"
|
|
}
|
|
|
|
# The directory's reply to a search starts with "Found N group(s)"; earlier messages name the same groups.
|
|
in_search_reply() {
|
|
local reply
|
|
reply=$(sed -n '/Found [0-9]* group(s)/,$p' "$work/out")
|
|
grep -qF "$1" <<< "$reply"
|
|
}
|
|
|
|
# A search sent as a message, which any client can send.
|
|
probe_search() {
|
|
admin 5 "@'$directory_name' $search_text"
|
|
local deadline=$((SECONDS + 120))
|
|
until admin 5 "/tail @'$directory_name' 10" && in_search_reply "ci_group (CI seed group)" && in_search_reply "ci_channel (CI seed channel)"; do
|
|
if [ "$SECONDS" -ge "$deadline" ]; then
|
|
echo "error: the directory's reply to \"$search_text\" does not list ci_group and ci_channel"
|
|
return 1
|
|
fi
|
|
done
|
|
echo "search for \"$search_text\" found ci_group and ci_channel"
|
|
}
|
|
|
|
# What the app will send, from a client that never connected to the directory.
|
|
probe_service_search() {
|
|
chat probe 1 "/_service_request 1 $directory_link timeout=30 {\"type\":\"search\",\"searchText\":\"$search_text\"}" --user-display-name ci_probe
|
|
expect_out '"displayName":"ci_group"'
|
|
expect_out '"displayName":"ci_channel"'
|
|
echo "search request for \"$search_text\" found ci_group and ci_channel"
|
|
}
|
|
|
|
# Follows the search cursor to the end, recording what each response carried and its size against the limit.
|
|
probe_pages() {
|
|
local text=$1 expected=$2
|
|
local page=0 cursor=null req resp names=""
|
|
{
|
|
echo "## Search \"$text\", page by page"
|
|
echo
|
|
echo "Responses must fit $max_service_body bytes, as JSON or zstd-compressed (the zstd size is measured with the zstd CLI at level 3, a few bytes off the core's framing)."
|
|
echo
|
|
echo "| Page | Entries (avatar characters) | JSON bytes | zstd -3 bytes | More |"
|
|
echo "|---|---|---|---|---|"
|
|
} >> "$work/seed.md"
|
|
while [ "$cursor" != end ]; do
|
|
page=$((page + 1))
|
|
if [ "$page" -gt 20 ]; then
|
|
echo "error: more than 20 pages"
|
|
return 1
|
|
fi
|
|
req=$(jq -cn --arg t "$text" --argjson c "$cursor" '{type: "search", searchText: $t} + (if $c == null then {} else {searchCursor: $c} end)')
|
|
chat probe 1 "/_service_request 1 $directory_link timeout=30 $req" --user-display-name ci_probe
|
|
resp=$(sed -n 's/^service response: //p' "$work/out")
|
|
if [ -z "$resp" ]; then
|
|
echo "error: no service response for page $page"
|
|
return 1
|
|
fi
|
|
if jq -e '.searchCursor != null' <<< "$resp" > /dev/null; then
|
|
cursor=$(jq -c '.searchCursor' <<< "$resp")
|
|
else
|
|
cursor=end
|
|
fi
|
|
names="$names$(jq -r '.entries[].displayName' <<< "$resp")"$'\n'
|
|
echo "| $page | $(jq -r '[.entries[] | "\(.displayName) (\(.image // "" | length))"] | join(", ")' <<< "$resp") | $(printf '%s' "$resp" | wc -c | tr -d ' ') | $(printf '%s' "$resp" | zstd -3 -q -c --no-check | wc -c | tr -d ' ') | $([ "$cursor" = end ] && echo no || echo yes) |" >> "$work/seed.md"
|
|
done
|
|
local found repeated
|
|
found=$(grep -c . <<< "$names" || true)
|
|
repeated=$({ grep . <<< "$names" || true; } | sort | uniq -d | paste -sd ' ' -)
|
|
{
|
|
echo
|
|
echo "$found entries in $page pages, $expected expected; repeated: ${repeated:-none}."
|
|
echo
|
|
} >> "$work/seed.md"
|
|
echo "search for \"$text\": $found entries in $page pages, $expected expected, repeated: ${repeated:-none}"
|
|
if [ "$found" -ne "$expected" ] || [ -n "$repeated" ]; then
|
|
echo "::warning::search for \"$text\" returned $found entries ($expected expected), repeated: ${repeated:-none}"
|
|
fi
|
|
}
|
|
|
|
write_seed() {
|
|
{
|
|
echo "# Test directory"
|
|
echo
|
|
echo "Directory address: $directory_link"
|
|
echo
|
|
echo "## Listed groups and channels"
|
|
echo
|
|
echo "| Search with | Entries | Seeded for |"
|
|
echo "|---|---|---|"
|
|
echo "| \`$search_text\` | ci_group (group), ci_channel (channel), each with a small avatar (64 px) | the smoke test; results on one page |"
|
|
echo "| \`ci cards\` | ci_cards (channel, with a small avatar) | the smoke test joins it, so that the chat list shows its cards |"
|
|
echo "| \`$avatar_search_text\` | ci_avatar_g1…3 (groups), ci_avatar_c1…3 (channels), each with an avatar the size the app makes | avatars and the response size limit: about one entry per page |"
|
|
echo "| \`ci long\` | $long_name (group): a long name and a two-line description | D3 |"
|
|
echo "| \`ci markdown\` | ci_markdown (group): markdown in the description | D5 |"
|
|
echo "| \`cinews\` | cinews (channel, with the name cinews.testing), cinews_digest and cinews_weekly (groups) | a search that shows local chats, Names and Directory |"
|
|
echo
|
|
sqlite3 -markdown "$work/directory_chat.db" "$listings_query"
|
|
echo
|
|
echo "## SimpleX names"
|
|
echo
|
|
echo "Names server, to add in the app for resolving names: \`$names_server\`"
|
|
echo
|
|
echo "| Name | Resolves to |"
|
|
echo "|---|---|"
|
|
echo "| cinews.testing | the channel cinews, listed in the directory (\"CI news channel\") |"
|
|
echo "| ciboth.testing | a contact address (ci_admin) and the channel ciboth, not listed |"
|
|
echo "| cialice.testing | the contact cialice, online and accepting connections |"
|
|
echo "| cibobby.testing | the contact Robert (display name without the name), online and accepting connections |"
|
|
echo "| any other, e.g. cinone.testing | not registered |"
|
|
echo
|
|
} > "$work/seed.md"
|
|
}
|
|
|
|
dump_directory() {
|
|
echo "::group::directory registrations"
|
|
db_query directory "SELECT group_id, group_reg_status FROM sx_directory_group_regs" || true
|
|
echo "::endgroup::"
|
|
}
|
|
|
|
start() {
|
|
bin=$work/bin
|
|
mkdir -p "$bin"
|
|
unzip -o -q "$1" -d "$bin"
|
|
"$bin/simplex-chat" --version
|
|
"$bin/simplex-directory-service" --version
|
|
"$bin/smp-server" --version
|
|
make_avatars
|
|
|
|
start_names
|
|
start_relay
|
|
create_directory
|
|
start_directory "0:nobody"
|
|
trap dump_directory EXIT
|
|
connect_admin
|
|
seed_group ci_group "CI seed group" "$work/avatar-7.txt"
|
|
setup_relay
|
|
seed_channel ci_channel "CI seed channel" "$work/avatar-8.txt"
|
|
seed_channel ci_cards "CI cards channel" "$work/avatar-9.txt"
|
|
seed_avatars
|
|
seed_group "$long_name" "CI long entry: a description long enough to take two lines in the directory results, to check how the row truncates"
|
|
seed_group ci_markdown "CI markdown *bold* _italic_ [x](https://example.com)"
|
|
seed_names
|
|
probe_search
|
|
probe_names
|
|
probe_service_search
|
|
write_seed
|
|
probe_pages "$search_text" 2
|
|
probe_pages "$avatar_search_text" 6
|
|
}
|
|
|
|
stop() {
|
|
local p processes="directory relay ci_alice ci_robert smp-server names-resolver"
|
|
for p in $processes; do
|
|
if [ -f "$work/$p.pid" ]; then
|
|
kill -INT "$(cat "$work/$p.pid")" 2> /dev/null || true
|
|
fi
|
|
done
|
|
sleep 3
|
|
for p in $processes; do
|
|
echo "::group::$p log"
|
|
cat "$work/$p.log" 2> /dev/null || true
|
|
echo "::endgroup::"
|
|
done
|
|
}
|
|
|
|
case "${1:-}" in
|
|
start)
|
|
work=$3
|
|
mkdir -p "$work"
|
|
start "$2"
|
|
;;
|
|
stop)
|
|
work=$2
|
|
stop
|
|
;;
|
|
directory-stop)
|
|
work=$2
|
|
stop_directory
|
|
;;
|
|
directory-start)
|
|
work=$2
|
|
bin=$work/bin
|
|
start_directory "$(cat "$work/super-user")"
|
|
;;
|
|
listings)
|
|
work=$2
|
|
sqlite3 -markdown "$work/directory_chat.db" "$listings_query"
|
|
;;
|
|
*)
|
|
echo "usage: $0 start <backend zip> <work dir> | stop <work dir> | directory-stop <work dir> | directory-start <work dir> | listings <work dir>"
|
|
exit 1
|
|
;;
|
|
esac
|