From eaecc26d22e709f6e4eb00841e141dd7680efd6b Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Thu, 5 Mar 2026 07:12:48 +0000 Subject: [PATCH] test: Fix RESTAPI tests for containerized CI and improved debuggability Update RESTAPI tests to use 'proxysql' hostname instead of 'localhost' for compatibility with containerized CI environments. Add comprehensive diagnostic logging including test headers, connection progress, and better error reporting for easier debugging of test failures. --- test/tap/tap/utils.cpp | 5 +- .../reg_test_3223-restapi_return_codes-t.cpp | 24 ++++++++-- .../tests/reg_test_3591-restapi_num_fds-t.cpp | 20 +++++++- ...eg_test_4001-restapi_scripts_num_fds-t.cpp | 47 ++++++++++++++----- 4 files changed, 78 insertions(+), 18 deletions(-) diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp index 1ff246c1d..84bf6d023 100644 --- a/test/tap/tap/utils.cpp +++ b/test/tap/tap/utils.cpp @@ -1418,7 +1418,7 @@ const char t_restapi_insert[] { "VALUES (1,%ld,'%s','%s','%s','comm')", }; -const string base_address { "http://localhost:6070/sync/" }; +const string base_address { "http://proxysql:6070/sync/" }; int configure_endpoints( MYSQL* admin, @@ -1464,7 +1464,8 @@ int configure_endpoints( int endpoint_timeout = wait_post_enpoint_ready(full_endpoint, "{}", 1000, 100); if (endpoint_timeout) { - diag("Timeout while trying to reach first valid enpoint"); + diag("Timeout while trying to reach first valid endpoint: %s", full_endpoint.c_str()); + diag("This usually means RESTAPI enabled but script failed to execute. Check TAP_WORKDIR."); return EXIT_FAILURE; } diff --git a/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp b/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp index 86c3336d7..be29f7286 100644 --- a/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp +++ b/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp @@ -31,7 +31,7 @@ using std::vector; using hrc = std::chrono::high_resolution_clock; using nlohmann::json; -const string base_address { "http://localhost:6070/sync/" }; +const string base_address { "http://proxysql:6070/sync/" }; const vector honest_requests { { { "valid_output_script", "%s.py", "POST", 1000 }, { "{}" } }, @@ -126,11 +126,23 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } + diag("=== Regression Test #3223: RESTAPI Script Execution & Return Codes ==="); + diag("This test ensures that ProxySQL RESTAPI correctly handles script execution"); + diag("and returns the expected HTTP status codes and script exit codes."); + diag("The test strategy is:"); + diag("1. Register multiple valid and faulty scripts in RESTAPI routes."); + diag("2. Issue POST/GET requests to these endpoints."); + diag("3. Verify HTTP return codes (200 for success, 400/424 for failures)."); + diag("4. Verify internal script error codes (exit codes, timeouts, signals)."); + diag("5. Check behavior with large outputs and partial flushes."); + diag("========================================================================="); + plan(count_exp_tests(honest_requests, invalid_requests)); MYSQL* admin = mysql_init(NULL); // Initialize connections + diag("Connecting to ProxySQL Admin at %s:%d as %s", cl.host, cl.admin_port, cl.admin_username); if (!admin) { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); return EXIT_FAILURE; @@ -142,12 +154,14 @@ int main(int argc, char** argv) { } // Enable 'RESTAPI' + diag("Enabling RESTAPI on port 6070"); MYSQL_QUERY(admin, "SET admin-restapi_enabled='true'"); MYSQL_QUERY(admin, "SET admin-restapi_port=6070"); MYSQL_QUERY(admin, "LOAD ADMIN VARIABLES TO RUNTIME"); // Clean current 'restapi_routes' if any + diag("Configuring RESTAPI routes..."); MYSQL_QUERY(admin, "DELETE FROM restapi_routes"); // Configure restapi_routes to be used @@ -161,18 +175,20 @@ int main(int argc, char** argv) { honest_requests.begin(), honest_requests.end(), std::back_inserter(v_epts_info), ext_v_epts_info ); + diag("Initializing endpoints for valid requests..."); int ept_conf_res = configure_endpoints(admin, script_base_path, v_epts_info, dummy_ept, true); if (ept_conf_res) { diag("Endpoint configuration failed. Skipping endpoint testing..."); return EXIT_FAILURE; } + diag("Testing valid RESTAPI requests..."); { for (const auto& req : honest_requests) { for (const string& params : req.params) { const string ept { join_path(base_address, req.ept_info.name) }; diag( - "Checking valid '%s' request - ept: '%s', params: '%s'", + " Checking valid '%s' request - ept: '%s', params: '%s'", req.ept_info.method.c_str(), ept.c_str(), params.c_str() ); std::chrono::nanoseconds duration; @@ -294,12 +310,14 @@ int main(int argc, char** argv) { invalid_requests.begin(), invalid_requests.end(), std::back_inserter(i_epts_info), ext_i_epts_info ); + diag("Initializing endpoints for invalid requests..."); ept_conf_res = configure_endpoints(admin, script_base_path, i_epts_info, dummy_ept, true); if (ept_conf_res) { diag("Endpoint configuration failed. Skipping endpoint testing..."); return EXIT_FAILURE; } + diag("Testing invalid RESTAPI requests..."); for (const auto& req : invalid_requests) { for (const ept_pl_t& ept_pl : req.ept_pls) { std::chrono::nanoseconds duration; @@ -308,7 +326,7 @@ int main(int argc, char** argv) { const string ept { join_path(base_address, req.ept_info.name) }; diag( - "Checking valid '%s' request - ept: '%s', params: '%s'", + " Checking invalid '%s' request - ept: '%s', params: '%s'", req.ept_info.method.c_str(), ept.c_str(), ept_pl.params.c_str() ); diff --git a/test/tap/tests/reg_test_3591-restapi_num_fds-t.cpp b/test/tap/tests/reg_test_3591-restapi_num_fds-t.cpp index 13bb29142..d43704bd3 100644 --- a/test/tap/tests/reg_test_3591-restapi_num_fds-t.cpp +++ b/test/tap/tests/reg_test_3591-restapi_num_fds-t.cpp @@ -38,6 +38,17 @@ int main(int argc, char** argv) { diag("Failed to get the required environmental variables."); return -1; } + + diag("=== Regression Test #3591: RESTAPI with High Number of FDs ==="); + diag("This test verifies that the ProxySQL metrics endpoint remains functional"); + diag("even when a large number of client connections (2047) are opened."); + diag("The test strategy is:"); + diag("1. Elevate process FD limits."); + diag("2. Enable RESTAPI on port 6070."); + diag("3. Open 2047 concurrent MySQL connections to ProxySQL."); + diag("4. Verify that the /metrics endpoint is still reachable."); + diag("=============================================================="); + struct rlimit limits { 0, 0 }; getrlimit(RLIMIT_NOFILE, &limits); diag("Old process limits: { %ld, %ld }", limits.rlim_cur, limits.rlim_max); @@ -48,6 +59,7 @@ int main(int argc, char** argv) { MYSQL* admin = mysql_init(NULL); // Initialize connections + diag("Connecting to ProxySQL Admin at %s:%d as %s", cl.host, cl.admin_port, cl.admin_username); if (!admin) { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); return -1; @@ -59,12 +71,14 @@ int main(int argc, char** argv) { } // Enable 'RESTAPI' + diag("Enabling RESTAPI on port 6070"); MYSQL_QUERY(admin, "SET admin-restapi_enabled='true'"); MYSQL_QUERY(admin, "SET admin-restapi_port=6070"); MYSQL_QUERY(admin, "LOAD ADMIN VARIABLES TO RUNTIME"); std::vector mysql_connections {}; + diag("Establishing %d connections to ProxySQL...", NUM_CONNECTIONS); for (int i = 0; i < NUM_CONNECTIONS; i++) { MYSQL* proxy = mysql_init(NULL); if ( @@ -79,11 +93,15 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } mysql_connections.push_back(proxy); + if ((i + 1) % 500 == 0) diag(" Established %d/%d connections...", i + 1, NUM_CONNECTIONS); } + diag("Connections established."); - int endpoint_timeout = wait_get_enpoint_ready("http://localhost:6070/metrics/", 1000, 100); + diag("Verifying RESTAPI /metrics endpoint via proxysql:6070..."); + int endpoint_timeout = wait_get_enpoint_ready("http://proxysql:6070/metrics/", 1000, 100); ok(endpoint_timeout == 0, "The endpoint should be available instead of timing out."); + diag("Closing connections and cleaning up..."); for (int i = 0; i < NUM_CONNECTIONS; i++) { mysql_close(mysql_connections[i]); } diff --git a/test/tap/tests/reg_test_4001-restapi_scripts_num_fds-t.cpp b/test/tap/tests/reg_test_4001-restapi_scripts_num_fds-t.cpp index b035d8c40..4a4a79791 100644 --- a/test/tap/tests/reg_test_4001-restapi_scripts_num_fds-t.cpp +++ b/test/tap/tests/reg_test_4001-restapi_scripts_num_fds-t.cpp @@ -35,7 +35,7 @@ using std::string; using std::vector; const int NUM_CONNECTIONS = 1300; -const string base_address { "http://localhost:6070/sync/" }; +const string base_address { "http://proxysql:6070/sync/" }; const vector honest_requests { { { "valid_output_script", "%s.py", "POST", 1000 }, { "{}" } }, @@ -56,6 +56,17 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } + diag("=== Regression Test #4001: RESTAPI with High FD Usage ==="); + diag("This test verifies that the RESTAPI remains operational when ProxySQL"); + diag("is handling a large number of concurrent file descriptors (> FD_SETSIZE)."); + diag("The test strategy is:"); + diag("1. Elevate process FD limits."); + diag("2. Establish 1300 concurrent MySQL connections to ProxySQL."); + diag("3. While these connections are held, perform continuous RESTAPI requests."); + diag("4. Simultaneously create and destroy additional MySQL connections."); + diag("5. Verify that RESTAPI requests and connection operations remain stable."); + diag("=========================================================="); + diag("Setting new process limits beyond 'FD_SETSIZE'"); struct rlimit limits { 0, 0 }; getrlimit(RLIMIT_NOFILE, &limits); @@ -67,6 +78,7 @@ int main(int argc, char** argv) { MYSQL* admin = mysql_init(NULL); // Initialize connections + diag("Connecting to ProxySQL Admin at %s:%d as %s", cl.host, cl.admin_port, cl.admin_username); if (!admin) { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); return EXIT_FAILURE; @@ -77,7 +89,7 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - string script_base_path { string { cl.workdir } + "reg_test_3223_scripts" }; + const char* d_env = getenv("REGULAR_INFRA_DATADIR"); string script_base_path = (d_env ? string(d_env) + "/reg_test_3223_scripts" : string(cl.workdir) + "reg_test_3223_scripts"); const ept_info_t dummy_ept { "dummy_ept_script", "%s.py", "POST", 1000 }; vector v_epts_info {}; @@ -86,6 +98,7 @@ int main(int argc, char** argv) { honest_requests.begin(), honest_requests.end(), std::back_inserter(v_epts_info), ext_v_epts_info ); + diag("Configuring RESTAPI endpoints..."); int ept_conf_res = configure_endpoints(admin, script_base_path, v_epts_info, dummy_ept, true); if (ept_conf_res) { diag("Endpoint configuration failed. Skipping endpoint testing..."); @@ -94,6 +107,7 @@ int main(int argc, char** argv) { std::vector mysql_connections {}; + diag("Establishing %d baseline connections to ProxySQL...", NUM_CONNECTIONS); for (int i = 0; i < NUM_CONNECTIONS; i++) { MYSQL* proxy = mysql_init(NULL); if (!mysql_real_connect(proxy, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { @@ -101,7 +115,9 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } mysql_connections.push_back(proxy); + if ((i + 1) % 200 == 0) diag(" Established %d/%d connections...", i + 1, NUM_CONNECTIONS); } + diag("Baseline connections established."); typedef std::chrono::high_resolution_clock hrc; const uint64_t test_duration = 10000; @@ -111,6 +127,7 @@ int main(int argc, char** argv) { uint64_t conn_count = 0; uint64_t mysql_fails = 0; + diag("Starting simultaneous RESTAPI requests and connection churn (Duration: %ldms)...", test_duration); { std::thread create_conns([&]() -> int { std::chrono::nanoseconds duration; @@ -120,7 +137,7 @@ int main(int argc, char** argv) { start = hrc::now(); while (true) { - if (mysql_fails > (conn_count * FAILURE_RATE) / 100) { + if (conn_count > 0 && mysql_fails > (conn_count * FAILURE_RATE) / 100) { diag("Too many mysql failures in connection creation, considering test as failed..."); conn_creation_res = EXIT_FAILURE; return EXIT_FAILURE; @@ -129,17 +146,17 @@ int main(int argc, char** argv) { MYSQL* proxy = mysql_init(NULL); if (!mysql_real_connect(proxy, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { - fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy)); - mysql_fails += 1; - } - - int rc = mysql_query(proxy, "DO 1"); - if (rc) { - diag("mysql_errno: '%d', mysql_error: '%s'", mysql_errno(proxy), mysql_error(proxy)); + diag("Churn connection failed: %s", mysql_error(proxy)); mysql_fails += 1; + } else { + int rc = mysql_query(proxy, "DO 1"); + if (rc) { + diag("Churn query failed: mysql_errno: '%d', mysql_error: '%s'", mysql_errno(proxy), mysql_error(proxy)); + mysql_fails += 1; + } + mysql_close(proxy); } - mysql_close(proxy); end = hrc::now(); duration = end - start; @@ -160,6 +177,7 @@ int main(int argc, char** argv) { start = hrc::now(); + int api_request_count = 0; while (true) { int rc = 0; @@ -191,10 +209,13 @@ int main(int argc, char** argv) { } if (curl_err == 7) { - diag("Operation over endpoint failed with 'CURLE_COULDNT_CONNECT'. Aborting..."); + diag("Operation over endpoint %s failed with 'CURLE_COULDNT_CONNECT'. Aborting...", ept.c_str()); + rc = 1; break; } + api_request_count++; } + if (rc) break; } end = hrc::now(); @@ -205,7 +226,9 @@ int main(int argc, char** argv) { } } + diag("Joining churn thread and cleaning up..."); create_conns.join(); + diag("Churn thread finished. Total API requests: %d, Total churn conns: %ld, Failures: %ld", api_request_count, conn_count, mysql_fails); ok( conn_creation_res == EXIT_SUCCESS,