From e09625c4666f3630ef145b0a48435fc50d62ab90 Mon Sep 17 00:00:00 2001 From: Hayodea Hekol Date: Wed, 29 Apr 2026 23:49:07 -0400 Subject: [PATCH] Add PGSql tests --- CMakeLists.txt | 2 + .../add-migration-cmds-prod-proddev-dev.md | 1 - tests/db-actions/CMakeLists.txt | 67 ++++++- tests/db-actions/cmake/TestCommon.cmake | 181 ++++++++++++++++++ .../scripts/pgsql_backfill_real.cmake | 49 +++++ .../scripts/pgsql_createfrom_real.cmake | 38 ++++ .../scripts/pgsql_migrate_real.cmake | 33 ++++ .../scripts/pgsql_proddev_clone_real.cmake | 53 +++++ .../scripts/pgsql_proddev_stale_real.cmake | 35 ++++ 9 files changed, 453 insertions(+), 6 deletions(-) create mode 100644 tests/db-actions/scripts/pgsql_backfill_real.cmake create mode 100644 tests/db-actions/scripts/pgsql_createfrom_real.cmake create mode 100644 tests/db-actions/scripts/pgsql_migrate_real.cmake create mode 100644 tests/db-actions/scripts/pgsql_proddev_clone_real.cmake create mode 100644 tests/db-actions/scripts/pgsql_proddev_stale_real.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d243e8..bbf102f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,8 @@ set(CPPBESSOT_ODB_TEST_SQLITE_CONNSTR "" CACHE STRING "Optional SQLite connection string for ODB runtime tests") set(CPPBESSOT_ODB_TEST_PGSQL_CONNSTR "" CACHE STRING "Optional PostgreSQL conninfo string for ODB runtime tests") +set(CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR "" CACHE STRING + "Optional PostgreSQL admin conninfo string for real db-action test database lifecycle operations") include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CppBeSSOT.cmake") diff --git a/docs/prompts/add-migration-cmds-prod-proddev-dev.md b/docs/prompts/add-migration-cmds-prod-proddev-dev.md index cd36112..65e7f61 100644 --- a/docs/prompts/add-migration-cmds-prod-proddev-dev.md +++ b/docs/prompts/add-migration-cmds-prod-proddev-dev.md @@ -21,4 +21,3 @@ Be very aggressive about splitting and isolating code into common subfunctions a ---- That sqlite_apply.py script: can it be done as a bash script instead? I'd prefer to add the sqlite3 cli to the project's CMake find_* targets, than to add python unnecessarily. - diff --git a/tests/db-actions/CMakeLists.txt b/tests/db-actions/CMakeLists.txt index ec5836f..05adff9 100644 --- a/tests/db-actions/CMakeLists.txt +++ b/tests/db-actions/CMakeLists.txt @@ -1,11 +1,47 @@ +function(_cppbessot_db_action_test_common_args out_var test_name script_name) + set(_args + "-DCPPBESSOT_TEST_NAME=${test_name}" + "-DCPPBESSOT_TEST_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}" + "-DCPPBESSOT_TEST_MODULE_SOURCE_DIR=${PROJECT_SOURCE_DIR}" + "-DCPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR=${CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR}" + "-DCPPBESSOT_DB_PGSQL_DEV_CONNSTR=${CPPBESSOT_DB_PGSQL_DEV_CONNSTR}" + "-DCPPBESSOT_DB_PGSQL_PROD_CONNSTR=${CPPBESSOT_DB_PGSQL_PROD_CONNSTR}" + "-DCPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR=${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" + "-DCPPBESSOT_DB_ACTION_TEST_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/scripts/${script_name}") + set(${out_var} "${_args}" PARENT_SCOPE) +endfunction() + function(cppbessot_add_db_action_test test_name script_name) + _cppbessot_db_action_test_common_args(_test_args "${test_name}" "${script_name}") add_test( NAME ${test_name} - COMMAND "${CMAKE_COMMAND}" - "-DCPPBESSOT_TEST_NAME=${test_name}" - "-DCPPBESSOT_TEST_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}" - "-DCPPBESSOT_TEST_MODULE_SOURCE_DIR=${PROJECT_SOURCE_DIR}" - -P "${CMAKE_CURRENT_SOURCE_DIR}/scripts/${script_name}") + COMMAND "${CMAKE_COMMAND}" ${_test_args} -P "${CMAKE_CURRENT_SOURCE_DIR}/scripts/${script_name}") +endfunction() + +function(cppbessot_add_real_pgsql_db_action_test test_name script_name) + find_program(_psql psql) + if(NOT _psql) + message(STATUS "Skipping real PostgreSQL db-action test `${test_name}` because `psql` is not available.") + return() + endif() + + if("${CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR}" STREQUAL "") + message(STATUS "Skipping real PostgreSQL db-action test `${test_name}` because CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR is empty.") + return() + endif() + + foreach(_required_var IN LISTS ARGN) + if("${${_required_var}}" STREQUAL "") + message(STATUS "Skipping real PostgreSQL db-action test `${test_name}` because ${_required_var} is empty.") + return() + endif() + endforeach() + + _cppbessot_db_action_test_common_args(_test_args "${test_name}" "${script_name}") + add_test( + NAME ${test_name} + COMMAND "${CMAKE_COMMAND}" ${_test_args} -P "${CMAKE_CURRENT_SOURCE_DIR}/scripts/${script_name}") + set_tests_properties(${test_name} PROPERTIES RUN_SERIAL TRUE) endfunction() cppbessot_add_db_action_test(cppbessot_db_action_invalid_target invalid_target.cmake) @@ -23,4 +59,25 @@ cppbessot_add_db_action_test(cppbessot_db_action_pgsql_createfrom_mock pgsql_cre cppbessot_add_db_action_test(cppbessot_db_action_pgsql_migrate_order pgsql_migrate_order.cmake) cppbessot_add_db_action_test(cppbessot_db_action_pgsql_stale_abort pgsql_stale_abort.cmake) cppbessot_add_db_action_test(cppbessot_db_action_backfill_env_no_structural backfill_env_no_structural.cmake) +cppbessot_add_real_pgsql_db_action_test( + cppbessot_db_action_pgsql_createfrom_real + pgsql_createfrom_real.cmake + CPPBESSOT_DB_PGSQL_DEV_CONNSTR) +cppbessot_add_real_pgsql_db_action_test( + cppbessot_db_action_pgsql_migrate_real + pgsql_migrate_real.cmake + CPPBESSOT_DB_PGSQL_DEV_CONNSTR) +cppbessot_add_real_pgsql_db_action_test( + cppbessot_db_action_pgsql_proddev_clone_real + pgsql_proddev_clone_real.cmake + CPPBESSOT_DB_PGSQL_PROD_CONNSTR + CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR) +cppbessot_add_real_pgsql_db_action_test( + cppbessot_db_action_pgsql_proddev_stale_real + pgsql_proddev_stale_real.cmake + CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR) +cppbessot_add_real_pgsql_db_action_test( + cppbessot_db_action_pgsql_backfill_real + pgsql_backfill_real.cmake + CPPBESSOT_DB_PGSQL_DEV_CONNSTR) cppbessot_add_db_action_test(cppbessot_db_action_regression regression_targets.cmake) diff --git a/tests/db-actions/cmake/TestCommon.cmake b/tests/db-actions/cmake/TestCommon.cmake index 961ef09..1595bb8 100644 --- a/tests/db-actions/cmake/TestCommon.cmake +++ b/tests/db-actions/cmake/TestCommon.cmake @@ -20,6 +20,10 @@ function(cppbessot_test_write_file path) file(WRITE "${path}" "${_content}") endfunction() +function(cppbessot_test_cache_string_setting out_var var_name value) + set(${out_var} "set(${var_name} \"${value}\" CACHE STRING \"\")\n" PARENT_SCOPE) +endfunction() + function(cppbessot_test_write_shell_script path) cppbessot_test_write_file("${path}" ${ARGN}) execute_process(COMMAND chmod +x "${path}") @@ -156,6 +160,183 @@ function(cppbessot_test_set_path_with_tool_dir tool_dir) endif() endfunction() +function(cppbessot_test_has_real_pgsql_support out_var) + find_program(_psql psql) + if(_psql AND DEFINED CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR + AND NOT "${CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR}" STREQUAL "") + set(${out_var} TRUE PARENT_SCOPE) + else() + set(${out_var} FALSE PARENT_SCOPE) + endif() +endfunction() + +function(cppbessot_test_require_real_pgsql_support) + cppbessot_test_has_real_pgsql_support(_has_support) + if(NOT _has_support) + message(FATAL_ERROR + "Real PostgreSQL db-action test support requires `psql` and CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR.") + endif() +endfunction() + +function(cppbessot_test_pgsql_exec connstr sql_text) + find_program(_psql psql REQUIRED) + execute_process( + COMMAND "${_psql}" "${connstr}" -v ON_ERROR_STOP=1 -c "${sql_text}" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _stdout + ERROR_VARIABLE _stderr + ) + if(NOT _result EQUAL 0) + message(FATAL_ERROR "Failed PostgreSQL SQL execution.\n${_stdout}\n${_stderr}") + endif() +endfunction() + +function(cppbessot_test_pgsql_exec_file connstr sql_file) + find_program(_psql psql REQUIRED) + execute_process( + COMMAND "${_psql}" "${connstr}" -v ON_ERROR_STOP=1 -f "${sql_file}" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _stdout + ERROR_VARIABLE _stderr + ) + if(NOT _result EQUAL 0) + message(FATAL_ERROR "Failed PostgreSQL SQL file execution for `${sql_file}`.\n${_stdout}\n${_stderr}") + endif() +endfunction() + +function(cppbessot_test_pgsql_query_scalar out_var connstr query) + find_program(_psql psql REQUIRED) + execute_process( + COMMAND "${_psql}" "${connstr}" -v ON_ERROR_STOP=1 -t -A -c "${query}" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _stdout + ERROR_VARIABLE _stderr + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT _result EQUAL 0) + message(FATAL_ERROR "Failed PostgreSQL query.\n${_stderr}") + endif() + string(STRIP "${_stdout}" _value) + set(${out_var} "${_value}" PARENT_SCOPE) +endfunction() + +function(cppbessot_test_pgsql_connstr_dbname out_var connstr) + string(REGEX MATCH "(^|[ \t])dbname=([^ \t]+)" _match " ${connstr}") + if("${CMAKE_MATCH_2}" STREQUAL "") + message(FATAL_ERROR "Expected PostgreSQL connstr to include dbname=... but got `${connstr}`.") + endif() + set(${out_var} "${CMAKE_MATCH_2}" PARENT_SCOPE) +endfunction() + +function(cppbessot_test_pgsql_connstr_replace_dbname out_var connstr new_dbname) + cppbessot_test_pgsql_connstr_dbname(_old_dbname "${connstr}") + string(REGEX REPLACE "(^|[ \t])dbname=[^ \t]+" "\\1dbname=${new_dbname}" _updated " ${connstr}") + string(STRIP "${_updated}" _updated) + set(${out_var} "${_updated}" PARENT_SCOPE) +endfunction() + +function(cppbessot_test_pgsql_unique_dbname out_var role_suffix) + cppbessot_test_require_var(CPPBESSOT_TEST_NAME) + cppbessot_test_require_var(CPPBESSOT_TEST_BINARY_DIR) + string(TOLOWER "${CPPBESSOT_TEST_NAME}" _base) + string(REGEX REPLACE "[^a-z0-9]+" "_" _base "${_base}") + string(REGEX REPLACE "^_+|_+$" "" _base "${_base}") + if("${_base}" STREQUAL "") + set(_base "db_action_test") + endif() + string(TOLOWER "${role_suffix}" _role) + string(REGEX REPLACE "[^a-z0-9]+" "_" _role "${_role}") + string(REGEX REPLACE "^_+|_+$" "" _role "${_role}") + string(SHA256 _scope_hash "${CPPBESSOT_TEST_BINARY_DIR}") + string(SUBSTRING "${_scope_hash}" 0 8 _scope_suffix) + set(${out_var} "cppbessot_${_base}_${_role}_${_scope_suffix}" PARENT_SCOPE) +endfunction() + +function(cppbessot_test_pgsql_isolated_connstr out_var base_connstr role_suffix) + cppbessot_test_pgsql_unique_dbname(_dbname "${role_suffix}") + cppbessot_test_pgsql_connstr_replace_dbname(_updated "${base_connstr}" "${_dbname}") + set(${out_var} "${_updated}" PARENT_SCOPE) +endfunction() + +function(cppbessot_test_pgsql_escape_identifier out_var identifier) + string(REPLACE "\"" "\"\"" _escaped "${identifier}") + set(${out_var} "\"${_escaped}\"" PARENT_SCOPE) +endfunction() + +function(cppbessot_test_pgsql_escape_literal out_var literal) + string(REPLACE "'" "''" _escaped "${literal}") + set(${out_var} "'${_escaped}'" PARENT_SCOPE) +endfunction() + +function(cppbessot_test_pgsql_drop_database connstr) + cppbessot_test_require_real_pgsql_support() + cppbessot_test_pgsql_connstr_dbname(_dbname "${connstr}") + cppbessot_test_pgsql_escape_identifier(_db_ident "${_dbname}") + cppbessot_test_pgsql_escape_literal(_db_lit "${_dbname}") + cppbessot_test_pgsql_exec( + "${CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR}" + "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = ${_db_lit} AND pid <> pg_backend_pid();") + cppbessot_test_pgsql_exec( + "${CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR}" + "DROP DATABASE IF EXISTS ${_db_ident};") +endfunction() + +function(cppbessot_test_pgsql_create_database connstr) + cppbessot_test_require_real_pgsql_support() + cppbessot_test_pgsql_connstr_dbname(_dbname "${connstr}") + cppbessot_test_pgsql_escape_identifier(_db_ident "${_dbname}") + cppbessot_test_pgsql_exec( + "${CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR}" + "CREATE DATABASE ${_db_ident};") +endfunction() + +function(cppbessot_test_pgsql_reset_database connstr) + cppbessot_test_pgsql_drop_database("${connstr}") + cppbessot_test_pgsql_create_database("${connstr}") +endfunction() + +function(cppbessot_test_pgsql_clone_database source_connstr target_connstr) + cppbessot_test_require_real_pgsql_support() + cppbessot_test_pgsql_connstr_dbname(_source_db "${source_connstr}") + cppbessot_test_pgsql_connstr_dbname(_target_db "${target_connstr}") + cppbessot_test_pgsql_escape_identifier(_source_ident "${_source_db}") + cppbessot_test_pgsql_escape_identifier(_target_ident "${_target_db}") + cppbessot_test_pgsql_drop_database("${target_connstr}") + cppbessot_test_pgsql_exec( + "${CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR}" + "CREATE DATABASE ${_target_ident} TEMPLATE ${_source_ident};") +endfunction() + +function(cppbessot_test_shell_single_quote out_var text) + string(REPLACE "'" "'\"'\"'" _quoted "${text}") + set(${out_var} "'${_quoted}'" PARENT_SCOPE) +endfunction() + +function(cppbessot_test_pgsql_clone_command out_var source_connstr target_connstr) + cppbessot_test_require_real_pgsql_support() + cppbessot_test_pgsql_connstr_dbname(_source_db "${source_connstr}") + cppbessot_test_pgsql_connstr_dbname(_target_db "${target_connstr}") + cppbessot_test_pgsql_escape_identifier(_source_ident "${_source_db}") + cppbessot_test_pgsql_escape_identifier(_target_ident "${_target_db}") + cppbessot_test_pgsql_escape_literal(_source_lit "${_source_db}") + cppbessot_test_pgsql_escape_literal(_target_lit "${_target_db}") + cppbessot_test_shell_single_quote(_admin_shell "${CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR}") + cppbessot_test_shell_single_quote( + _term_target_shell + "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = ${_target_lit} AND pid <> pg_backend_pid();") + cppbessot_test_shell_single_quote( + _term_source_shell + "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = ${_source_lit} AND pid <> pg_backend_pid();") + cppbessot_test_shell_single_quote(_drop_shell "DROP DATABASE IF EXISTS ${_target_ident};") + cppbessot_test_shell_single_quote(_create_shell "CREATE DATABASE ${_target_ident} TEMPLATE ${_source_ident};") + set(${out_var} + "psql ${_admin_shell} -v ON_ERROR_STOP=1 -c ${_term_target_shell} && " + "psql ${_admin_shell} -v ON_ERROR_STOP=1 -c ${_term_source_shell} && " + "psql ${_admin_shell} -v ON_ERROR_STOP=1 -c ${_drop_shell} && " + "psql ${_admin_shell} -v ON_ERROR_STOP=1 -c ${_create_shell}" + PARENT_SCOPE) +endfunction() + function(cppbessot_test_sqlite_exec db_path sql_text) find_program(_sqlite3 sqlite3 REQUIRED) get_filename_component(_parent "${db_path}" DIRECTORY) diff --git a/tests/db-actions/scripts/pgsql_backfill_real.cmake b/tests/db-actions/scripts/pgsql_backfill_real.cmake new file mode 100644 index 0000000..0420e09 --- /dev/null +++ b/tests/db-actions/scripts/pgsql_backfill_real.cmake @@ -0,0 +1,49 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake") + +cppbessot_test_require_real_pgsql_support() +cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_DEV_CONNSTR) +cppbessot_test_case_dir(_case_dir) +set(_log_file "${_case_dir}/events.log") +cppbessot_test_write_file("${_log_file}" "") +cppbessot_test_pgsql_isolated_connstr(_dev_connstr "${CPPBESSOT_DB_PGSQL_DEV_CONNSTR}" "dev") + +cppbessot_test_pgsql_reset_database("${_dev_connstr}") +cppbessot_test_pgsql_exec( + "${_dev_connstr}" + "CREATE TABLE sample(id TEXT PRIMARY KEY); INSERT INTO sample(id) VALUES ('row-1');") + +cppbessot_test_cache_string_setting(_dev_setting "CPPBESSOT_DB_PGSQL_DEV_CONNSTR" "${_dev_connstr}") +cppbessot_test_write_project( + "${_case_dir}" + "set(DB_MIGRATE_WITH \"v1.0-v1.1\" CACHE STRING \"\")\n" + "${_dev_setting}") +cppbessot_test_add_schema("${_case_dir}" "v1.1") +cppbessot_test_write_shell_script( + "${_case_dir}/db/migrations/v1.0-v1.1/pre-structural-backfill.sh" + "#!/bin/sh\n" + "set -eu\n" + "printf 'pre:%s:%s:%s\\n' \"$CPPBESSOT_DB_TARGET\" \"$CPPBESSOT_DB_BACKEND\" \"$CPPBESSOT_DB_PGSQL_CONNSTR\" >> \"${_log_file}\"\n") +cppbessot_test_write_shell_script( + "${_case_dir}/db/migrations/v1.0-v1.1/post-structural-backfill.sh" + "#!/bin/sh\n" + "set -eu\n" + "printf 'post:%s:%s\\n' \"$CPPBESSOT_DB_MIGRATE_WITH\" \"$CPPBESSOT_DB_SCHEMA_DIR_TO_GENERATE\" >> \"${_log_file}\"\n") +cppbessot_test_add_sql_file("${_case_dir}/db/migrations/v1.0-v1.1/postgre/01-migrate.sql" + "ALTER TABLE sample ADD COLUMN note TEXT;\n" + "UPDATE sample SET note = 'hooked' WHERE id = 'row-1';\n") + +cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr) +cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure") +cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr) +cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_migrate pgsql backfill real") + +cppbessot_test_assert_log_order( + "${_log_file}" + "pre:dev:postgre:${_dev_connstr}" + "post:v1.0-v1.1:v1.1") +cppbessot_test_pgsql_query_scalar(_note "${_dev_connstr}" "SELECT note FROM sample WHERE id='row-1';") +if(NOT "${_note}" STREQUAL "hooked") + message(FATAL_ERROR "Expected PostgreSQL backfill migration to update note column, got `${_note}`.") +endif() + +cppbessot_test_pgsql_drop_database("${_dev_connstr}") diff --git a/tests/db-actions/scripts/pgsql_createfrom_real.cmake b/tests/db-actions/scripts/pgsql_createfrom_real.cmake new file mode 100644 index 0000000..2e0ffd5 --- /dev/null +++ b/tests/db-actions/scripts/pgsql_createfrom_real.cmake @@ -0,0 +1,38 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake") + +cppbessot_test_require_real_pgsql_support() +cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_DEV_CONNSTR) +cppbessot_test_case_dir(_case_dir) +cppbessot_test_pgsql_isolated_connstr(_dev_connstr "${CPPBESSOT_DB_PGSQL_DEV_CONNSTR}" "dev") + +cppbessot_test_pgsql_reset_database("${_dev_connstr}") +cppbessot_test_pgsql_exec( + "${_dev_connstr}" + "CREATE TABLE legacy_data(id TEXT PRIMARY KEY); INSERT INTO legacy_data(id) VALUES ('old-row');") + +cppbessot_test_cache_string_setting(_dev_setting "CPPBESSOT_DB_PGSQL_DEV_CONNSTR" "${_dev_connstr}") +cppbessot_test_write_project("${_case_dir}" "${_dev_setting}") +cppbessot_test_add_schema("${_case_dir}" "v1.1") +cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/postgre/01-schema.sql" + "CREATE TABLE sample(id TEXT PRIMARY KEY, note TEXT);\n") +cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/postgre/02-seed.sql" + "INSERT INTO sample(id, note) VALUES ('row-1', 'seeded');\n") + +cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr) +cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure") +cppbessot_test_build_target("${_case_dir}/build" "db_createfrom" _build_result _build_stdout _build_stderr) +cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_createfrom pgsql real") + +cppbessot_test_pgsql_query_scalar(_row_count "${_dev_connstr}" "SELECT COUNT(*) FROM sample;") +if(NOT "${_row_count}" STREQUAL "1") + message(FATAL_ERROR "Expected seeded sample row after PostgreSQL createfrom, got `${_row_count}`.") +endif() +cppbessot_test_pgsql_query_scalar( + _legacy_count + "${_dev_connstr}" + "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'legacy_data';") +if(NOT "${_legacy_count}" STREQUAL "0") + message(FATAL_ERROR "Expected legacy_data table to be removed during PostgreSQL createfrom.") +endif() + +cppbessot_test_pgsql_drop_database("${_dev_connstr}") diff --git a/tests/db-actions/scripts/pgsql_migrate_real.cmake b/tests/db-actions/scripts/pgsql_migrate_real.cmake new file mode 100644 index 0000000..0924be7 --- /dev/null +++ b/tests/db-actions/scripts/pgsql_migrate_real.cmake @@ -0,0 +1,33 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake") + +cppbessot_test_require_real_pgsql_support() +cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_DEV_CONNSTR) +cppbessot_test_case_dir(_case_dir) +cppbessot_test_pgsql_isolated_connstr(_dev_connstr "${CPPBESSOT_DB_PGSQL_DEV_CONNSTR}" "dev") + +cppbessot_test_pgsql_reset_database("${_dev_connstr}") +cppbessot_test_pgsql_exec( + "${_dev_connstr}" + "CREATE TABLE sample(id TEXT PRIMARY KEY); INSERT INTO sample(id) VALUES ('row-1');") + +cppbessot_test_cache_string_setting(_dev_setting "CPPBESSOT_DB_PGSQL_DEV_CONNSTR" "${_dev_connstr}") +cppbessot_test_write_project( + "${_case_dir}" + "set(DB_MIGRATE_WITH \"v1.0-v1.1\" CACHE STRING \"\")\n" + "${_dev_setting}") +cppbessot_test_add_schema("${_case_dir}" "v1.1") +cppbessot_test_add_sql_file("${_case_dir}/db/migrations/v1.0-v1.1/postgre/01-migrate.sql" + "ALTER TABLE sample ADD COLUMN note TEXT;\n" + "UPDATE sample SET note = 'migrated' WHERE id = 'row-1';\n") + +cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr) +cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure") +cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr) +cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_migrate pgsql real") + +cppbessot_test_pgsql_query_scalar(_note "${_dev_connstr}" "SELECT note FROM sample WHERE id='row-1';") +if(NOT "${_note}" STREQUAL "migrated") + message(FATAL_ERROR "Expected PostgreSQL migration to set note column, got `${_note}`.") +endif() + +cppbessot_test_pgsql_drop_database("${_dev_connstr}") diff --git a/tests/db-actions/scripts/pgsql_proddev_clone_real.cmake b/tests/db-actions/scripts/pgsql_proddev_clone_real.cmake new file mode 100644 index 0000000..9da79f6 --- /dev/null +++ b/tests/db-actions/scripts/pgsql_proddev_clone_real.cmake @@ -0,0 +1,53 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake") + +cppbessot_test_require_real_pgsql_support() +cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_PROD_CONNSTR) +cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR) +cppbessot_test_case_dir(_case_dir) +cppbessot_test_pgsql_isolated_connstr(_prod_connstr "${CPPBESSOT_DB_PGSQL_PROD_CONNSTR}" "prod") +cppbessot_test_pgsql_isolated_connstr(_proddev_connstr "${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" "proddev") + +cppbessot_test_pgsql_reset_database("${_prod_connstr}") +cppbessot_test_pgsql_drop_database("${_proddev_connstr}") +cppbessot_test_pgsql_exec( + "${_prod_connstr}" + "CREATE TABLE sample(id TEXT PRIMARY KEY); INSERT INTO sample(id) VALUES ('prod-row');") + +cppbessot_test_pgsql_clone_command( + _clone_command + "${_prod_connstr}" + "${_proddev_connstr}") +cppbessot_test_cache_string_setting(_prod_setting "CPPBESSOT_DB_PGSQL_PROD_CONNSTR" "${_prod_connstr}") +cppbessot_test_cache_string_setting(_proddev_setting "CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR" "${_proddev_connstr}") +cppbessot_test_cache_string_setting(_clone_setting "CPPBESSOT_DB_PGSQL_CLONE_PROD_TO_PRODDEV_COMMAND" "${_clone_command}") +cppbessot_test_write_project( + "${_case_dir}" + "set(DB_TARGET \"proddev\" CACHE STRING \"\")\n" + "set(DB_MIGRATE_WITH \"v1.0-v1.1\" CACHE STRING \"\")\n" + "${_prod_setting}" + "${_proddev_setting}" + "${_clone_setting}") +cppbessot_test_add_schema("${_case_dir}" "v1.1") +cppbessot_test_add_sql_file("${_case_dir}/db/migrations/v1.0-v1.1/postgre/01-migrate.sql" + "ALTER TABLE sample ADD COLUMN note TEXT;\n" + "UPDATE sample SET note = 'cloned' WHERE id = 'prod-row';\n") + +cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr) +cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure") +cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr) +cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_migrate pgsql proddev clone real") + +cppbessot_test_pgsql_query_scalar(_proddev_note "${_proddev_connstr}" "SELECT note FROM sample WHERE id='prod-row';") +if(NOT "${_proddev_note}" STREQUAL "cloned") + message(FATAL_ERROR "Expected migrated proddev clone to contain note column, got `${_proddev_note}`.") +endif() +cppbessot_test_pgsql_query_scalar( + _prod_note_column_count + "${_prod_connstr}" + "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'sample' AND column_name = 'note';") +if(NOT "${_prod_note_column_count}" STREQUAL "0") + message(FATAL_ERROR "Expected prod database to remain unchanged after proddev migration.") +endif() + +cppbessot_test_pgsql_drop_database("${_proddev_connstr}") +cppbessot_test_pgsql_drop_database("${_prod_connstr}") diff --git a/tests/db-actions/scripts/pgsql_proddev_stale_real.cmake b/tests/db-actions/scripts/pgsql_proddev_stale_real.cmake new file mode 100644 index 0000000..ef3012e --- /dev/null +++ b/tests/db-actions/scripts/pgsql_proddev_stale_real.cmake @@ -0,0 +1,35 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake") + +cppbessot_test_require_real_pgsql_support() +cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR) +cppbessot_test_case_dir(_case_dir) +cppbessot_test_pgsql_isolated_connstr(_proddev_connstr "${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" "proddev") + +cppbessot_test_pgsql_reset_database("${_proddev_connstr}") +cppbessot_test_pgsql_exec( + "${_proddev_connstr}" + "CREATE TABLE sample(id TEXT PRIMARY KEY); INSERT INTO sample(id) VALUES ('stale-row');") + +cppbessot_test_cache_string_setting(_proddev_setting "CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR" "${_proddev_connstr}") +cppbessot_test_write_project( + "${_case_dir}" + "set(DB_TARGET \"proddev\" CACHE STRING \"\")\n" + "set(DB_MIGRATE_WITH \"v1.0-v1.1\" CACHE STRING \"\")\n" + "set(DB_MIGRATE_PRODDEV_USE_STALE ON CACHE BOOL \"\")\n" + "${_proddev_setting}") +cppbessot_test_add_schema("${_case_dir}" "v1.1") +cppbessot_test_add_sql_file("${_case_dir}/db/migrations/v1.0-v1.1/postgre/01-migrate.sql" + "ALTER TABLE sample ADD COLUMN note TEXT;\n" + "UPDATE sample SET note = 'stale' WHERE id = 'stale-row';\n") + +cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr) +cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure") +cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr) +cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_migrate pgsql proddev stale real") + +cppbessot_test_pgsql_query_scalar(_note "${_proddev_connstr}" "SELECT note FROM sample WHERE id='stale-row';") +if(NOT "${_note}" STREQUAL "stale") + message(FATAL_ERROR "Expected PostgreSQL stale proddev migration to update note column, got `${_note}`.") +endif() + +cppbessot_test_pgsql_drop_database("${_proddev_connstr}")