#!/bin/bash # Run the Mnemosyne Django test suite against an ephemeral Postgres + Neo4j. # # Pattern borrowed from spelunker/test-postgres.sh. Both databases run as # throwaway Docker containers on a private bridge network; each invocation # gets a fresh pair. The Django test runner applies migrations to the # empty Postgres and tears the whole network down on exit. # # Neo4j is included because a handful of library/tests/* modules touch # neomodel at import time even though the assertions themselves stub the # cypher layer; having a reachable bolt endpoint keeps startup probes # from crashing. # # Usage: # ./test-postgres.sh # run everything # ./test-postgres.sh mcp_server # scope to one app # ./test-postgres.sh mcp_server.tests.test_auth # ./test-postgres.sh mcp_server -v 2 --failfast set -euo pipefail # --------------------------------------------------------------------------- # Configuration # --------------------------------------------------------------------------- NET="mnemosyne-test-$$" PG_CONTAINER="mnemosyne-test-pg-$$" NEO_CONTAINER="mnemosyne-test-neo4j-$$" PG_USER="mnemosyne" PG_PASS="mnemosyne" PG_DB="mnemosyne_test" NEO_PASS="mnemosynetestpw" # Neo4j rejects short or obvious secrets. IMAGE="git.helu.ca/r/mnemosyne:latest" # Colours (skipped when not a TTY). if [ -t 1 ]; then GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'; NC='\033[0m' else GREEN=''; RED=''; YELLOW=''; NC='' fi say() { printf "${GREEN}==> %s${NC}\n" "$*"; } warn() { printf "${YELLOW}[!] %s${NC}\n" "$*"; } die() { printf "${RED}[✗] %s${NC}\n" "$*" >&2; exit 1; } # --------------------------------------------------------------------------- # Cleanup # --------------------------------------------------------------------------- cleanup() { say "Cleaning up test containers" docker rm -f "$PG_CONTAINER" >/dev/null 2>&1 || true docker rm -f "$NEO_CONTAINER" >/dev/null 2>&1 || true docker network rm "$NET" >/dev/null 2>&1 || true } trap cleanup EXIT # --------------------------------------------------------------------------- # Start the support services # --------------------------------------------------------------------------- say "Creating Docker network $NET" docker network create "$NET" >/dev/null say "Starting Postgres ($PG_CONTAINER)" docker run -d --rm \ --name "$PG_CONTAINER" \ --network "$NET" \ -e POSTGRES_USER="$PG_USER" \ -e POSTGRES_PASSWORD="$PG_PASS" \ -e POSTGRES_DB="$PG_DB" \ postgres:16-alpine \ >/dev/null say "Starting Neo4j ($NEO_CONTAINER)" docker run -d --rm \ --name "$NEO_CONTAINER" \ --network "$NET" \ -e NEO4J_AUTH="neo4j/$NEO_PASS" \ -e NEO4J_PLUGINS='["apoc"]' \ neo4j:5-community \ >/dev/null # Wait for Postgres to accept connections. say "Waiting for Postgres to become ready" for i in $(seq 1 30); do if docker exec "$PG_CONTAINER" pg_isready -U "$PG_USER" -d "$PG_DB" >/dev/null 2>&1; then break fi sleep 1 done docker exec "$PG_CONTAINER" pg_isready -U "$PG_USER" -d "$PG_DB" >/dev/null \ || die "Postgres never became ready" # Wait for Neo4j's HTTP endpoint — bolt comes up just after it. say "Waiting for Neo4j to become ready" for i in $(seq 1 60); do if docker exec "$NEO_CONTAINER" wget -qO- http://localhost:7474/ >/dev/null 2>&1; then break fi sleep 1 done # --------------------------------------------------------------------------- # Run the test command # --------------------------------------------------------------------------- SOURCE_DIR="$(cd "$(dirname "$0")" && pwd)/mnemosyne" say "Running Django test suite" docker run --rm \ --network "$NET" \ -v "$SOURCE_DIR":/app \ -w /app \ -e DJANGO_SETTINGS_MODULE=mnemosyne.settings \ -e APP_DB_NAME="$PG_DB" \ -e APP_DB_USER="$PG_USER" \ -e APP_DB_PASSWORD="$PG_PASS" \ -e DB_HOST="$PG_CONTAINER" \ -e DB_PORT=5432 \ -e NEOMODEL_NEO4J_BOLT_URL="bolt://neo4j:$NEO_PASS@$NEO_CONTAINER:7687" \ -e SECRET_KEY=test-only-not-a-real-secret \ -e DEBUG=0 \ -e MCP_REQUIRE_AUTH=True \ "$IMAGE" \ python manage.py test "$@"