feat(package): migrator: ! sqlite support
This commit is contained in:
@@ -28,6 +28,105 @@ sha256sum() {
|
||||
cksum --algorithm=sha256 --untagged "$file" | awk '{printf $1}'
|
||||
}
|
||||
|
||||
# detect_db_type()
|
||||
# Returns: "postgresql" or "sqlite"
|
||||
detect_db_type() {
|
||||
case "$DB_URL" in
|
||||
postgresql://*|postgres://*)
|
||||
printf 'postgresql'
|
||||
;;
|
||||
sqlite://*|*.db|*.sqlite|*.sqlite3)
|
||||
printf 'sqlite'
|
||||
;;
|
||||
*)
|
||||
log error "unsupported database URL format: ${WHITE}$DB_URL${NC}"
|
||||
log error "supported formats: postgresql://... or sqlite://... or *.db"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# get_sqlite_path()
|
||||
get_sqlite_path() {
|
||||
case "$DB_URL" in
|
||||
sqlite://*)
|
||||
printf '%s' "$DB_URL" | sed 's|^sqlite://||'
|
||||
;;
|
||||
*)
|
||||
printf '%s' "$DB_URL"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# db_exec(sql)
|
||||
db_exec() {
|
||||
local sql="$1"
|
||||
local db_type
|
||||
db_type=$(detect_db_type)
|
||||
|
||||
case "$db_type" in
|
||||
postgresql)
|
||||
local psql_args
|
||||
psql_args="$(form_psql_args)"
|
||||
# shellcheck disable=SC2086
|
||||
printf '%s' "$sql" | psql $psql_args "$DB_URL"
|
||||
;;
|
||||
sqlite)
|
||||
local db_path
|
||||
db_path=$(get_sqlite_path)
|
||||
printf '%s' "$sql" | sqlite3 "$db_path"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# db_query(sql)
|
||||
db_query() {
|
||||
local sql="$1"
|
||||
local db_type
|
||||
db_type=$(detect_db_type)
|
||||
|
||||
case "$db_type" in
|
||||
postgresql)
|
||||
psql "$DB_URL" --no-align --tuples-only --quiet --command "$sql" | awk NF
|
||||
;;
|
||||
sqlite)
|
||||
local db_path
|
||||
db_path=$(get_sqlite_path)
|
||||
sqlite3 "$db_path" "$sql"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# db_exec_file(file_path)
|
||||
db_exec_file() {
|
||||
local file_path="$1"
|
||||
local db_type
|
||||
db_type=$(detect_db_type)
|
||||
|
||||
case "$db_type" in
|
||||
postgresql)
|
||||
local psql_args escaped_path
|
||||
psql_args="$(form_psql_args)"
|
||||
escaped_path=$(printf '%s' "$file_path" | sed "s/'/''/g")
|
||||
# shellcheck disable=SC2086
|
||||
psql $psql_args "$DB_URL" <<SQL
|
||||
BEGIN;
|
||||
\i '$escaped_path'
|
||||
COMMIT;
|
||||
SQL
|
||||
;;
|
||||
sqlite)
|
||||
local db_path
|
||||
db_path=$(get_sqlite_path)
|
||||
sqlite3 "$db_path" <<SQL
|
||||
BEGIN;
|
||||
.read $file_path
|
||||
COMMIT;
|
||||
SQL
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2120
|
||||
init() {
|
||||
while [ $# -gt 0 ]; do
|
||||
@@ -59,9 +158,15 @@ init() {
|
||||
|
||||
error_handler_no_db_url
|
||||
|
||||
psql_args="$(form_psql_args)"
|
||||
db_type=$(detect_db_type)
|
||||
|
||||
# INHERITS is PostgreSQL-only feature
|
||||
[ ${INHERITS_LIST+x} ] && {
|
||||
if [ "$db_type" != "postgresql" ]; then
|
||||
log error "INHERITS is only supported for PostgreSQL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
oldIFS="$IFS"
|
||||
IFS=','
|
||||
check_inherits=
|
||||
@@ -75,15 +180,13 @@ init() {
|
||||
"$check_inherits" \
|
||||
'COMMIT;')
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
if ! psql $psql_args -c "$check_inherits"; then
|
||||
if ! db_exec "$check_inherits"; then
|
||||
log error "init failed: ${WHITE}one of inherits table does not exists: ${CYAN}$INHERITS_LIST"
|
||||
exit 5
|
||||
fi
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
if ! psql $psql_args -c "$(init_sql)"; then
|
||||
if ! db_exec "$(init_sql)"; then
|
||||
log error "init failed"
|
||||
exit 13
|
||||
fi
|
||||
@@ -92,10 +195,11 @@ init() {
|
||||
# error_handler_no_db_url()
|
||||
error_handler_no_db_url() {
|
||||
[ "${DB_URL+x}" ] || { log error "no ${WHITE}DB_URL${NC} or ${WHITE}--db-url${NC} specified"; exit 3; }
|
||||
check_db_dependencies
|
||||
}
|
||||
|
||||
init_sql() {
|
||||
local sql
|
||||
init_sql_postgresql() {
|
||||
local sql inherits
|
||||
|
||||
inherits=
|
||||
[ ${INHERITS_LIST+x} ] && inherits="$(printf 'INHERITS(%s)' "$INHERITS_LIST")"
|
||||
@@ -103,7 +207,7 @@ init_sql() {
|
||||
sql="$(cat <<EOF
|
||||
BEGIN;
|
||||
|
||||
DO \$$
|
||||
DO \$\$
|
||||
DECLARE
|
||||
version TEXT;
|
||||
BEGIN
|
||||
@@ -120,18 +224,18 @@ BEGIN
|
||||
) THEN
|
||||
SELECT hectic.version.version FROM hectic.version WHERE name = 'migrator' INTO version;
|
||||
IF version != '$VERSION' THEN
|
||||
RAISE EXCEPTION 'Incampetible migrator versions: % and $VERSION', version; -- TODO(yukkop): show versions
|
||||
RAISE EXCEPTION 'Incompatible migrator versions: % and $VERSION', version;
|
||||
END IF;
|
||||
ELSE
|
||||
CREATE DOMAIN hectic.migration_name AS TEXT CHECK (VALUE ~ '^[0-9]{14}-.*');
|
||||
CREATE DOMAIN hectic.sha256 AS CHAR(64) CHECK (VALUE ~ '^[0-9a-f]{64}$');
|
||||
CREATE DOMAIN hectic.sha256 AS CHAR(64) CHECK (VALUE ~ '^[0-9a-f]{64}\$');
|
||||
|
||||
CREATE FUNCTION hectic.sha256_lower() RETURNS trigger AS \$fn$
|
||||
CREATE FUNCTION hectic.sha256_lower() RETURNS trigger AS \$fn\$
|
||||
BEGIN
|
||||
NEW.hash = lower(NEW.hash);
|
||||
RETURN NEW;
|
||||
END;
|
||||
\$fn$ LANGUAGE plpgsql;
|
||||
\$fn\$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TABLE hectic.version (
|
||||
name TEXT PRIMARY KEY,
|
||||
@@ -153,7 +257,7 @@ BEGIN
|
||||
FOR EACH ROW EXECUTE FUNCTION hectic.sha256_lower();
|
||||
END IF;
|
||||
END;
|
||||
\$$;
|
||||
\$\$;
|
||||
|
||||
COMMIT;
|
||||
EOF
|
||||
@@ -162,6 +266,59 @@ EOF
|
||||
printf '%s' "$sql"
|
||||
}
|
||||
|
||||
init_sql_sqlite() {
|
||||
local sql
|
||||
|
||||
sql="$(cat <<'EOF'
|
||||
BEGIN;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS hectic_version (
|
||||
name TEXT PRIMARY KEY,
|
||||
version TEXT NOT NULL,
|
||||
installed_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS hectic_migration (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT UNIQUE NOT NULL CHECK (name GLOB '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-*'),
|
||||
hash TEXT UNIQUE NOT NULL CHECK (length(hash) = 64 AND lower(hash) = hash),
|
||||
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Check version compatibility
|
||||
INSERT OR IGNORE INTO hectic_version (name, version) VALUES ('migrator', 'VERSION_PLACEHOLDER');
|
||||
|
||||
-- Verify version if it already exists
|
||||
SELECT CASE
|
||||
WHEN version != 'VERSION_PLACEHOLDER' AND name = 'migrator'
|
||||
THEN RAISE(ABORT, 'Incompatible migrator versions')
|
||||
ELSE 1
|
||||
END FROM hectic_version WHERE name = 'migrator';
|
||||
|
||||
COMMIT;
|
||||
EOF
|
||||
)"
|
||||
|
||||
# Replace version placeholder
|
||||
sql=$(printf '%s' "$sql" | sed "s/VERSION_PLACEHOLDER/$VERSION/g")
|
||||
|
||||
printf '%s' "$sql"
|
||||
}
|
||||
|
||||
init_sql() {
|
||||
local db_type
|
||||
db_type=$(detect_db_type)
|
||||
|
||||
case "$db_type" in
|
||||
postgresql)
|
||||
init_sql_postgresql
|
||||
;;
|
||||
sqlite)
|
||||
init_sql_sqlite
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
help() {
|
||||
# inherits: List one or more tables the migration table must inherit from
|
||||
echo help
|
||||
@@ -334,11 +491,16 @@ migrate() {
|
||||
|
||||
fs_migrations=$(migration_list)
|
||||
|
||||
db_migrations=$(
|
||||
psql "$DB_URL" --no-align --tuples-only --quiet \
|
||||
--command "SELECT name FROM hectic.migration ORDER BY name ASC" \
|
||||
| awk NF
|
||||
)
|
||||
db_type=$(detect_db_type)
|
||||
|
||||
case "$db_type" in
|
||||
postgresql)
|
||||
db_migrations=$(db_query "SELECT name FROM hectic.migration ORDER BY name ASC")
|
||||
;;
|
||||
sqlite)
|
||||
db_migrations=$(db_query "SELECT name FROM hectic_migration ORDER BY name ASC")
|
||||
;;
|
||||
esac
|
||||
|
||||
log debug "db mig: $db_migrations"
|
||||
db_mig_count=$(printf '%s' "$db_migrations" | wc -l)
|
||||
@@ -415,6 +577,10 @@ migrate() {
|
||||
mig_hash=$(sha256sum "$mig_path")
|
||||
log info "applying migration ${WHITE}$fs_migration${NC} (up)"
|
||||
|
||||
case "$db_type" in
|
||||
postgresql)
|
||||
local psql_args
|
||||
psql_args="$(form_psql_args)"
|
||||
# shellcheck disable=SC2086
|
||||
if ! psql $psql_args "$DB_URL" <<SQL
|
||||
BEGIN;
|
||||
@@ -426,6 +592,22 @@ SQL
|
||||
log error "migration failed: ${WHITE}$fs_migration${NC}"
|
||||
exit 4
|
||||
fi
|
||||
;;
|
||||
sqlite)
|
||||
local db_path
|
||||
db_path=$(get_sqlite_path)
|
||||
if ! sqlite3 "$db_path" <<SQL
|
||||
BEGIN;
|
||||
.read $mig_path
|
||||
INSERT INTO hectic_migration (name, hash) VALUES ('$escaped_name', '$mig_hash');
|
||||
COMMIT;
|
||||
SQL
|
||||
then
|
||||
log error "migration failed: ${WHITE}$fs_migration${NC}"
|
||||
exit 4
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
i=$((i + 1))
|
||||
done
|
||||
@@ -451,6 +633,10 @@ SQL
|
||||
|
||||
log info "reverting migration ${WHITE}$fs_migration${NC} (down)"
|
||||
|
||||
case "$db_type" in
|
||||
postgresql)
|
||||
local psql_args
|
||||
psql_args="$(form_psql_args)"
|
||||
# shellcheck disable=SC2086
|
||||
if ! psql $psql_args "$DB_URL" <<SQL
|
||||
BEGIN;
|
||||
@@ -462,6 +648,22 @@ SQL
|
||||
log error "migration rollback failed: ${WHITE}$fs_migration${NC}"
|
||||
exit 4
|
||||
fi
|
||||
;;
|
||||
sqlite)
|
||||
local db_path
|
||||
db_path=$(get_sqlite_path)
|
||||
if ! sqlite3 "$db_path" <<SQL
|
||||
BEGIN;
|
||||
.read $mig_path
|
||||
DELETE FROM hectic_migration WHERE name = '$escaped_name';
|
||||
COMMIT;
|
||||
SQL
|
||||
then
|
||||
log error "migration rollback failed: ${WHITE}$fs_migration${NC}"
|
||||
exit 4
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
i=$((i - 1))
|
||||
done
|
||||
@@ -506,10 +708,11 @@ SQL
|
||||
}
|
||||
|
||||
form_psql_args() {
|
||||
psql_args="-d $DB_URL -v ON_ERROR_STOP=1"
|
||||
local psql_args="-v ON_ERROR_STOP=1"
|
||||
for var in ${VARIABLE_LIST:-}; do
|
||||
psql_args="$psql_args -v $var"
|
||||
done
|
||||
printf '%s' "$psql_args"
|
||||
}
|
||||
|
||||
create() {
|
||||
@@ -609,10 +812,28 @@ generate_word() {
|
||||
printf '%s' "$w"
|
||||
}
|
||||
|
||||
if ! command -v psql >/dev/null; then
|
||||
log error "Required tool (psql) are not installed."
|
||||
check_db_dependencies() {
|
||||
[ "${DB_URL+x}" ] || return 0 # Skip if no DB_URL yet
|
||||
|
||||
db_type=$(detect_db_type)
|
||||
|
||||
case "$db_type" in
|
||||
postgresql)
|
||||
if ! command -v psql >/dev/null; then
|
||||
log error "Required tool (psql) is not installed."
|
||||
log error "PostgreSQL client tools are required for postgresql:// URLs"
|
||||
exit 127
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
sqlite)
|
||||
if ! command -v sqlite3 >/dev/null; then
|
||||
log error "Required tool (sqlite3) is not installed."
|
||||
log error "SQLite3 client is required for sqlite:// URLs"
|
||||
exit 127
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
if ! [ "${AS_LIBRARY+x}" ]; then
|
||||
while [ $# -gt 0 ]; do
|
||||
|
||||
164
test/package/migrator/README.md
Normal file
164
test/package/migrator/README.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# Migrator Test Suite
|
||||
|
||||
This directory contains comprehensive tests for the database migration tool supporting both PostgreSQL and SQLite.
|
||||
|
||||
## Test Structure
|
||||
|
||||
```
|
||||
test/package/migrator/
|
||||
├── default.nix # Nix test builder - auto-detects test type
|
||||
├── lauch.sh # PostgreSQL test launcher
|
||||
├── lauch-sqlite.sh # SQLite test launcher
|
||||
├── util.sh # Shared test utilities
|
||||
└── test/ # Test cases
|
||||
├── <test-name>/ # PostgreSQL tests (default)
|
||||
└── sqlite-<name>/ # SQLite tests (prefix with "sqlite-")
|
||||
```
|
||||
|
||||
## Test Types
|
||||
|
||||
### PostgreSQL Tests (Default)
|
||||
|
||||
Any test directory or `.sh` file in `test/` will use PostgreSQL by default:
|
||||
- Automatic PostgreSQL setup (initdb, pg_ctl, createdb)
|
||||
- `DATABASE_URL` set to PostgreSQL connection string
|
||||
- Requires: `pkgs.postgresql`
|
||||
|
||||
**Examples:**
|
||||
- `migrate-up-single/`
|
||||
- `migrate-down-multiple/`
|
||||
- `init-migrator.sh`
|
||||
|
||||
### SQLite Tests
|
||||
|
||||
Tests with names starting with `sqlite-` use SQLite:
|
||||
- Simple file-based database
|
||||
- `DATABASE_URL` set to `sqlite:///path/to/test.db`
|
||||
- Requires: `pkgs.sqlite`
|
||||
|
||||
**Examples:**
|
||||
- `sqlite-basic/`
|
||||
- `sqlite-migration-test/`
|
||||
|
||||
## Test Categories
|
||||
|
||||
### Core Functionality
|
||||
- `init-migrator.sh` - Initialization
|
||||
- `init-migrator-with-inherits.sh` - PostgreSQL INHERITS feature
|
||||
- `migrate-up-single/` - Single step up migration
|
||||
- `migrate-up-multiple/` - Multiple step up migrations
|
||||
- `migrate-down-single/` - Single step down migration
|
||||
- `migrate-down-multiple/` - Multiple step down migrations
|
||||
- `migrate-to-forward/` - Migrate to specific version (forward)
|
||||
- `migrate-to-backward/` - Migrate to specific version (backward)
|
||||
- `migrate-already-at-target/` - Edge case: no-op migration
|
||||
|
||||
### Existing Database Support
|
||||
- `migrate-existing-database/` - Add migrator to production DB
|
||||
- `migrate-existing-with-conflicts/` - Handle schema conflicts
|
||||
- `migrate-existing-data-migration/` - Transform existing data
|
||||
|
||||
### SQLite Support
|
||||
- `sqlite-basic/` - Basic SQLite functionality
|
||||
|
||||
### Helper Functions
|
||||
- `function-index-of.sh` - Test index_of helper
|
||||
- `function-migration-list.sh` - Test migration_list helper
|
||||
- `function-generate-word.sh` - Test word generator
|
||||
|
||||
### Utilities
|
||||
- `create-migration.sh` - Test migration creation
|
||||
- `migrations-list/` - Test migration listing
|
||||
- `arguments.sh` - Test argument parsing
|
||||
|
||||
## Creating New Tests
|
||||
|
||||
### PostgreSQL Test
|
||||
|
||||
```bash
|
||||
mkdir -p test/<test-name>/migration/<timestamp>-<name>
|
||||
cat > test/<test-name>/run.sh <<'EOF'
|
||||
#!/bin/dash
|
||||
HECTIC_NAMESPACE=test-my-test
|
||||
log notice "test case: ${WHITE}my test"
|
||||
|
||||
# $DATABASE_URL is automatically set to PostgreSQL
|
||||
migrator --db-url "$DATABASE_URL" init
|
||||
# ... your test code ...
|
||||
|
||||
log notice "test passed"
|
||||
EOF
|
||||
|
||||
# Create up.sql and down.sql migration files
|
||||
```
|
||||
|
||||
### SQLite Test
|
||||
|
||||
Same as above, but prefix the directory name with `sqlite-`:
|
||||
|
||||
```bash
|
||||
mkdir -p test/sqlite-<test-name>/migration/<timestamp>-<name>
|
||||
# ... rest is the same
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
Tests are built and run via Nix:
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
nix build .#checks.x86_64-linux
|
||||
|
||||
# Run specific test
|
||||
nix build .#checks.x86_64-linux.migrator-test-<test-name>
|
||||
|
||||
# Run SQLite tests
|
||||
nix build .#checks.x86_64-linux.migrator-test-sqlite-basic
|
||||
```
|
||||
|
||||
## Test Isolation
|
||||
|
||||
Each test runs in complete isolation:
|
||||
- **PostgreSQL**: Fresh PostgreSQL cluster per test
|
||||
- **SQLite**: Fresh database file per test
|
||||
- Clean working directory
|
||||
- Independent environment variables
|
||||
|
||||
## Available Test Utilities
|
||||
|
||||
From `util.sh`:
|
||||
- `columns(table)` - Get column names from table
|
||||
- `is_number(var)` - Check if variable is numeric
|
||||
|
||||
From test environment:
|
||||
- `log <level> <message>` - Logging (trace, debug, info, notice, error)
|
||||
- `migrator` - The migrator binary under test
|
||||
- `$DATABASE_URL` - Database connection string (auto-configured)
|
||||
|
||||
## Test Conventions
|
||||
|
||||
1. **Naming**: Use descriptive names with hyphens
|
||||
2. **Logging**: Use `log` for output, not `echo`
|
||||
3. **Exit codes**:
|
||||
- 0 = success
|
||||
- 1 = test failure
|
||||
- Other = specific error conditions
|
||||
4. **Cleanup**: Tests are automatically cleaned up by Nix
|
||||
5. **Assertions**: Explicit checks with meaningful error messages
|
||||
|
||||
## Database-Specific Notes
|
||||
|
||||
### PostgreSQL
|
||||
- Full schema support (`hectic.migration`)
|
||||
- Domains with regex validation
|
||||
- Triggers and functions
|
||||
- INHERITS support
|
||||
- TIMESTAMPTZ support
|
||||
|
||||
### SQLite
|
||||
- Simple table names (`hectic_migration`)
|
||||
- CHECK constraints instead of domains
|
||||
- No triggers needed
|
||||
- TEXT timestamps with datetime()
|
||||
- Table recreation for column removal (older SQLite versions)
|
||||
|
||||
@@ -1,37 +1,41 @@
|
||||
{ inputs, self, pkgs, system, ... }: let
|
||||
lib = inputs.nixpkgs.lib;
|
||||
|
||||
# turn anything under ./test into a derivation that exposes $out/run.sh
|
||||
mkTestDrv = name: type:
|
||||
# turn anything under test directory into a derivation that exposes $out/run.sh
|
||||
mkTestDrv = folder: name: type:
|
||||
if type == "directory" then
|
||||
pkgs.runCommand "test-${name}" {} ''
|
||||
if ! [ -f ${./test + "/${name}" + /run.sh} ]; then
|
||||
if ! [ -f ${"${folder}/${name}/run.sh"} ]; then
|
||||
echo no run.sh in test/${name}
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$out"
|
||||
cp -r ${./test + "/${name}"}/* "$out/"
|
||||
cp -r ${"${folder}/${name}"}/* "$out/"
|
||||
chmod +x "$out/run.sh"
|
||||
''
|
||||
else if lib.hasSuffix ".sh" name then
|
||||
pkgs.runCommand "test-${lib.removeSuffix ".sh" name}" {} ''
|
||||
mkdir -p "$out"
|
||||
install -Dm755 ${./test + "/${name}"} "$out/run.sh"
|
||||
install -Dm755 ${"${folder}/${name}"} "$out/run.sh"
|
||||
''
|
||||
else
|
||||
null;
|
||||
|
||||
testDir = builtins.readDir ./test;
|
||||
testDir = folder: builtins.readDir folder;
|
||||
|
||||
# attrset: testName -> drv with run.sh
|
||||
testDrvs =
|
||||
testDrvs = folder:
|
||||
lib.mapAttrs' (n: v:
|
||||
lib.nameValuePair (lib.removeSuffix ".sh" n) v
|
||||
) (lib.filterAttrs (_: v: v != null)
|
||||
(lib.mapAttrs (n: t: mkTestDrv n t) testDir));
|
||||
(lib.mapAttrs (n: t: mkTestDrv folder n t) (testDir folder)));
|
||||
|
||||
postgresqlTestDrvs = testDrvs ./test/postgresql;
|
||||
sqliteTestDrvs = testDrvs ./test/sqlite;
|
||||
|
||||
migrator = self.packages.${system}.migrator;
|
||||
|
||||
mkPgTest = testName: testDrv: pkgs.runCommand "migrator-test-${testName}"
|
||||
{
|
||||
nativeBuildInputs = [ pkgs.coreutils pkgs.gnugrep pkgs.gnused ];
|
||||
@@ -41,10 +45,26 @@
|
||||
test=${testDrv}
|
||||
export HECTIC_LOG=trace
|
||||
${builtins.readFile ./util.sh}
|
||||
${builtins.readFile ./lauch.sh}
|
||||
${builtins.readFile ./lauch-postgresql.sh}
|
||||
|
||||
# success marker for Nix
|
||||
# shellcheck disable=SC2154
|
||||
mkdir -p "$out"
|
||||
'';
|
||||
in lib.mapAttrs (name: drv: mkPgTest name drv) testDrvs
|
||||
|
||||
mkSqliteTest = testName: testDrv: pkgs.runCommand "migrator-test-${testName}"
|
||||
{
|
||||
nativeBuildInputs = [ pkgs.coreutils pkgs.gnugrep pkgs.gnused ];
|
||||
buildInputs = [ pkgs.which migrator pkgs.sqlite ];
|
||||
} ''
|
||||
${builtins.readFile self.legacyPackages.${system}.helpers.posix-shell.log}
|
||||
test=${testDrv}
|
||||
export HECTIC_LOG=trace
|
||||
${builtins.readFile ./util.sh}
|
||||
${builtins.readFile ./lauch-sqlite.sh}
|
||||
|
||||
# success marker for Nix
|
||||
# shellcheck disable=SC2154
|
||||
mkdir -p "$out"
|
||||
'';
|
||||
in (lib.mapAttrs (name: drv: mkPgTest name drv) postgresqlTestDrvs) // (lib.mapAttrs (name: drv: mkSqliteTest name drv) sqliteTestDrvs)
|
||||
|
||||
@@ -6,13 +6,12 @@
|
||||
HECTIC_NAMESPACE=test-laucher
|
||||
export HECTIC_LOG=trace
|
||||
|
||||
# shellcheck disable=SC2154
|
||||
test_derivation="$(basename "$test")"
|
||||
test_name="${test_derivation#*-*-}"
|
||||
|
||||
set -eu
|
||||
|
||||
root_dir="$(dirname $0)"
|
||||
|
||||
HECTIC_LOG=
|
||||
|
||||
# save path to pg_ctl in case $PATH will change
|
||||
@@ -65,8 +64,10 @@ log info "run test ${WHITE}${test_name}${NC}"
|
||||
mkdir './test'
|
||||
cp -r "$test"/* './test/'
|
||||
cd './test'
|
||||
# shellcheck disable=SC1091
|
||||
. './run.sh'
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
HECTIC_NAMESPACE=test-laucher
|
||||
|
||||
log info "finish test pipeline"
|
||||
45
test/package/migrator/lauch-sqlite.sh
Normal file
45
test/package/migrator/lauch-sqlite.sh
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/bin/dash
|
||||
|
||||
# $out - nix derivation output
|
||||
# $test - test and assertion file
|
||||
|
||||
HECTIC_NAMESPACE=test-laucher
|
||||
export HECTIC_LOG=trace
|
||||
|
||||
# shellcheck disable=SC2154
|
||||
test_derivation="$(basename "$test")"
|
||||
test_name="${test_derivation#*-*-}"
|
||||
|
||||
set -eu
|
||||
|
||||
HECTIC_LOG=
|
||||
|
||||
log info 'start test pipeline (SQLite)'
|
||||
|
||||
# temp dirs
|
||||
wd="$PWD"
|
||||
db_file="$wd/test.db"
|
||||
|
||||
# Set up SQLite database URL
|
||||
DATABASE_URL="sqlite://$db_file"
|
||||
export DATABASE_URL
|
||||
|
||||
log info "using SQLite database: $db_file"
|
||||
log info "run test ${WHITE}${test_name}${NC}"
|
||||
|
||||
# run test
|
||||
mkdir './test'
|
||||
cp -r "$test"/* './test/'
|
||||
cd './test'
|
||||
# shellcheck disable=SC1091
|
||||
. './run.sh'
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
HECTIC_NAMESPACE=test-laucher
|
||||
|
||||
log info "finish test pipeline"
|
||||
|
||||
# success marker for Nix
|
||||
# shellcheck disable=SC2154
|
||||
mkdir -p "$out"
|
||||
|
||||
@@ -2,18 +2,6 @@
|
||||
|
||||
HECTIC_NAMESPACE=test-init-migrator
|
||||
|
||||
### CASE 1
|
||||
log notice "test case: ${WHITE}dry run"
|
||||
# NOTE: does not matter exist inherits tables or not, it must not connect to db
|
||||
|
||||
if ! migration_table_sql="$(migrator --inherits tablename --inherits 'table name' init --dry-run)"; then
|
||||
log error "test failed: ${WHITE}error on migration table init dry run"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s' "$migration_table_sql" | grep -Eq 'INHERITS[[:space:]]*\([[:space:]]*"tablename"[[:space:]]*,[[:space:]]*"table name"[[:space:]]*\)' ||
|
||||
{ log error "test failed: ${WHITE}not correct migration table inherits"; exit 1; }
|
||||
|
||||
### CASE 2
|
||||
log notice "test case: ${WHITE}error: table inherit tables that not exists"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
DROP TABLE users;
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
-- SQLite doesn't support DROP COLUMN directly before 3.35.0
|
||||
-- We need to recreate the table
|
||||
CREATE TABLE users_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO users_new (id, name) SELECT id, name FROM users;
|
||||
DROP TABLE users;
|
||||
ALTER TABLE users_new RENAME TO users;
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE users ADD COLUMN email TEXT;
|
||||
|
||||
|
||||
86
test/package/migrator/test/sqlite/sqlite-basic/run.sh
Normal file
86
test/package/migrator/test/sqlite/sqlite-basic/run.sh
Normal file
@@ -0,0 +1,86 @@
|
||||
#!/bin/dash
|
||||
|
||||
HECTIC_NAMESPACE=test-sqlite-basic
|
||||
|
||||
log notice "test case: ${WHITE}SQLite basic migration"
|
||||
|
||||
# Create SQLite database
|
||||
SQLITE_DB="$PWD/test.db"
|
||||
export DB_URL="sqlite://$SQLITE_DB"
|
||||
|
||||
log info "using SQLite database: $SQLITE_DB"
|
||||
|
||||
# Initialize migrator with SQLite
|
||||
if ! migrator --db-url "$DB_URL" init; then
|
||||
log error "test failed: ${WHITE}init failed for SQLite"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify tables were created
|
||||
if ! sqlite3 "$SQLITE_DB" "SELECT name FROM hectic_version WHERE name = 'migrator'" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}hectic_version table not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! sqlite3 "$SQLITE_DB" "SELECT COUNT(*) FROM hectic_migration" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}hectic_migration table not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log info "migrator tables created successfully"
|
||||
|
||||
# Apply first migration
|
||||
if ! migrator --db-url "$DB_URL" migrate up; then
|
||||
log error "test failed: ${WHITE}first migration failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify migration was applied
|
||||
migration_count=$(sqlite3 "$SQLITE_DB" "SELECT COUNT(*) FROM hectic_migration")
|
||||
if [ "$migration_count" != "1" ]; then
|
||||
log error "test failed: ${WHITE}expected 1 migration, got $migration_count"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify table was created
|
||||
if ! sqlite3 "$SQLITE_DB" "SELECT COUNT(*) FROM users" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}users table not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log info "first migration applied successfully"
|
||||
|
||||
# Apply second migration
|
||||
if ! migrator --db-url "$DB_URL" migrate up; then
|
||||
log error "test failed: ${WHITE}second migration failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify email column exists
|
||||
if ! sqlite3 "$SQLITE_DB" "SELECT email FROM users LIMIT 0" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}email column not added"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Migrate down
|
||||
if ! migrator --db-url "$DB_URL" migrate down; then
|
||||
log error "test failed: ${WHITE}migrate down failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify only 1 migration remains
|
||||
migration_count=$(sqlite3 "$SQLITE_DB" "SELECT COUNT(*) FROM hectic_migration")
|
||||
if [ "$migration_count" != "1" ]; then
|
||||
log error "test failed: ${WHITE}expected 1 migration after down, got $migration_count"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify email column removed
|
||||
if sqlite3 "$SQLITE_DB" "SELECT email FROM users LIMIT 0" >/dev/null 2>&1; then
|
||||
log error "test failed: ${WHITE}email column should be removed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log notice "test passed: SQLite support works correctly"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user