add_executable(neograph_tests
    test_graph_state.cpp
    test_graph_engine.cpp
    test_checkpoint.cpp
    test_store.cpp
    test_node.cpp
    test_loader.cpp
    test_json_path.cpp
    test_pending_writes.cpp
    test_plan_execute_graph.cpp
    test_signal_dispatch.cpp
    test_multi_node_resume.cpp
    test_scheduler.cpp
    test_node_execution.cpp
    test_compiler.cpp
    test_barrier_persistence.cpp
    test_coordinator.cpp
    test_sends_interrupt_order.cpp
    test_multi_send_events.cpp
    test_multi_send_routing.cpp
    test_schema_provider_concurrent.cpp
    test_schema_provider_stream_usage.cpp
    test_schema_provider_responses_stream.cpp
    test_rate_limited_provider.cpp
    test_admin_api_barrier_preservation.cpp
    test_intent_classifier_streaming.cpp
    test_incremental_checkpoint.cpp
    test_deep_research_human_review.cpp
    test_async_conn_pool.cpp
    test_async_http_errors.cpp
    test_async_sse_parser.cpp
    test_async_ws_codec.cpp
    test_async_ws_loopback.cpp
    test_schema_provider_ws_responses.cpp
    test_async_http_stream.cpp
    test_async_http_options.cpp
    test_provider_async_default.cpp
    test_openai_provider_async.cpp
    test_checkpoint_async_default.cpp
    test_node_async_default.cpp
    test_graph_engine_async_api.cpp
    test_async_tool.cpp
    test_executor_async_retry.cpp
    test_executor_async_parallel.cpp
    test_async_run_sync_pool.cpp
    test_node_cache.cpp
    test_node_default_dispatch.cpp
    test_concurrent_stress.cpp
)

if(NEOGRAPH_BUILD_MCP)
    target_sources(neograph_tests PRIVATE
        test_mcp_client_async.cpp
        test_mcp_stdio_async.cpp
    )
endif()

if(NEOGRAPH_BUILD_A2A)
    target_sources(neograph_tests PRIVATE
        test_a2a_types.cpp
        test_a2a_client.cpp
        test_a2a_server.cpp
    )
endif()

# Smoke tests for the async::async_get helper added with the A2A client
# — the GET path was new and build_request gained a method parameter,
# so these guard the surface against future POST/GET regressions.
target_sources(neograph_tests PRIVATE test_async_get.cpp)

# PostgresCheckpointStore integration tests are gated on NEOGRAPH_BUILD_POSTGRES
# at the build level (so projects without libpqxx still build the test
# binary) and on NEOGRAPH_TEST_POSTGRES_URL at runtime (skipped if unset).
if(NEOGRAPH_BUILD_POSTGRES)
    target_sources(neograph_tests PRIVATE test_postgres_checkpoint.cpp)
endif()

if(NEOGRAPH_BUILD_SQLITE)
    target_sources(neograph_tests PRIVATE test_sqlite_checkpoint.cpp)
endif()

target_link_libraries(neograph_tests PRIVATE
    neograph::core
    neograph::llm
    neograph::async
    httplib OpenSSL::SSL OpenSSL::Crypto
    GTest::gtest_main
)

if(NEOGRAPH_BUILD_MCP)
    target_link_libraries(neograph_tests PRIVATE neograph::mcp)
endif()

if(NEOGRAPH_BUILD_A2A)
    target_link_libraries(neograph_tests PRIVATE neograph::a2a)
endif()

if(NEOGRAPH_BUILD_POSTGRES)
    # The test TU pokes libpq directly (PQconnectdb + pg_terminate_backend
    # sibling path in RetriesAfterBrokenConnection), so it needs the
    # libpq include / link in addition to neograph::postgres.
    target_link_libraries(neograph_tests PRIVATE neograph::postgres PostgreSQL::PostgreSQL)
endif()

if(NEOGRAPH_BUILD_SQLITE)
    target_link_libraries(neograph_tests PRIVATE neograph::sqlite)
endif()

include(GoogleTest)

# ThreadSanitizer ASLR notice: when CMAKE_CXX_FLAGS contains
# `-fsanitize=thread`, the test binaries can FATAL with
# "unexpected memory mapping" on Linux kernels with high
# mmap_rnd_bits (newer Ubuntu / RHEL defaults). Workaround is to
# invoke ctest itself through `setarch x86_64 -R` — the
# ADDR_NO_RANDOMIZE flag inherits through fork, so every test
# child also gets a low-randomization layout TSan understands:
#
#     setarch x86_64 -R ctest -E "BIG_|valgrind"
#
# CI's tsan-test job does this in its run-step. There is no clean
# CMake hook to inject the launcher into add_test commands
# generated by gtest_discover_tests, so we document the contract
# rather than try to hide it.
if(CMAKE_CXX_FLAGS MATCHES "fsanitize=thread")
    message(STATUS "TSan detected — invoke ctest through "
                   "`setarch x86_64 -R ctest ...` to avoid the "
                   "ASLR/`unexpected memory mapping` FATAL.")
endif()

if(NEOGRAPH_BUILD_POSTGRES)
    # PostgresCheckpointTest cases share one DB and call drop_schema in
    # SetUp/TearDown — running them in parallel collides on DDL. Mark
    # them with a RESOURCE_LOCK so ctest serializes only this group
    # while the other ~200 tests still run concurrently.
    gtest_discover_tests(neograph_tests
        TEST_FILTER "PostgresCheckpointTest.*"
        PROPERTIES RESOURCE_LOCK postgres_test_db
    )
    gtest_discover_tests(neograph_tests
        TEST_FILTER "-PostgresCheckpointTest.*"
    )
else()
    gtest_discover_tests(neograph_tests)
endif()
