Owner decision 2026-10-07: production's mongod becomes a one-member replica set (rs0), so that a backup can read every collection at one instant (a snapshot read session), which a standalone mongod can't. The unit runs mongod with --replSet rs0 and a 990 MB oplog; setup.sh converts it once, idempotently (PrivaPub stopped, mongod restarted, rs.initiate, the primary awaited, PrivaPub started); every connection string says directConnection=true, which works against the standalone too, so this code can deploy before the conversion. The CI's throwaway mongod and the pasture's are replica sets as well, so the snapshot path is what the tests exercise; MongoTopology tells which a mongod is, and TopologyTests holds the test mongod to it. The suite passes on it (906). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
69 lines
3.2 KiB
Bash
Executable File
69 lines
3.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Runs a command (normally `dotnet test`) with a throwaway mongod: a fresh data directory on a random localhost port,
|
|
# stopped and deleted when the command ends. The box's own mongods (27017 shared, 27022 PrivaPub's) are never touched.
|
|
# Like production's, it is a one-member replica set (rs0), so the backup's snapshot reads are what the tests exercise.
|
|
# usage: tools/ci/with-test-mongod.sh dotnet test PrivaPub.sln -c Release
|
|
set -euo pipefail
|
|
|
|
root="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/privapub-test-mongod"
|
|
mkdir -p "$root"
|
|
|
|
# Leftovers of a cancelled run: anything here older than two hours.
|
|
find "$root" -mindepth 1 -maxdepth 1 -type d -mmin +120 -print0 2>/dev/null | while IFS= read -r -d '' old; do
|
|
[ -f "$old/pid" ] && kill "$(cat "$old/pid")" 2>/dev/null || true
|
|
[ -f "$old/container" ] && podman rm -f "$(cat "$old/container")" >/dev/null 2>&1 || true
|
|
rm -rf "$old"
|
|
done
|
|
|
|
listening() { (exec 3<>"/dev/tcp/127.0.0.1/$1") 2>/dev/null; }
|
|
|
|
port=""
|
|
for _ in $(seq 1 20); do
|
|
candidate=$(python3 -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1", 0)); print(s.getsockname()[1]); s.close()')
|
|
case "$candidate" in 27017|27022) continue ;; esac
|
|
listening "$candidate" && continue
|
|
port=$candidate; break
|
|
done
|
|
[ -n "$port" ] || { echo "::error::no free port for the test mongod"; exit 1; }
|
|
|
|
dir=$(mktemp -d "$root/run-XXXXXX")
|
|
cleanup() {
|
|
status=$?
|
|
[ -f "$dir/pid" ] && kill "$(cat "$dir/pid")" 2>/dev/null || true
|
|
[ -f "$dir/container" ] && podman rm -f "$(cat "$dir/container")" >/dev/null 2>&1 || true
|
|
for _ in $(seq 1 20); do listening "$port" || break; sleep 0.5; done
|
|
rm -rf "$dir"
|
|
exit $status
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
|
|
if command -v mongod >/dev/null 2>&1; then
|
|
mkdir -p "$dir/db"
|
|
timeout 7200 mongod --dbpath "$dir/db" --port "$port" --bind_ip 127.0.0.1 --noauth \
|
|
--wiredTigerCacheSizeGB 0.25 --replSet rs0 --quiet --logpath "$dir/mongod.log" &
|
|
echo $! > "$dir/pid"
|
|
how="mongod $(mongod --version | head -1)"
|
|
elif command -v podman >/dev/null 2>&1; then
|
|
podman run -d --rm -p "127.0.0.1:$port:27017" docker.io/library/mongo:8 --quiet --replSet rs0 > "$dir/container"
|
|
how="podman mongo:8"
|
|
else
|
|
echo "::error::neither mongod nor podman is available for the test mongod"; exit 1
|
|
fi
|
|
|
|
for _ in $(seq 1 60); do listening "$port" && break; sleep 0.5; done
|
|
listening "$port" || { echo "::error::the test mongod did not start"; cat "$dir/mongod.log" 2>/dev/null | tail -20; exit 1; }
|
|
|
|
# the one-member replica set, then wait until it is its primary
|
|
shell() {
|
|
if [ -f "$dir/container" ]; then podman exec "$(cat "$dir/container")" mongosh --quiet --eval "$1"
|
|
else mongosh --quiet --port "$port" --eval "$1"; fi
|
|
}
|
|
member=$([ -f "$dir/container" ] && echo "127.0.0.1:27017" || echo "127.0.0.1:$port")
|
|
shell "rs.initiate({_id: 'rs0', members: [{_id: 0, host: '$member'}]})" >/dev/null
|
|
for _ in $(seq 1 60); do [ "$(shell 'db.hello().isWritablePrimary' 2>/dev/null)" = "true" ] && break; sleep 0.5; done
|
|
[ "$(shell 'db.hello().isWritablePrimary' 2>/dev/null)" = "true" ] || { echo "::error::the test mongod never became its replica set's primary"; exit 1; }
|
|
echo "test mongod: $how on 127.0.0.1:$port, replica set rs0"
|
|
|
|
export PRIVAPUB_TEST_MONGOD=1 PRIVAPUB_TEST_REQUIRE_MONGOD=1 PRIVAPUB_TEST_MONGO="mongodb://127.0.0.1:$port/?directConnection=true"
|
|
"$@"
|