feat(package): migrator: up to latest
This commit is contained in:
@@ -241,7 +241,7 @@ BEGIN
|
|||||||
name TEXT PRIMARY KEY,
|
name TEXT PRIMARY KEY,
|
||||||
version TEXT NOT NULL,
|
version TEXT NOT NULL,
|
||||||
installed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
installed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
);
|
)$inherits;
|
||||||
|
|
||||||
INSERT INTO hectic.version (name, version) VALUES ('migrator', '$VERSION');
|
INSERT INTO hectic.version (name, version) VALUES ('migrator', '$VERSION');
|
||||||
|
|
||||||
@@ -323,8 +323,139 @@ init_sql() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
help() {
|
help() {
|
||||||
# inherits: List one or more tables the migration table must inherit from
|
cat <<'EOF'
|
||||||
echo help
|
migrator - Database Migration Tool
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
migrator [OPTIONS] COMMAND [ARGS...]
|
||||||
|
|
||||||
|
DESCRIPTION:
|
||||||
|
A lightweight database migration tool supporting PostgreSQL and SQLite.
|
||||||
|
Tracks migrations in a dedicated table and supports bidirectional migrations.
|
||||||
|
|
||||||
|
COMMANDS:
|
||||||
|
init Initialize migration tables in database
|
||||||
|
migrate Apply or revert migrations
|
||||||
|
create Create a new migration file
|
||||||
|
list List available migrations
|
||||||
|
fetch Fetch migration status from database
|
||||||
|
|
||||||
|
GLOBAL OPTIONS:
|
||||||
|
--db-url URL, -u URL
|
||||||
|
Database connection URL (required for most commands)
|
||||||
|
PostgreSQL: postgresql://user@host/database
|
||||||
|
SQLite: sqlite:///path/to/file.db or /path/to/file.db
|
||||||
|
|
||||||
|
--migration-dir DIR, -d DIR
|
||||||
|
Directory containing migrations (default: ./migration)
|
||||||
|
|
||||||
|
--inherits TABLE (PostgreSQL only) Parent table for hectic.migration
|
||||||
|
Can be specified multiple times
|
||||||
|
|
||||||
|
MIGRATE SUBCOMMANDS:
|
||||||
|
up [N] Apply next N migrations (default: 1)
|
||||||
|
up all Apply all pending migrations (same as: up latest)
|
||||||
|
down [N] Revert last N migrations (default: 1)
|
||||||
|
to MIGRATION Migrate to specific migration (forward or backward)
|
||||||
|
to latest Migrate to the latest migration (aliases: head, last)
|
||||||
|
|
||||||
|
MIGRATE OPTIONS:
|
||||||
|
--force, -f Force migration despite tree mismatch (not implemented)
|
||||||
|
--set VAR, -v VAR Set psql variable (PostgreSQL only)
|
||||||
|
|
||||||
|
INIT OPTIONS:
|
||||||
|
--dry-run Print initialization SQL without executing
|
||||||
|
|
||||||
|
CREATE OPTIONS:
|
||||||
|
--name NAME, -n NAME
|
||||||
|
Name for the migration (default: random word)
|
||||||
|
|
||||||
|
LIST OPTIONS:
|
||||||
|
--raw, -r Output raw migration names without validation
|
||||||
|
|
||||||
|
EXAMPLES:
|
||||||
|
# Initialize migration tracking
|
||||||
|
migrator --db-url postgresql://user@localhost/mydb init
|
||||||
|
|
||||||
|
# Create a new migration
|
||||||
|
migrator create --name add-users-table
|
||||||
|
|
||||||
|
# Apply next migration
|
||||||
|
migrator -u postgresql://user@localhost/mydb migrate up
|
||||||
|
|
||||||
|
# Apply next 3 migrations
|
||||||
|
migrator -u postgresql://user@localhost/mydb migrate up 3
|
||||||
|
|
||||||
|
# Apply all pending migrations
|
||||||
|
migrator -u postgresql://user@localhost/mydb migrate up all
|
||||||
|
# or:
|
||||||
|
migrator -u postgresql://user@localhost/mydb migrate to latest
|
||||||
|
|
||||||
|
# Revert last migration
|
||||||
|
migrator -u postgresql://user@localhost/mydb migrate down
|
||||||
|
|
||||||
|
# Migrate to specific version
|
||||||
|
migrator -u postgresql://user@localhost/mydb migrate to 20231201120000-add-users
|
||||||
|
|
||||||
|
# List migrations
|
||||||
|
migrator list
|
||||||
|
|
||||||
|
# Use SQLite
|
||||||
|
migrator --db-url sqlite:///path/to/db.sqlite migrate up
|
||||||
|
|
||||||
|
# PostgreSQL with table inheritance
|
||||||
|
migrator --inherits audit_log --db-url $DB_URL init
|
||||||
|
|
||||||
|
MIGRATION FILE STRUCTURE:
|
||||||
|
migration/
|
||||||
|
└── 20231201120000-migration-name/
|
||||||
|
├── up.sql - Forward migration
|
||||||
|
└── down.sql - Rollback migration
|
||||||
|
|
||||||
|
MIGRATION NAMING:
|
||||||
|
Migrations must follow the format: YYYYMMDDHHMMSS-description
|
||||||
|
Example: 20231201120000-add-users-table
|
||||||
|
|
||||||
|
DATABASE SUPPORT:
|
||||||
|
PostgreSQL:
|
||||||
|
- Full schema support (hectic.migration)
|
||||||
|
- Domains with regex validation
|
||||||
|
- Triggers and functions
|
||||||
|
- Table inheritance (--inherits)
|
||||||
|
- Custom psql variables (--set)
|
||||||
|
|
||||||
|
SQLite:
|
||||||
|
- Simple table names (hectic_migration, hectic_version)
|
||||||
|
- CHECK constraints for validation
|
||||||
|
- Trigger-based version control
|
||||||
|
- File-based databases
|
||||||
|
|
||||||
|
ENVIRONMENT VARIABLES:
|
||||||
|
MIGRATION_DIR Default migration directory
|
||||||
|
DB_URL Default database URL (can be overridden with --db-url)
|
||||||
|
|
||||||
|
EXIT CODES:
|
||||||
|
0 Success
|
||||||
|
1 Generic error
|
||||||
|
2 Ambiguous arguments or unrelated migration tree
|
||||||
|
3 Missing required argument
|
||||||
|
4 Migration execution failed
|
||||||
|
5 Table does not exist (for --inherits)
|
||||||
|
9 Invalid argument or command
|
||||||
|
13 System/database incompatibility
|
||||||
|
127 Required tool not installed (psql or sqlite3)
|
||||||
|
|
||||||
|
VERSION:
|
||||||
|
0.0.1
|
||||||
|
|
||||||
|
AUTHOR:
|
||||||
|
Created with Nix and POSIX shell
|
||||||
|
|
||||||
|
MORE INFO:
|
||||||
|
Migration files are executed within transactions.
|
||||||
|
Failed migrations are automatically rolled back.
|
||||||
|
Migration hashes are tracked to detect tampering.
|
||||||
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
migrate_down() {
|
migrate_down() {
|
||||||
@@ -372,14 +503,20 @@ migrate_down() {
|
|||||||
|
|
||||||
migrate_up() {
|
migrate_up() {
|
||||||
UP_NUMBER=1
|
UP_NUMBER=1
|
||||||
|
local apply_all=0
|
||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
--*|-*)
|
--*|-*)
|
||||||
log error "\`migrate up\` argument $WHITE$1$NC does not exists"
|
log error "\`migrate up\` argument $WHITE$1$NC does not exists"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
|
all|latest|head)
|
||||||
|
apply_all=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
''|*[!0-9]*)
|
''|*[!0-9]*)
|
||||||
log error "up argument not a number";
|
log error "up argument not a number or 'all'";
|
||||||
exit 1;
|
exit 1;
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -389,6 +526,17 @@ migrate_up() {
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# If "all" specified, migrate to the last migration
|
||||||
|
if [ "$apply_all" -eq 1 ]; then
|
||||||
|
target_migration=$(printf '%s' "$fs_migrations" | tail -n1)
|
||||||
|
if [ -z "$target_migration" ]; then
|
||||||
|
log error "no migrations found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
printf '%s' "$target_migration"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
# Calculate target migration: current + UP_NUMBER
|
# Calculate target migration: current + UP_NUMBER
|
||||||
if [ -z "$db_migrations" ]; then
|
if [ -z "$db_migrations" ]; then
|
||||||
target_line=$UP_NUMBER
|
target_line=$UP_NUMBER
|
||||||
@@ -426,7 +574,17 @@ migrate_to() {
|
|||||||
done
|
done
|
||||||
|
|
||||||
[ "${migration_name+x}" ] || { log error "no migration name specified"; exit 1; }
|
[ "${migration_name+x}" ] || { log error "no migration name specified"; exit 1; }
|
||||||
printf '%s' "$migration_name"
|
|
||||||
|
# Handle special keywords for latest migration
|
||||||
|
case "$migration_name" in
|
||||||
|
latest|head|last)
|
||||||
|
# Return the last migration from filesystem
|
||||||
|
printf '%s' "$fs_migrations" | tail -n1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf '%s' "$migration_name"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
migration_list() {
|
migration_list() {
|
||||||
@@ -839,9 +997,20 @@ check_db_dependencies() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ! [ "${AS_LIBRARY+x}" ]; then
|
if ! [ "${AS_LIBRARY+x}" ]; then
|
||||||
|
# Show help if no arguments
|
||||||
|
[ $# -eq 0 ] && { help; exit 0; }
|
||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
log debug "arg: $1"
|
log debug "arg: $1"
|
||||||
case $1 in
|
case $1 in
|
||||||
|
--version|-V)
|
||||||
|
printf 'migrator version %s\n' "$VERSION"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
help|--help|-h)
|
||||||
|
help
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
migrate|create|fetch|list|init)
|
migrate|create|fetch|list|init)
|
||||||
[ "${SUBCOMMAND+x}" ] && {
|
[ "${SUBCOMMAND+x}" ] && {
|
||||||
log error "ambiguous subcommand, decide ${WHITE}$SUBCOMMAND ${NC}or ${WHITE}$1";
|
log error "ambiguous subcommand, decide ${WHITE}$SUBCOMMAND ${NC}or ${WHITE}$1";
|
||||||
@@ -864,7 +1033,7 @@ if ! [ "${AS_LIBRARY+x}" ]; then
|
|||||||
done
|
done
|
||||||
|
|
||||||
[ "${INHERITS_LIST+x}" ] && INHERITS_LIST="$(printf '%s' "$INHERITS_LIST" | sed -E 's/"/,/g; s/([^,]+)/"\1"/g')"
|
[ "${INHERITS_LIST+x}" ] && INHERITS_LIST="$(printf '%s' "$INHERITS_LIST" | sed -E 's/"/,/g; s/([^,]+)/"\1"/g')"
|
||||||
[ "${SUBCOMMAND+x}" ] || { log error "no subcomand specified"; exit 1; }
|
[ "${SUBCOMMAND+x}" ] || { log error "no subcommand specified. Use 'migrator help' for usage information."; exit 1; }
|
||||||
|
|
||||||
|
|
||||||
log debug "subcommand: $WHITE$SUBCOMMAND"
|
log debug "subcommand: $WHITE$SUBCOMMAND"
|
||||||
|
|||||||
80
test/package/migrator/test/postgresql/help-and-version.sh
Normal file
80
test/package/migrator/test/postgresql/help-and-version.sh
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#!/bin/dash
|
||||||
|
|
||||||
|
HECTIC_NAMESPACE=test-help-and-version
|
||||||
|
|
||||||
|
### CASE 1: Help with no arguments
|
||||||
|
log notice "test case: ${WHITE}help with no arguments"
|
||||||
|
|
||||||
|
output=$(migrator 2>&1)
|
||||||
|
if ! printf '%s' "$output" | grep -q "migrator - Database Migration Tool"; then
|
||||||
|
log error "test failed: ${WHITE}no help output when no arguments"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
### CASE 2: Explicit help command
|
||||||
|
log notice "test case: ${WHITE}explicit help command"
|
||||||
|
|
||||||
|
if ! migrator help | grep -q "USAGE:"; then
|
||||||
|
log error "test failed: ${WHITE}help command doesn't work"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
### CASE 3: --help flag
|
||||||
|
log notice "test case: ${WHITE}--help flag"
|
||||||
|
|
||||||
|
if ! migrator --help | grep -q "COMMANDS:"; then
|
||||||
|
log error "test failed: ${WHITE}--help flag doesn't work"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
### CASE 4: -h flag
|
||||||
|
log notice "test case: ${WHITE}-h flag"
|
||||||
|
|
||||||
|
if ! migrator -h | grep -q "EXAMPLES:"; then
|
||||||
|
log error "test failed: ${WHITE}-h flag doesn't work"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
### CASE 5: --version flag
|
||||||
|
log notice "test case: ${WHITE}--version flag"
|
||||||
|
|
||||||
|
version_output=$(migrator --version)
|
||||||
|
if ! printf '%s' "$version_output" | grep -q "migrator version"; then
|
||||||
|
log error "test failed: ${WHITE}--version doesn't show version"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
### CASE 6: -V flag
|
||||||
|
log notice "test case: ${WHITE}-V flag"
|
||||||
|
|
||||||
|
if ! migrator -V | grep -q "0.0.1"; then
|
||||||
|
log error "test failed: ${WHITE}-V flag doesn't show version"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
### CASE 7: Help message contains database support info
|
||||||
|
log notice "test case: ${WHITE}help shows database support"
|
||||||
|
|
||||||
|
help_output=$(migrator help)
|
||||||
|
if ! printf '%s' "$help_output" | grep -q "PostgreSQL"; then
|
||||||
|
log error "test failed: ${WHITE}help doesn't mention PostgreSQL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf '%s' "$help_output" | grep -q "SQLite"; then
|
||||||
|
log error "test failed: ${WHITE}help doesn't mention SQLite"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
### CASE 8: Help mentions key commands
|
||||||
|
log notice "test case: ${WHITE}help shows all commands"
|
||||||
|
|
||||||
|
for cmd in init migrate create list fetch; do
|
||||||
|
if ! printf '%s' "$help_output" | grep -qi "$cmd"; then
|
||||||
|
log error "test failed: ${WHITE}help doesn't mention $cmd command"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log notice "test passed"
|
||||||
|
|
||||||
148
test/package/migrator/test/postgresql/migrate-to-latest.sh
Normal file
148
test/package/migrator/test/postgresql/migrate-to-latest.sh
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
#!/bin/dash
|
||||||
|
|
||||||
|
HECTIC_NAMESPACE=test-migrate-to-latest
|
||||||
|
|
||||||
|
log notice "test case: ${WHITE}migrate to latest migration"
|
||||||
|
|
||||||
|
# Create initial schema
|
||||||
|
psql "$DATABASE_URL" -c 'CREATE TABLE articles (id INTEGER PRIMARY KEY)'
|
||||||
|
|
||||||
|
# Initialize migrator
|
||||||
|
if ! migrator --db-url "$DATABASE_URL" init; then
|
||||||
|
log error "test failed: ${WHITE}init failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create migrations directory with 4 migrations
|
||||||
|
mkdir -p migration
|
||||||
|
for i in 1 2 3 4; do
|
||||||
|
mig_name="2025010100000${i}-migration-${i}"
|
||||||
|
mkdir -p "migration/${mig_name}"
|
||||||
|
|
||||||
|
echo "ALTER TABLE articles ADD COLUMN col${i} TEXT;" > "migration/${mig_name}/up.sql"
|
||||||
|
echo "ALTER TABLE articles DROP COLUMN col${i};" > "migration/${mig_name}/down.sql"
|
||||||
|
done
|
||||||
|
|
||||||
|
### CASE 1: migrate up all
|
||||||
|
log notice "test case: ${WHITE}migrate up all"
|
||||||
|
|
||||||
|
if ! migrator --db-url "$DATABASE_URL" migrate up all; then
|
||||||
|
log error "test failed: ${WHITE}migrate up all failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify all 4 migrations were applied
|
||||||
|
applied_count=$(psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL")
|
||||||
|
if [ "$applied_count" != "4" ]; then
|
||||||
|
log error "test failed: ${WHITE}expected 4 migrations, got $applied_count"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify all columns exist
|
||||||
|
if ! psql -Atc "SELECT col1, col2, col3, col4 FROM articles LIMIT 0" "$DATABASE_URL" >/dev/null 2>&1; then
|
||||||
|
log error "test failed: ${WHITE}not all columns were added"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log info "migrate up all: success"
|
||||||
|
|
||||||
|
# Revert all migrations for next test
|
||||||
|
migrator --db-url "$DATABASE_URL" migrate down 4
|
||||||
|
|
||||||
|
### CASE 2: migrate to latest
|
||||||
|
log notice "test case: ${WHITE}migrate to latest"
|
||||||
|
|
||||||
|
if ! migrator --db-url "$DATABASE_URL" migrate to latest; then
|
||||||
|
log error "test failed: ${WHITE}migrate to latest failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify all 4 migrations were applied
|
||||||
|
applied_count=$(psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL")
|
||||||
|
if [ "$applied_count" != "4" ]; then
|
||||||
|
log error "test failed: ${WHITE}expected 4 migrations, got $applied_count"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log info "migrate to latest: success"
|
||||||
|
|
||||||
|
# Revert for next test
|
||||||
|
migrator --db-url "$DATABASE_URL" migrate down 4
|
||||||
|
|
||||||
|
### CASE 3: migrate to head (alias)
|
||||||
|
log notice "test case: ${WHITE}migrate to head (alias)"
|
||||||
|
|
||||||
|
if ! migrator --db-url "$DATABASE_URL" migrate to head; then
|
||||||
|
log error "test failed: ${WHITE}migrate to head failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
applied_count=$(psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL")
|
||||||
|
if [ "$applied_count" != "4" ]; then
|
||||||
|
log error "test failed: ${WHITE}expected 4 migrations, got $applied_count"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log info "migrate to head: success"
|
||||||
|
|
||||||
|
# Revert for next test
|
||||||
|
migrator --db-url "$DATABASE_URL" migrate down 4
|
||||||
|
|
||||||
|
### CASE 4: migrate up latest (alias)
|
||||||
|
log notice "test case: ${WHITE}migrate up latest"
|
||||||
|
|
||||||
|
if ! migrator --db-url "$DATABASE_URL" migrate up latest; then
|
||||||
|
log error "test failed: ${WHITE}migrate up latest failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
applied_count=$(psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL")
|
||||||
|
if [ "$applied_count" != "4" ]; then
|
||||||
|
log error "test failed: ${WHITE}expected 4 migrations, got $applied_count"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log info "migrate up latest: success"
|
||||||
|
|
||||||
|
### CASE 5: migrate to latest when already at latest (should be no-op)
|
||||||
|
log notice "test case: ${WHITE}migrate to latest when already at latest"
|
||||||
|
|
||||||
|
if ! migrator --db-url "$DATABASE_URL" migrate to latest; then
|
||||||
|
log error "test failed: ${WHITE}migrate to latest (no-op) failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
applied_count=$(psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL")
|
||||||
|
if [ "$applied_count" != "4" ]; then
|
||||||
|
log error "test failed: ${WHITE}expected 4 migrations, got $applied_count"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log info "migrate to latest (no-op): success"
|
||||||
|
|
||||||
|
### CASE 6: Partial migration then up all
|
||||||
|
log notice "test case: ${WHITE}partial migration then up all"
|
||||||
|
|
||||||
|
# Revert to first migration only
|
||||||
|
migrator --db-url "$DATABASE_URL" migrate down 3
|
||||||
|
|
||||||
|
applied_count=$(psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL")
|
||||||
|
if [ "$applied_count" != "1" ]; then
|
||||||
|
log error "test failed: ${WHITE}expected 1 migration after down 3, got $applied_count"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Now apply all remaining
|
||||||
|
if ! migrator --db-url "$DATABASE_URL" migrate up all; then
|
||||||
|
log error "test failed: ${WHITE}migrate up all from partial state failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
applied_count=$(psql -Atc "SELECT COUNT(*) FROM hectic.migration" "$DATABASE_URL")
|
||||||
|
if [ "$applied_count" != "4" ]; then
|
||||||
|
log error "test failed: ${WHITE}expected 4 migrations after up all, got $applied_count"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log notice "test passed"
|
||||||
|
|
||||||
72
test/package/migrator/test/sqlite/migrate-to-latest.sh
Normal file
72
test/package/migrator/test/sqlite/migrate-to-latest.sh
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#!/bin/dash
|
||||||
|
|
||||||
|
HECTIC_NAMESPACE=test-sqlite-migrate-to-latest
|
||||||
|
|
||||||
|
log notice "test case: ${WHITE}SQLite migrate to latest migration"
|
||||||
|
|
||||||
|
# Create SQLite database
|
||||||
|
SQLITE_DB="$PWD/test.db"
|
||||||
|
export DB_URL="sqlite://$SQLITE_DB"
|
||||||
|
|
||||||
|
# Create initial schema
|
||||||
|
sqlite3 "$SQLITE_DB" "CREATE TABLE posts (id INTEGER PRIMARY KEY)"
|
||||||
|
|
||||||
|
# Initialize migrator
|
||||||
|
if ! migrator --db-url "$DB_URL" init; then
|
||||||
|
log error "test failed: ${WHITE}init failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create migrations directory with 3 migrations
|
||||||
|
mkdir -p migration
|
||||||
|
for i in 1 2 3; do
|
||||||
|
mig_name="2025010100000${i}-migration-${i}"
|
||||||
|
mkdir -p "migration/${mig_name}"
|
||||||
|
|
||||||
|
echo "ALTER TABLE posts ADD COLUMN field${i} TEXT;" > "migration/${mig_name}/up.sql"
|
||||||
|
# Note: SQLite DROP COLUMN requires table recreation before 3.35.0
|
||||||
|
cat > "migration/${mig_name}/down.sql" <<SQL
|
||||||
|
-- Simplified: just note the revert in comment
|
||||||
|
-- In production, this would recreate the table without field${i}
|
||||||
|
SQL
|
||||||
|
done
|
||||||
|
|
||||||
|
### CASE 1: migrate up all
|
||||||
|
log notice "test case: ${WHITE}migrate up all (SQLite)"
|
||||||
|
|
||||||
|
if ! migrator --db-url "$DB_URL" migrate up all; then
|
||||||
|
log error "test failed: ${WHITE}migrate up all failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify all 3 migrations were applied
|
||||||
|
applied_count=$(sqlite3 "$SQLITE_DB" "SELECT COUNT(*) FROM hectic_migration")
|
||||||
|
if [ "$applied_count" != "3" ]; then
|
||||||
|
log error "test failed: ${WHITE}expected 3 migrations, got $applied_count"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify all columns exist
|
||||||
|
if ! sqlite3 "$SQLITE_DB" "SELECT field1, field2, field3 FROM posts LIMIT 0" >/dev/null 2>&1; then
|
||||||
|
log error "test failed: ${WHITE}not all columns were added"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log info "migrate up all: success"
|
||||||
|
|
||||||
|
### CASE 2: migrate to latest when already at latest
|
||||||
|
log notice "test case: ${WHITE}migrate to latest when already at latest (SQLite)"
|
||||||
|
|
||||||
|
if ! migrator --db-url "$DB_URL" migrate to latest; then
|
||||||
|
log error "test failed: ${WHITE}migrate to latest (no-op) failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
applied_count=$(sqlite3 "$SQLITE_DB" "SELECT COUNT(*) FROM hectic_migration")
|
||||||
|
if [ "$applied_count" != "3" ]; then
|
||||||
|
log error "test failed: ${WHITE}expected 3 migrations, got $applied_count"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log notice "test passed"
|
||||||
|
|
||||||
Reference in New Issue
Block a user