#!/bin/sh # Прогін тестів проти СПРАВЖНЬОЇ бази. # # ЧОМУ ЦЕ ОКРЕМИЙ СКРИПТ # # Тести проти бази мовчки пропускаються без NETPULSE_TEST_DSN — і саме # тому вони півтора року нічого не перевіряли: `go test ./...` показував # «ok», а всередині кожного з них стояв t.Skip. «Пропущено» в підсумку # виглядає рівно як «пройдено», і це найдорожча тиха відмова в проєкті: # перевірка ізоляції кабінетів, стелі тарифу, гасіння алертів і драбин # ескалації існували, компілювались і не виконувались. # # scripts/check.sh лишається швидким (без Docker і без бази) — його # ганяють на кожну правку. Цей скрипт повільніший і потребує Postgres, # тож викликається окремо: перед розгортанням і в CI. # # БАЗА МУСИТЬ БУТИ ОДНОРАЗОВОЮ. Тести пишуть, видаляють і перемикають # ролі, а перед ними ще й котяться міграції — зокрема ті, яких у робочій # базі ще не має бути. І це не побажання в коментарі: рівно так гине # бойова база, коли в профілі шелла лишився експортований # NETPULSE_TEST_DSN зі вчорашнього налагодження. Тому нижче стоїть # перевірка імені, а не заклик до обережності. # # ВИКОРИСТАННЯ # scripts/dbtest.sh # підніме свій Postgres # NETPULSE_TEST_DSN=... scripts/dbtest.sh # проти готової бази, # # ім'я якої містить probe/test # set -u SRC="$(cd "$(dirname "$0")/.." && pwd)" OWN_DB=0 NAME="" # Ім'я бази має саме казати, що вона одноразова. Перевірка навмисно # дурна й невідмикна: розумніша (маркерна таблиця, прапорець згоди) # перекладає рішення на людину рівно в той момент, коли людина поспішає. guard_disposable() { db=$(printf '%s' "$1" | sed -e 's/?.*$//' -e 's#.*/##') case "$db" in *probe*|*test*) return 0 ;; esac echo "!!! NETPULSE_TEST_DSN вказує на базу «$db»." >&2 echo " Тести пишуть, видаляють і котять міграції — база мусить бути" >&2 echo " одноразовою, і її імʼя має містити «probe» або «test»." >&2 echo " Якщо це чернетка — перейменуйте базу. Якщо ні — ви щойно мало" >&2 echo " не накотили міграції на робочу базу." >&2 exit 1 } if [ -z "${NETPULSE_TEST_DSN:-}" ]; then # Своя база на час прогону. Порт випадковий-таки ні: фіксований, але # нетиповий, щоб не зіткнутись із локальним Postgres розробника. PORT=${NETPULSE_TEST_PORT:-55433} # Імʼя з номером процесу: два прогони поруч (другий термінал, дві # задачі на одному раннері) інакше вбивали б бази один одному, і # падіння виглядало б як помилка зʼєднання нізвідки. NAME="netpulse-dbtest-$$" echo "== піднімаю одноразовий Postgres :$PORT" docker run -d --name "$NAME" -p "$PORT:5432" \ -e POSTGRES_USER=netpulse -e POSTGRES_PASSWORD=probe \ -e POSTGRES_DB=netpulse_probe \ timescale/timescaledb:2.17.2-pg16 >/dev/null || exit 1 OWN_DB=1 NETPULSE_TEST_DSN="postgres://netpulse:probe@127.0.0.1:$PORT/netpulse_probe?sslmode=disable" export NETPULSE_TEST_DSN n=0 until docker exec "$NAME" pg_isready -h 127.0.0.1 -U netpulse >/dev/null 2>&1; do n=$((n+1)); [ "$n" -gt 60 ] && { echo "база не піднялась"; exit 1; } sleep 1 done else guard_disposable "$NETPULSE_TEST_DSN" fi # Своя база зноситься лише після ЗЕЛЕНОГО прогону. Після червоного вона # лишається: подивитись, що саме опинилось у таблицях, — єдиний спосіб # зрозуміти половину падінь, а знесена база забирає цю можливість рівно # тоді, коли вона потрібна. cleanup() { [ "$OWN_DB" = 1 ] || return 0 if [ "${rc:-1}" = 0 ]; then docker rm -f "$NAME" >/dev/null 2>&1 else echo echo "база лишилась для розбору: docker exec -it $NAME psql -U netpulse netpulse_probe" echo "прибрати: docker rm -f $NAME" fi return 0 } trap cleanup EXIT INT TERM echo "== накат міграцій" ( cd "$SRC/server" && NETPULSE_DSN="$NETPULSE_TEST_DSN" go run ./cmd/netpulse-migrate ) || exit 1 echo "== тести" ( cd "$SRC/server" && go test ./internal/... -count=1 ) rc=$? echo if [ "$rc" = 0 ]; then echo "усе зелене проти бази" else echo "!!! тести проти бази не пройшли" fi exit $rc