From f56b6b898925f3dff0552a648299199047e90acb Mon Sep 17 00:00:00 2001 From: Hayodea Hekol Date: Fri, 5 Jun 2026 15:59:55 -0400 Subject: [PATCH] Enum-only headers don't get generated so don't expect them --- cmake/dbGenODB.cmake | 14 +- cmake/dbGenerationCommon.cmake | 190 +++++++++++++++--- tests/db-actions/CMakeLists.txt | 1 + .../scripts/openapi_odb_table_filter.cmake | 131 ++++++++++++ .../scripts/regression_targets.cmake | 34 ++++ 5 files changed, 344 insertions(+), 26 deletions(-) create mode 100644 tests/db-actions/scripts/openapi_odb_table_filter.cmake diff --git a/cmake/dbGenODB.cmake b/cmake/dbGenODB.cmake index a2abfdc..1a81c15 100644 --- a/cmake/dbGenODB.cmake +++ b/cmake/dbGenODB.cmake @@ -13,16 +13,26 @@ function(cppbessot_add_db_gen_odb_target schema_dir) # - Files under `/generated-odb-source/{sqlite,postgre}`. cppbessot_validate_schema_dir_name("${schema_dir}") cppbessot_get_schema_dir_path(_version_dir "${schema_dir}") + cppbessot_get_expected_odb_generation_artifacts( + _expected_odb_model_headers + _expected_odb_sources + "${schema_dir}") + _cppbessot_get_openapi_file_path(_openapi_file "${schema_dir}") - add_custom_target(db_gen_odb_logic + add_custom_command( + OUTPUT ${_expected_odb_sources} COMMAND "${CMAKE_COMMAND}" -DCPPBESSOT_ODB_EXECUTABLE=${CPPBESSOT_ODB_EXECUTABLE} -DCPPBESSOT_VERSION_DIR=${_version_dir} -P "${_CPPBESSOT_DB_GEN_ODB_DIR}/scripts/run_odb_logic.cmake" - DEPENDS db_gen_cpp_headers + DEPENDS "${_openapi_file}" ${_expected_odb_model_headers} COMMENT "Generating ODB ORM sources for ${schema_dir} (sqlite + postgre)" VERBATIM ) + add_custom_target(db_gen_odb_logic + DEPENDS ${_expected_odb_sources}) + add_dependencies(db_gen_odb_logic db_gen_cpp_headers) + set_target_properties(db_gen_odb_logic PROPERTIES EXCLUDE_FROM_ALL TRUE) endfunction() diff --git a/cmake/dbGenerationCommon.cmake b/cmake/dbGenerationCommon.cmake index e5890f7..7d65fc6 100644 --- a/cmake/dbGenerationCommon.cmake +++ b/cmake/dbGenerationCommon.cmake @@ -116,19 +116,22 @@ function(cppbessot_get_model_headers_glob out_var schema_dir) set(${out_var} "${_schema_dir_path}/generated-cpp-source/include/*/model/*.h" PARENT_SCOPE) endfunction() -function(cppbessot_get_openapi_schema_names out_var schema_dir) - # Purpose: Parse top-level component schema names from a schema directory's OpenAPI file. +function(_cppbessot_collect_openapi_schema_names out_var openapi_file filter_mode) + # Purpose: Shared OpenAPI components.schemas walker. # Inputs: - # - out_var: Parent-scope variable name to receive the schema names. - # - schema_dir: Schema directory basename. + # - out_var: Parent-scope variable for collected schema names. + # - openapi_file: Absolute path to openapi.yaml. + # - filter_mode: ALL (every top-level schema) or ODB_TABLE (x-odbTable only). # Outputs: - # - (PARENT_SCOPE): List of top-level component schema names. - cppbessot_get_schema_dir_path(_schema_dir_path "${schema_dir}") - cppbessot_assert_openapi_exists("${schema_dir}") - set(_openapi_file "${_schema_dir_path}/openapi/openapi.yaml") + # - (PARENT_SCOPE): Collected schema names. + if(NOT "${filter_mode}" STREQUAL "ALL" AND NOT "${filter_mode}" STREQUAL "ODB_TABLE") + message(FATAL_ERROR + "Unsupported OpenAPI schema filter `${filter_mode}`; expected ALL or ODB_TABLE.") + endif() - file(STRINGS "${_openapi_file}" _openapi_lines) + file(STRINGS "${openapi_file}" _openapi_lines) set(_schema_names) + set(_current_schema_name) set(_in_components FALSE) set(_in_schemas FALSE) @@ -136,8 +139,16 @@ function(cppbessot_get_openapi_schema_names out_var schema_dir) if(_in_schemas) if(_line MATCHES "^[^ ]" OR _line MATCHES "^ [^ ]") set(_in_schemas FALSE) + unset(_current_schema_name) elseif(_line MATCHES "^ ([A-Za-z_][A-Za-z0-9_]*)[ \t]*:[ \t]*$") - list(APPEND _schema_names "${CMAKE_MATCH_1}") + set(_current_schema_name "${CMAKE_MATCH_1}") + if("${filter_mode}" STREQUAL "ALL") + list(APPEND _schema_names "${_current_schema_name}") + endif() + elseif("${filter_mode}" STREQUAL "ODB_TABLE" + AND _current_schema_name + AND _line MATCHES "^ x-odbTable:[ \t]*") + list(APPEND _schema_names "${_current_schema_name}") endif() endif() @@ -155,10 +166,52 @@ function(cppbessot_get_openapi_schema_names out_var schema_dir) endforeach() if(NOT _schema_names) - message(FATAL_ERROR - "No component schema names were found in ${_openapi_file}.") + if("${filter_mode}" STREQUAL "ODB_TABLE") + message(FATAL_ERROR + "No x-odbTable schema names were found in ${openapi_file}.") + else() + message(FATAL_ERROR + "No component schema names were found in ${openapi_file}.") + endif() endif() + list(REMOVE_DUPLICATES _schema_names) + set(${out_var} "${_schema_names}" PARENT_SCOPE) +endfunction() + +function(_cppbessot_get_openapi_file_path out_var schema_dir) + # Purpose: Resolve and validate a schema directory's SSOT OpenAPI file path. + # Inputs: + # - out_var: Parent-scope variable name to receive the absolute OpenAPI path. + # - schema_dir: Schema directory basename. + # Outputs: + # - (PARENT_SCOPE): Absolute path to openapi.yaml. + cppbessot_get_schema_dir_path(_schema_dir_path "${schema_dir}") + cppbessot_assert_openapi_exists("${schema_dir}") + set(${out_var} "${_schema_dir_path}/openapi/openapi.yaml" PARENT_SCOPE) +endfunction() + +function(cppbessot_get_openapi_schema_names out_var schema_dir) + # Purpose: Parse top-level component schema names from a schema directory's OpenAPI file. + # Inputs: + # - out_var: Parent-scope variable name to receive the schema names. + # - schema_dir: Schema directory basename. + # Outputs: + # - (PARENT_SCOPE): List of top-level component schema names. + _cppbessot_get_openapi_file_path(_openapi_file "${schema_dir}") + _cppbessot_collect_openapi_schema_names(_schema_names "${_openapi_file}" "ALL") + set(${out_var} "${_schema_names}" PARENT_SCOPE) +endfunction() + +function(cppbessot_get_openapi_odb_table_schema_names out_var schema_dir) + # Purpose: Parse OpenAPI schema names that declare x-odbTable (ODB object models). + # Inputs: + # - out_var: Parent-scope variable name to receive the schema names. + # - schema_dir: Schema directory basename. + # Outputs: + # - (PARENT_SCOPE): Schema names with x-odbTable in the OpenAPI file. + _cppbessot_get_openapi_file_path(_openapi_file "${schema_dir}") + _cppbessot_collect_openapi_schema_names(_schema_names "${_openapi_file}" "ODB_TABLE") set(${out_var} "${_schema_names}" PARENT_SCOPE) endfunction() @@ -187,8 +240,41 @@ function(cppbessot_get_expected_cpp_model_outputs out_headers_var out_sources_va set(${out_sources_var} "${_sources}" PARENT_SCOPE) endfunction() +function(_cppbessot_build_odb_table_artifact_paths + schema_dir + schema_names + out_model_headers_var + out_sqlite_sources_var + out_pgsql_sources_var) + # Purpose: Build expected ODB model-header and backend source paths from schema names. + # Inputs: + # - schema_dir: Schema directory basename. + # - schema_names: x-odbTable schema names. + # Outputs: + # - (PARENT_SCOPE): Expected generated ODB model headers. + # - (PARENT_SCOPE): Expected sqlite `*-odb.cxx` sources. + # - (PARENT_SCOPE): Expected postgre `*-odb.cxx` sources. + cppbessot_get_schema_dir_path(_schema_dir_path "${schema_dir}") + + set(_headers) + set(_sqlite_sources) + set(_pgsql_sources) + foreach(_schema_name IN LISTS schema_names) + list(APPEND _headers + "${_schema_dir_path}/generated-cpp-source/include/cppbessot/model/${_schema_name}.h") + list(APPEND _sqlite_sources + "${_schema_dir_path}/generated-odb-source/sqlite/${_schema_name}-odb.cxx") + list(APPEND _pgsql_sources + "${_schema_dir_path}/generated-odb-source/postgre/${_schema_name}-odb.cxx") + endforeach() + + set(${out_model_headers_var} "${_headers}" PARENT_SCOPE) + set(${out_sqlite_sources_var} "${_sqlite_sources}" PARENT_SCOPE) + set(${out_pgsql_sources_var} "${_pgsql_sources}" PARENT_SCOPE) +endfunction() + function(cppbessot_get_expected_odb_outputs out_sqlite_sources_var out_pgsql_sources_var schema_dir) - # Purpose: Infer generated ODB backend source files from OpenAPI schema names. + # Purpose: Infer generated ODB backend source files from x-odbTable OpenAPI schemas. # Inputs: # - out_sqlite_sources_var: Parent-scope variable for sqlite `*-odb.cxx`. # - out_pgsql_sources_var: Parent-scope variable for postgre `*-odb.cxx`. @@ -196,18 +282,74 @@ function(cppbessot_get_expected_odb_outputs out_sqlite_sources_var out_pgsql_sou # Outputs: # - (PARENT_SCOPE): Expected sqlite ODB sources. # - (PARENT_SCOPE): Expected postgre ODB sources. - cppbessot_get_schema_dir_path(_schema_dir_path "${schema_dir}") - cppbessot_get_openapi_schema_names(_schema_names "${schema_dir}") - - set(_sqlite_sources) - set(_pgsql_sources) - foreach(_schema_name IN LISTS _schema_names) - list(APPEND _sqlite_sources - "${_schema_dir_path}/generated-odb-source/sqlite/${_schema_name}-odb.cxx") - list(APPEND _pgsql_sources - "${_schema_dir_path}/generated-odb-source/postgre/${_schema_name}-odb.cxx") - endforeach() + cppbessot_get_openapi_odb_table_schema_names(_schema_names "${schema_dir}") + _cppbessot_build_odb_table_artifact_paths( + "${schema_dir}" + "${_schema_names}" + _expected_model_headers + _sqlite_sources + _pgsql_sources) set(${out_sqlite_sources_var} "${_sqlite_sources}" PARENT_SCOPE) set(${out_pgsql_sources_var} "${_pgsql_sources}" PARENT_SCOPE) endfunction() + +function(cppbessot_get_expected_odb_model_headers out_var schema_dir) + # Purpose: Infer generated C++ model headers for x-odbTable OpenAPI schemas. + # Inputs: + # - out_var: Parent-scope variable name to receive expected ODB model headers. + # - schema_dir: Schema directory basename. + # Outputs: + # - (PARENT_SCOPE): Expected generated model headers for ODB objects. + cppbessot_get_openapi_odb_table_schema_names(_schema_names "${schema_dir}") + _cppbessot_build_odb_table_artifact_paths( + "${schema_dir}" + "${_schema_names}" + _headers + _sqlite_sources + _pgsql_sources) + + set(${out_var} "${_headers}" PARENT_SCOPE) +endfunction() + +function(cppbessot_get_expected_odb_outputs_all out_var schema_dir) + # Purpose: Infer all generated ODB backend `*-odb.cxx` files for both backends. + # Inputs: + # - out_var: Parent-scope variable name to receive expected ODB sources. + # - schema_dir: Schema directory basename. + # Outputs: + # - (PARENT_SCOPE): Expected sqlite + postgre ODB sources. + cppbessot_get_expected_odb_outputs( + _expected_sqlite_odb_sources + _expected_pgsql_odb_sources + "${schema_dir}") + set(_combined + ${_expected_sqlite_odb_sources} + ${_expected_pgsql_odb_sources}) + set(${out_var} "${_combined}" PARENT_SCOPE) +endfunction() + +function(cppbessot_get_expected_odb_generation_artifacts + out_model_headers_var + out_all_sources_var + schema_dir) + # Purpose: Infer ODB generation dependencies and outputs in a single OpenAPI pass. + # Inputs: + # - out_model_headers_var: Parent-scope variable for expected ODB model headers. + # - out_all_sources_var: Parent-scope variable for expected sqlite + postgre sources. + # - schema_dir: Schema directory basename. + # Outputs: + # - (PARENT_SCOPE): Expected generated ODB model headers. + # - (PARENT_SCOPE): Expected sqlite + postgre `*-odb.cxx` sources. + cppbessot_get_openapi_odb_table_schema_names(_schema_names "${schema_dir}") + _cppbessot_build_odb_table_artifact_paths( + "${schema_dir}" + "${_schema_names}" + _headers + _sqlite_sources + _pgsql_sources) + set(_combined ${_sqlite_sources} ${_pgsql_sources}) + + set(${out_model_headers_var} "${_headers}" PARENT_SCOPE) + set(${out_all_sources_var} "${_combined}" PARENT_SCOPE) +endfunction() diff --git a/tests/db-actions/CMakeLists.txt b/tests/db-actions/CMakeLists.txt index ac89588..f760980 100644 --- a/tests/db-actions/CMakeLists.txt +++ b/tests/db-actions/CMakeLists.txt @@ -69,6 +69,7 @@ cppbessot_add_db_action_test(cppbessot_db_action_pgsql_migrate_order pgsql_migra 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_db_action_test(cppbessot_db_gen_migrations_policy migration_policy.cmake) +cppbessot_add_db_action_test(cppbessot_openapi_odb_table_filter openapi_odb_table_filter.cmake) cppbessot_add_real_pgsql_db_action_test( cppbessot_db_action_pgsql_createfrom_real pgsql_createfrom_real.cmake diff --git a/tests/db-actions/scripts/openapi_odb_table_filter.cmake b/tests/db-actions/scripts/openapi_odb_table_filter.cmake new file mode 100644 index 0000000..7452346 --- /dev/null +++ b/tests/db-actions/scripts/openapi_odb_table_filter.cmake @@ -0,0 +1,131 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake") + +cppbessot_test_require_var(CPPBESSOT_TEST_BINARY_DIR) +cppbessot_test_require_var(CPPBESSOT_TEST_MODULE_SOURCE_DIR) + +cppbessot_test_case_dir(_case_dir) +set(_fixture_root "${_case_dir}/openapi-filter") +cppbessot_test_reset_dir("${_fixture_root}") +file(MAKE_DIRECTORY "${_fixture_root}/db/v1.test/openapi") +cppbessot_test_write_file( + "${_fixture_root}/db/v1.test/openapi/openapi.yaml" + "openapi: 3.0.3\n" + "info:\n" + " title: cppbessot-openapi-filter-test\n" + " version: \"1.0.0\"\n" + "paths: {}\n" + "components:\n" + " schemas:\n" + " AgentPasswordHashType:\n" + " type: integer\n" + " format: int32\n" + " enum:\n" + " - 0\n" + " CredentialType:\n" + " type: integer\n" + " format: int32\n" + " enum:\n" + " - 0\n" + " Agents:\n" + " type: object\n" + " x-odbTable: Agents\n" + " properties:\n" + " id:\n" + " type: string\n" + " Roles:\n" + " type: object\n" + " x-odbTable: Roles\n" + " properties:\n" + " id:\n" + " type: string\n") + +set(_module_cmake_dir "${CPPBESSOT_TEST_MODULE_SOURCE_DIR}/cmake") +set(PROJECT_SOURCE_DIR "${_fixture_root}") +set(CPPBESSOT_WORKDIR "db") +include("${_module_cmake_dir}/dbGenerationCommon.cmake") + +cppbessot_get_openapi_schema_names(_all_schema_names "v1.test") +cppbessot_get_openapi_odb_table_schema_names(_odb_table_schema_names "v1.test") +cppbessot_get_expected_odb_outputs( + _sqlite_odb_sources + _pgsql_odb_sources + "v1.test") +cppbessot_get_expected_odb_model_headers(_odb_model_headers "v1.test") +cppbessot_get_expected_odb_outputs_all(_all_odb_sources "v1.test") +cppbessot_get_expected_odb_generation_artifacts( + _generation_model_headers + _generation_all_odb_sources + "v1.test") + +set(_expected_all_schema_names + AgentPasswordHashType + CredentialType + Agents + Roles) +set(_expected_odb_table_schema_names + Agents + Roles) + +function(_cppbessot_assert_same_list label actual expected) + list(SORT actual) + list(SORT expected) + if(NOT "${actual}" STREQUAL "${expected}") + message(FATAL_ERROR + "${label} mismatch.\n" + " expected: ${expected}\n" + " actual: ${actual}") + endif() +endfunction() + +_cppbessot_assert_same_list( + "all schema names" + "${_all_schema_names}" + "${_expected_all_schema_names}") +_cppbessot_assert_same_list( + "x-odbTable schema names" + "${_odb_table_schema_names}" + "${_expected_odb_table_schema_names}") +_cppbessot_assert_same_list( + "generation artifact model headers" + "${_generation_model_headers}" + "${_odb_model_headers}") +_cppbessot_assert_same_list( + "generation artifact ODB sources" + "${_generation_all_odb_sources}" + "${_all_odb_sources}") + +list(LENGTH _sqlite_odb_sources _sqlite_count) +list(LENGTH _pgsql_odb_sources _pgsql_count) +list(LENGTH _odb_model_headers _header_count) +list(LENGTH _all_odb_sources _all_odb_count) + +if(NOT _sqlite_count EQUAL 2) + message(FATAL_ERROR + "Expected 2 sqlite ODB sources, got ${_sqlite_count}: ${_sqlite_odb_sources}") +endif() +if(NOT _pgsql_count EQUAL 2) + message(FATAL_ERROR + "Expected 2 postgre ODB sources, got ${_pgsql_count}: ${_pgsql_odb_sources}") +endif() +if(NOT _header_count EQUAL 2) + message(FATAL_ERROR + "Expected 2 ODB model headers, got ${_header_count}: ${_odb_model_headers}") +endif() +if(NOT _all_odb_count EQUAL 4) + message(FATAL_ERROR + "Expected 4 combined ODB sources, got ${_all_odb_count}: ${_all_odb_sources}") +endif() + +foreach(_forbidden IN ITEMS + AgentPasswordHashType + CredentialType) + foreach(_backend IN ITEMS sqlite postgre) + set(_forbidden_source + "${_fixture_root}/db/v1.test/generated-odb-source/${_backend}/${_forbidden}-odb.cxx") + list(FIND _all_odb_sources "${_forbidden_source}" _forbidden_index) + if(NOT _forbidden_index EQUAL -1) + message(FATAL_ERROR + "Enum schema `${_forbidden}` must not produce `${_forbidden_source}`.") + endif() + endforeach() +endforeach() diff --git a/tests/db-actions/scripts/regression_targets.cmake b/tests/db-actions/scripts/regression_targets.cmake index bc7656d..3427614 100644 --- a/tests/db-actions/scripts/regression_targets.cmake +++ b/tests/db-actions/scripts/regression_targets.cmake @@ -30,5 +30,39 @@ execute_process( cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure") cppbessot_test_build_target_dry_run("${_build_dir}" "cppBeSsotOpenAiModelGen" _model_result _model_stdout _model_stderr) cppbessot_test_assert_success("${_model_result}" "${_model_stderr}" "dry-run openai model lib build") +cppbessot_test_build_target_dry_run("${_build_dir}" "cppBeSsotOdbSqlite" _odb_sqlite_result _odb_sqlite_stdout _odb_sqlite_stderr) +cppbessot_test_assert_success("${_odb_sqlite_result}" "${_odb_sqlite_stderr}" "dry-run sqlite ODB lib build") +cppbessot_test_build_target_dry_run("${_build_dir}" "cppBeSsotOdbPgSql" _odb_pgsql_result _odb_pgsql_stdout _odb_pgsql_stderr) +cppbessot_test_assert_success("${_odb_pgsql_result}" "${_odb_pgsql_stderr}" "dry-run postgre ODB lib build") cppbessot_test_build_target_dry_run("${_build_dir}" "db_gen_migrations" _mig_result _mig_stdout _mig_stderr) cppbessot_test_assert_success("${_mig_result}" "${_mig_stderr}" "dry-run migration generation build") + +# v1.3 includes enum-only schemas that must not become ODB library sources. +set(_v13_build_dir "${CPPBESSOT_TEST_BINARY_DIR}/regression-build-v1.3") +cppbessot_test_reset_dir("${_v13_build_dir}") +execute_process( + COMMAND "${CMAKE_COMMAND}" + -S "${_repo_root}" + -B "${_v13_build_dir}" + -DDB_SCHEMA_DIR_TO_GENERATE=v1.3 + -DDB_SCHEMA_DIR_MIGRATION_FROM=v1.2 + -DDB_SCHEMA_DIR_MIGRATION_TO=v1.3 + -DCPPBESSOT_AUTO_ENABLE=ON + -DCOURESILIENT_USE_LOCAL_CPPBESSOT_ENV=OFF + -DCPPBESSOT_DB_PGSQL_PROD_CONNSTR= + -DCPPBESSOT_DB_PGSQL_DEV_CONNSTR= + -DCPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR= + -DCPPBESSOT_DB_PGSQL_TESTS_CONNSTR= + -DCPPBESSOT_DB_SQLITE_PROD_PATH= + -DCPPBESSOT_DB_SQLITE_DEV_PATH= + -DCPPBESSOT_DB_SQLITE_PRODDEV_PATH= + -DCPPBESSOT_DB_SQLITE_TESTS_PATH= + RESULT_VARIABLE _v13_cfg_result + OUTPUT_VARIABLE _v13_cfg_stdout + ERROR_VARIABLE _v13_cfg_stderr +) +cppbessot_test_assert_success("${_v13_cfg_result}" "${_v13_cfg_stderr}" "v1.3 fixture configure") +cppbessot_test_build_target_dry_run("${_v13_build_dir}" "cppBeSsotOdbSqlite" _v13_sqlite_result _v13_sqlite_stdout _v13_sqlite_stderr) +cppbessot_test_assert_success("${_v13_sqlite_result}" "${_v13_sqlite_stderr}" "v1.3 dry-run sqlite ODB lib build") +cppbessot_test_build_target_dry_run("${_v13_build_dir}" "cppBeSsotOdbPgSql" _v13_pgsql_result _v13_pgsql_stdout _v13_pgsql_stderr) +cppbessot_test_assert_success("${_v13_pgsql_result}" "${_v13_pgsql_stderr}" "v1.3 dry-run postgre ODB lib build")