Something went wrong. Try again.
Backend environment for match hosting for lance.blue
Something went wrong. Try again.
Shell
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155#!/usr/bin/env bash# finalize.sh is the last thing that runs in a match container, and anything it# does not collect is gone: the writable layer goes with the task. This covers# the part that is easy to get wrong by hand - which files under the client's# logs/ are offered for upload and which are not.## The manifest here carries no upload targets, the same as the dev manifest# scripts/run.sh writes, so `upload` reports what it would have sent and returns# non-zero. That is what the assertions read: the file was found and offered, no# network involved.set -uo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"TMP="$(mktemp -d)"trap 'rm -rf "$TMP"' EXIT
fail=0check() { # <description> <test-command...> if "${@:2}"; then echo "ok $1"; else echo "FAIL $1"; fail=1; fi}
# The image collects container/ under ARENA_HOME; finalize.sh sources# lib/common.sh from there.mkdir -p "$TMP/home" "$TMP/run/state" "$TMP/run/spool"# The observer still spools a dump a phase; nothing collects them. What they# stood in for is kept properly now - raw/game_actions_N.tsv is the same match# structured and a thirtieth of the size, and raw/gamelog.html is MegaMek's own# account - and no reader for these ever existed.printf 'a turn report\n' > "$TMP/run/spool/007-r02-FIRING.txt"ln -s "$ROOT/container" "$TMP/home/container"echo '{"matchId":"test-match"}' > "$TMP/run/manifest.json"
run_finalize() { # <logdir> ARENA_HOME="$TMP/home" ARENA_RUN="$TMP/run" ARENA_MM_LOGDIR="$1" \ bash "$ROOT/container/exit/finalize.sh" 2>&1}
# --- a client that logged -----------------------------------------------------LOGS="$TMP/logs"mkdir -p "$LOGS"# What MegaMek's shipped mmconf/log4j2.xml writes. unified_log rolls at 1MB, so a# long match leaves the early rounds - the tileset and camo work - in the gz.touch "$LOGS/megamek.log" \ "$LOGS/unified_log.log" \ "$LOGS/unified_log_1.log.gz" \ "$LOGS/unified_log_2.log.gz" \ "$LOGS/bot_path_ranker.log" \ "$LOGS/megameklab.log"
out="$(run_finalize "$LOGS")"; status=$?
check "runs clean" test "$status" -eq 0[ "$status" -eq 0 ] || printf '%s\n' "$out" | sed 's/^/ /'
# The logs ride the `diagnostics` key, the same one the stats table and the# profiles use. Neither of those exists in this fixture, so a diagnostics line# here is one of ours.offered() { grep -q "diagnostics.*$1" <<<"$out"; }
check "the megamek logger's log is offered" offered "megamek.log"check "the unified log is offered" offered "unified_log.log"check "rolled unified logs are offered" bash -c \ "grep -q 'unified_log_1.log.gz' <<<\"\$1\" && grep -q 'unified_log_2.log.gz' <<<\"\$1\"" _ "$out"check "reports how many it sent" grep -q 'client log(s)' <<<"$out"
# Spooled dumps stay in the container. This is the assertion that keeps the# decision from drifting back by accident: an upload path re-added here would# name the file.check "a spooled turn report is not uploaded" bash -c \ "! grep -q '007-r02-FIRING.txt' <<<\"\$1\"" _ "$out"
# The two exclusions are the reason this test exists: both sit in the same# directory and both would come along with a wider glob. bot_path_ranker is# Princess's per-move ranking and is large; megameklab belongs to another# application and is always empty here.check "Princess's path ranking is left behind" bash -c "! grep -q 'bot_path_ranker' <<<\"\$1\"" _ "$out"check "another application's log is left behind" bash -c "! grep -q 'megameklab' <<<\"\$1\"" _ "$out"
# --- a client that never started ----------------------------------------------# A match that dies in init has no logs/ at all. Collecting nothing is correct;# failing the finalize, and losing the result upload with it, is not.EMPTY="$TMP/empty"mkdir -p "$EMPTY"out="$(run_finalize "$EMPTY")"; status=$?
check "an empty log dir still finalizes" test "$status" -eq 0check "and says it collected nothing" grep -q 'uploaded 0 client log(s)' <<<"$out"
# --- the result, however the match ended --------------------------------------# MatchHost keeps a provisional result on disk from the first tick of the game# and replaces it at victory, so a match that was abandoned, timed out or# signalled has one too. This pass must upload it either way, and must say which# it had: "no result" used to be the only record an undecided match left, and it# said nothing about how far the match got.RESULT="$TMP/run/state/result.json"
check "a match with no document says so" bash -c \ "grep -q 'no result.json - the match host wrote none' <<<\"\$1\"" _ "$out"
echo '{"ending": "no-victory-at-exit", "round": 4, "victoryTeam": 0, "victoryPlayerId": -1, "players": []}' > "$RESULT"out="$(run_finalize "$EMPTY")"; status=$?
check "an undecided match still finalizes" test "$status" -eq 0check "and its result is offered for upload" grep -q 'result-final.json' <<<"$out"check "and the log says how it ended" grep -q 'the match ended: no-victory-at-exit (round 4)' <<<"$out"
# A decided one reads the same way and differs only in the ending, which is the# whole point of the field: one shape, one reader.rm -rf "$TMP/run/state/sent" "$TMP/run/state/result-final.json"echo '{"ending": "victory", "round": 9, "victoryTeam": 1, "victoryPlayerId": -1, "players": []}' > "$RESULT"out="$(run_finalize "$EMPTY")"; status=$?
check "a decided match finalizes" test "$status" -eq 0check "and says so" grep -q 'the match ended: victory (round 9)' <<<"$out"
# An artifact that has already been accepted is never offered twice, whichever# pass got it through.rm -rf "$TMP/run/state/sent"mkdir -p "$TMP/run/state/sent": > "$TMP/run/state/sent/result-final.json"out="$(run_finalize "$EMPTY")"check "a sent result is not offered again" bash -c \ "! grep -q 'result-final.json' <<<\"\$1\"" _ "$out"rm -rf "$TMP/run/state/sent"
# --- counting the match -------------------------------------------------------# Two operational properties, neither about whether the numbers are right: that# a container nobody configured stays silent, and that a counter which cannot be# reached does not take the match down with it. The third property - that the# hash here equals the one web/src/analytics.ts computes - cannot be tested from# this repo and is why the salt is passed in rather than written in both.cat > "$TMP/run/state/identity.json" <<'IDENTITY'{"matchId":"test-match","players":[ {"slot":"Alpha","control":"human","did":"did:plc:aaa","handle":"a.example"}, {"slot":"Princess1","control":"bot","did":null,"handle":null}]}IDENTITY
out="$(run_finalize "$EMPTY")"; status=$?check "unconfigured, it counts nothing" bash -c "! grep -qi 'posthog\|counted the match' <<<\"\$1\"" _ "$out"check "and still finalizes" test "$status" -eq 0
# Port 1 refuses immediately, so this is the unreachable case rather than a slow# one. A match is not less finished because a counter did not answer.out="$(ARENA_POSTHOG_KEY=phc_test ARENA_POSTHOG_SALT=test-salt \ ARENA_POSTHOG_HOST=http://127.0.0.1:1 ARENA_POSTHOG_TIMEOUT=2 \ run_finalize "$EMPTY")"; status=$?
check "an unreachable counter warns" grep -qi 'posthog did not accept' <<<"$out"check "and does not fail the finalize" test "$status" -eq 0check "and the match still finalizes" grep -q 'done' <<<"$out"
exit "$fail"