diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e13b99e..83767bd2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,6 +55,11 @@ jobs: run: | npx prisma generate npx prisma migrate deploy || true + - name: Test Migration Rollbacks + working-directory: ./backend + env: + DATABASE_URL: postgresql://test:test@localhost:5432/test_db + run: npm run test:migrations - name: Run tests working-directory: ./backend env: diff --git a/backend/package.json b/backend/package.json index a3695fdd..8a883b35 100644 --- a/backend/package.json +++ b/backend/package.json @@ -10,6 +10,7 @@ "dev": "tsx watch src/index.ts", "test": "jest --runInBand --forceExit", "test:coverage": "jest --coverage --runInBand --forceExit", + "test:migrations": "bash scripts/test-migration-rollback.sh", "collaboration": "tsx src/collaborationServer.ts" }, "keywords": [], diff --git a/backend/scripts/test-migration-rollback.sh b/backend/scripts/test-migration-rollback.sh new file mode 100644 index 00000000..53a912ee --- /dev/null +++ b/backend/scripts/test-migration-rollback.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +# Exit on any error +set -e + +echo "Running Database Migration Rollback Checker..." + +# Ensure we are in the backend directory +cd "$(dirname "$0")/.." + +# Check if migrations directory exists +if [ ! -d "prisma/migrations" ]; then + echo "No migrations found. Exiting gracefully." + exit 0 +fi + +# Find all migrations (excluding migration_lock.toml and any non-directories) +MIGRATIONS=($(find prisma/migrations -mindepth 1 -maxdepth 1 -type d | sort)) +NUM_MIGRATIONS=${#MIGRATIONS[@]} + +if [ "$NUM_MIGRATIONS" -lt 1 ]; then + echo "Not enough migrations to test rollback." + exit 0 +fi + +LATEST_MIGRATION="${MIGRATIONS[$((NUM_MIGRATIONS-1))]}" +echo "Latest migration is: $(basename "$LATEST_MIGRATION")" + +# Prepare N-1 migrations directory +echo "Preparing previous migration state..." +rm -rf prisma/migrations_prev +mkdir -p prisma/migrations_prev +cp -r prisma/migrations/* prisma/migrations_prev/ 2>/dev/null || true +rm -rf "prisma/migrations_prev/$(basename "$LATEST_MIGRATION")" + +# Make sure database is fully migrated to state N +echo "Applying all migrations up to state N..." +npx prisma migrate deploy + +# Generate the DOWN migration script by diffing N against N-1 +echo "Generating down migration for $(basename "$LATEST_MIGRATION")..." +npx prisma migrate diff \ + --from-migrations prisma/migrations \ + --to-migrations prisma/migrations_prev \ + --script > down.sql + +echo "Down migration generated:" +cat down.sql + +# Execute the DOWN migration +echo "Executing down migration..." +npx prisma db execute --file down.sql --schema prisma/schema.prisma + +# Verify the database schema matches state N-1 +echo "Verifying database schema integrity post-rollback..." +# diff DB vs N-1 migrations. Exit code 0 means no differences (schema matches perfectly). +npx prisma migrate diff \ + --from-schema-datasource prisma/schema.prisma \ + --to-migrations prisma/migrations_prev \ + --exit-code + +echo "Rollback successful! Database schema matches state prior to the latest migration." + +# Clean up +rm -rf prisma/migrations_prev down.sql +echo "Database Migration Rollback Checker completed successfully."