#!/usr/bin/env bash set -euo pipefail repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "${repo_root}" project_name="${MINEROPS_CONTROL_MIGRATION_PROJECT:-minerops-control-migration-$$}" compose=(docker compose --project-name "${project_name}" --env-file .env.dev -f compose.dev.yaml) cleanup() { status=$? if (( status != 0 )); then "${compose[@]}" logs --no-color --tail 120 postgres || true fi "${compose[@]}" down --volumes --remove-orphans >/dev/null 2>&1 || true exit "${status}" } trap cleanup EXIT run_alembic() { "${compose[@]}" run --rm --no-deps backend alembic "$@"; } psql_db() { "${compose[@]}" exec -T postgres sh -ec \ 'exec psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" "$@"' sh "$@" } [[ -f .env.dev ]] || { echo "Missing .env.dev" >&2; exit 1; } "${compose[@]}" up --detach postgres for attempt in $(seq 1 60); do "${compose[@]}" exec -T postgres sh -ec \ 'pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"' >/dev/null 2>&1 && break (( attempt < 60 )) || { echo "PostgreSQL did not become ready" >&2; exit 1; } sleep 1 done "${compose[@]}" build backend echo "Testing 0015 preflight failures from the previous head..." run_alembic upgrade 0014_repair_parts_tracking psql_db -c "INSERT INTO minerops_agent_command_queue (id, agent_id, command_type, command_status) VALUES ('unknown-status', 'test-agent', 'test', 'mystery')" if output="$(run_alembic upgrade 0015_miner_control_foundation 2>&1)"; then echo "Unknown queue status unexpectedly passed migration preflight" >&2 exit 1 fi [[ "${output}" == *"unsupported command_status values: mystery"* ]] psql_db -c "DELETE FROM minerops_agent_command_queue WHERE id = 'unknown-status'" duplicate_miner="00000000-0000-4000-8000-000000001599" psql_db <&2 exit 1 fi [[ "${output}" == *"more than one pending or claimed miner_control"* ]] [[ "${output}" == *"${duplicate_miner}"* ]] psql_db -c "DELETE FROM minerops_agent_command_queue WHERE id IN ('duplicate-one', 'duplicate-two')" psql_db <<'SQL' INSERT INTO minerops_site_agents (agent_id, site_name) VALUES ('legacy-unassigned-agent', 'Legacy Display Name'); INSERT INTO minerops_agent_command_queue ( id, agent_id, command_type, command_status, request_payload ) VALUES ( 'preserved-history', 'legacy-unassigned-agent', 'miner_control', 'completed', '{"miner_id":"00000000-0000-4000-8000-000000001598","action":"suspend"}' ); SQL run_alembic upgrade 0015_miner_control_foundation [[ "$(run_alembic current 2>/dev/null)" == *"0015_miner_control_foundation"* ]] echo "Checking assignment, status, uniqueness, history, and preservation..." psql_db <<'SQL' DO $$ BEGIN IF (SELECT site_id IS NOT NULL FROM minerops_site_agents WHERE agent_id = 'legacy-unassigned-agent') THEN RAISE EXCEPTION 'Legacy site_name was silently converted to site_id'; END IF; IF NOT EXISTS (SELECT 1 FROM minerops_agent_command_queue WHERE id = 'preserved-history' AND command_status = 'completed') THEN RAISE EXCEPTION 'Historical queue row was not preserved'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'ck_minerops_agent_command_queue_status') THEN RAISE EXCEPTION 'Queue status constraint missing'; END IF; IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE indexname = 'uq_agent_queue_live_miner_control') OR NOT EXISTS (SELECT 1 FROM pg_indexes WHERE indexname = 'idx_agent_queue_miner_control_history') THEN RAISE EXCEPTION 'Miner control queue indexes missing'; END IF; END $$; INSERT INTO sites (id, name) VALUES ('00000000-0000-4000-8000-000000001590', 'Control Migration Site'); UPDATE minerops_site_agents SET site_id = '00000000-0000-4000-8000-000000001590' WHERE agent_id = 'legacy-unassigned-agent'; DO $$ BEGIN BEGIN INSERT INTO minerops_site_agents (agent_id, site_id, site_name) VALUES ('duplicate-site-agent', '00000000-0000-4000-8000-000000001590', 'Duplicate'); RAISE EXCEPTION 'Duplicate durable site assignment was accepted'; EXCEPTION WHEN unique_violation THEN NULL; END; BEGIN DELETE FROM sites WHERE id = '00000000-0000-4000-8000-000000001590'; RAISE EXCEPTION 'Assigned site deletion was accepted'; EXCEPTION WHEN foreign_key_violation THEN NULL; END; BEGIN INSERT INTO minerops_agent_command_queue (id, agent_id, command_type, command_status) VALUES ('bad-status-after', 'legacy-unassigned-agent', 'test', 'mystery'); RAISE EXCEPTION 'Unknown status passed CHECK'; EXCEPTION WHEN check_violation THEN NULL; END; END $$; SQL echo "Testing downgrade, historical preservation, and re-upgrade..." run_alembic downgrade 0014_repair_parts_tracking [[ "$(run_alembic current 2>/dev/null)" == *"0014_repair_parts_tracking"* ]] [[ "$(psql_db -Atc "SELECT count(*) FROM minerops_agent_command_queue WHERE id = 'preserved-history'")" == "1" ]] [[ "$(psql_db -Atc "SELECT count(*) FROM information_schema.columns WHERE table_name='minerops_site_agents' AND column_name='site_id'")" == "0" ]] run_alembic upgrade 0015_miner_control_foundation echo "Testing empty database through head..." "${compose[@]}" exec -T postgres sh -ec 'createdb -U "$POSTGRES_USER" minerops_control_empty' "${compose[@]}" run --rm --no-deps -e POSTGRES_DB=minerops_control_empty backend alembic upgrade head empty_revision="$("${compose[@]}" exec -T postgres sh -ec \ 'psql -U "$POSTGRES_USER" -d minerops_control_empty -Atc "SELECT version_num FROM alembic_version"')" [[ "${empty_revision}" == "0015_miner_control_foundation" ]] echo "Miner Control Foundation migration tests passed."