Tests live in the repository-root tests/ directory and use Drogon's own
DROGON_TEST framework (not gtest). The test binary is built by
tests/CMakeLists.txt and registered with CTest.
tests/
├── main.cc # DROGON_TEST_MAIN entry; overrides listener port
├── TestConfigHelper.h # testPort() / baseUrl() helpers (PAY_TEST_PORT, default 5567)
├── CMakeLists.txt # builds the test binary + ctest registration
├── unit/ # pure logic — no HTTP/DB round-trip
│ ├── AuthCheckTest.cc # auth / scope enforcement
│ ├── ConfigLoaderTest.cc
│ ├── ControllerMetricsTest.cc # /metrics + auth metrics endpoints
│ ├── OnceCallbackTest.cc
│ ├── PayAuthMetricsTest.cc # auth metrics counters + Prometheus format
│ ├── PayUtilsTest.cc
│ └── StartupValidatorTest.cc
└── integration/ # exercise the running Drogon app, DB or HTTP surface
├── CallbackControllerTest.cc
├── CreatePaymentIntegrationTest.cc
├── HealthProbeTest.cc # /healthz, /readyz probes
├── HttpResponseHeadersTest.cc # CORS preflight + security headers
├── IdempotencyIntegrationTest.cc
├── PayErrorCategoryTest.cc # error-code → HTTP status mapping
├── QueryOrderListAndReconcileTest.cc
├── QueryOrderTest.cc
├── ReconcileSummaryTest.cc
├── RefundQueryTest.cc
├── RouteRegistrationSmokeTest.cc
├── WechatCallbackIntegrationTest.cc
└── WechatPayClientTest.cc
HTTP-level e2e smoke scripts live with the host they exercise:
examples/pay-server/scripts/e2e_test.sh / e2e_test.ps1.
Tests are driven by CTest. They require PostgreSQL (and Redis when the
redis_client key is present in the loaded config). The test binary loads a
copy of examples/pay-server/config.json but overrides the listener port to
PAY_TEST_PORT (default 5567) so it never collides with a locally running
dev PayServer on 5566.
ctest --test-dir build/windows-msvc -C Release # Windows
ctest --test-dir build/linux-release # Linux/macOSOr through the dev wrappers, which pick the preset for you, refuse to run
before a build exists, and refuse to call an empty suite a pass:
examples/pay-server/scripts/test.sh (Windows:
examples\pay-server\scripts\test.bat), with -l to list case names,
-r <ExactName> to run one case and -o to also keep test_results.log in the
build directory. Per-case output is on by default — both wrappers set
VERBOSE=1 before parsing arguments, so -v is accepted only for flag parity
and there is no switch that turns the detail off.
tests/CMakeLists.txt registers one CTest case (PayBackendTests) that
runs every DROGON_TEST in a single process from the binary's own output
directory, so the ./config.json and ./.env resolve and one process exit code
propagates to CTest unchanged. CTest therefore cannot select an individual
case: ctest -R RefundQuery matches nothing, prints "No tests were found!!!"
and exits 0, which reads as a pass. Filter through the test binary instead —
its -r takes an exact case name and exits 1 when no case matches:
examples\pay-server\scripts\test.bat -l # list exact names
examples\pay-server\scripts\test.bat -r PayIdempotency_RedisSetNx
./build/windows-msvc/tests/Release/PayBackendTests -r PayIdempotency_RedisSetNx # directThe direct form must run from the binary's own directory, which is where
build.bat / build.sh put the config.json and .env the suite loads; the
wrappers already cd there for you.
cd examples/pay-server/scripts
./e2e_test.sh # Bash
./e2e_test.ps1 # PowerShellPlace a new file under tests/unit/ when it only calls functions/classes
directly, and under tests/integration/ when it talks to the in-process
Drogon server, the database or Redis. Register it in the matching section of
tests/CMakeLists.txt (explicit list, no glob).
Tests use the DROGON_TEST macros (TEST, assertions via the framework).
Inject test channels with PayPlugin::setTestChannels(...) (the legacy
setTestClients(...) adapter is kept for compatibility). Listener/client base
URLs should be built from pay::test_util::baseUrl() rather than hardcoding a
port, so tests follow PAY_TEST_PORT.
- Service / handler coverage across create / query / refund / callback paths
- Integration tests: all critical paths through the real Drogon HTTP layer
- Edge cases: error handling, validation, idempotency, error-code mapping
- Auth: missing / invalid key, scope denial, not-configured
Tests run automatically on:
- The
linux-build-and-testandwindows-build-and-testMAIN legs, on every pull request: the fullPayBackendTestssuite against a provisioned Postgres+Redis. Both are required status checks — the ruleset matches them by their reported context,<job name> / build-test— so they gate the merge. coverage.yml: the same suite built Debug+gcov, ratcheted per directory bucket.- The
macos-buildleg compiles the identical targets on arm64 clang with-Werrorbut runs no tests — its image cannot stand a database up, and it is the only lane that sees clang-exclusive diagnostics. Its context ismacos-build / build-test. See AGENTS.md "CI".
- Build the test schema with the migration executor, not by hand:
examples/pay-server/scripts/setup_database.sh(or.bat) resets the schema and replayssql/NNN_*.sqlthroughscripts/migrate_db.py; add--keep-datato apply only what is missing - The test config points at a throwaway database; isolate per CI run
- External provider calls are stubbed via test channels (no real network)