Skip to content

Commit

Permalink
Improvements and fixes for RESTAPI TAP tests #4001
Browse files Browse the repository at this point in the history
- Fix several utility functions and add new ones.
- Improve current tests targeting RESTAPI.
- Add new regression test for issue #4001.
- Add several scripts used for RESTAPI tests rework.
- Fix compilation of several tests after 'wexcevp' interface change.
  • Loading branch information
JavierJF committed Dec 8, 2022
1 parent 99e64bf commit 5b9f679
Show file tree
Hide file tree
Showing 20 changed files with 1,150 additions and 210 deletions.
171 changes: 153 additions & 18 deletions test/tap/tap/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,7 @@ static size_t write_callback(void *data, size_t size, size_t nmemb, void *userp)
}

CURLcode perform_simple_post(
const string& endpoint, const string& post_params,
uint64_t& curl_res_code, string& curl_out_err
const string& endpoint, const string& params, uint64_t& curl_res_code, string& curl_res_data
) {
CURL *curl;
CURLcode res;
Expand All @@ -368,21 +367,18 @@ CURLcode perform_simple_post(
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_params.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, params.c_str());
struct memory response = { 0 };
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);

res = curl_easy_perform(curl);

if(res != CURLE_OK) {
curl_out_err = string { curl_easy_strerror(res) };
curl_res_data = string { curl_easy_strerror(res) };
} else {
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &curl_res_code);

if (curl_res_code != 200) {
curl_out_err = string(response.data, response.size);
}
curl_res_data = string(response.data, response.size);
}

free(response.data);
Expand All @@ -392,23 +388,71 @@ CURLcode perform_simple_post(
return res;
}

int wait_until_enpoint_ready(
string endpoint, string post_params, uint32_t timeout, uint32_t delay
CURLcode perform_simple_get(
const string& endpoint, uint64_t& curl_res_code, string& curl_res_data
) {
CURL *curl;
CURLcode res;

curl_global_init(CURL_GLOBAL_ALL);

curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str());
struct memory response = { 0 };
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);

res = curl_easy_perform(curl);

if(res != CURLE_OK) {
curl_res_data = string { curl_easy_strerror(res) };
} else {
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &curl_res_code);
curl_res_data = string(response.data, response.size);
}

free(response.data);
curl_easy_cleanup(curl);
}

return res;
}

int wait_post_enpoint_ready(string endpoint, string post_params, uint32_t timeout, uint32_t delay) {
double waited = 0;
int res = -1;

while (waited < timeout) {
string curl_str_err {};
string curl_resp_err {};
uint64_t curl_res_code = 0;
int curl_err = perform_simple_post(endpoint, post_params, curl_res_code, curl_str_err);
int curl_res = perform_simple_post(endpoint, post_params, curl_res_code, curl_resp_err);

if (curl_err != CURLE_OK) {
diag(
"'curl_err_code': %d, 'curl_err': '%s', waiting for '%d'ms...",
curl_err, curl_str_err.c_str(), delay
);
waited += static_cast<double>(delay) / 1000;
if (curl_res != CURLE_OK || curl_res_code != 200) {
diag("'curl_res': %d, 'curl_err': '%s', waiting for '%d'ms...", curl_res, curl_resp_err.c_str(), delay);
waited += static_cast<double>(delay);
usleep(delay * 1000);
} else {
res = 0;
break;
}
}

return res;
}

int wait_get_enpoint_ready(string endpoint, uint32_t timeout, uint32_t delay) {
double waited = 0;
int res = -1;

while (waited < timeout) {
string curl_resp_err {};
uint64_t curl_res_code = 0;
int curl_res = perform_simple_get(endpoint, curl_res_code, curl_resp_err);

if (curl_res != CURLE_OK || curl_res_code != 200) {
diag("'curl_res': %d, 'curl_err': '%s', waiting for '%d'ms...", curl_res, curl_resp_err.c_str(), delay);
waited += static_cast<double>(delay);
usleep(delay * 1000);
} else {
res = 0;
Expand Down Expand Up @@ -922,3 +966,94 @@ int wait_for_backend_conns(
return EXIT_SUCCESS;
}
}

string join_path(const string& p1, const string& p2) {
if (p1.back() == '/') {
return p1 + p2;
} else {
return p1 + '/' + p2;
}
}

int check_endpoint_exists(MYSQL* admin, const ept_info_t& ept, bool& exists) {
const string select_query {
"SELECT count(*) FROM restapi_routes WHERE uri='" + ept.name + "' AND method='" + ept.method + "'"
};
MYSQL_QUERY(admin, select_query.c_str());
MYSQL_RES* myres = mysql_store_result(admin);
MYSQL_ROW myrow = mysql_fetch_row(myres);
bool res = EXIT_FAILURE;

if (myrow && myrow[0]) {
int entry_num = std::atoi(myrow[0]);
exists = entry_num != 0;

res = EXIT_SUCCESS;
} else {
diag("Invalid resultset returned from query '%s'", select_query.c_str());

res = EXIT_FAILURE;
}

mysql_free_result(myres);

return res;
}

const char t_restapi_insert[] {
"INSERT INTO restapi_routes (active, timeout_ms, method, uri, script, comment) "
"VALUES (1,%ld,'%s','%s','%s','comm')",
};

const string base_address { "http://localhost:6070/sync/" };

int configure_endpoints(
MYSQL* admin,
const string& script_base_path,
const vector<ept_info_t>& epts_info,
const ept_info_t& dummy_ept,
bool prevent_dups
) {
MYSQL_QUERY(admin, "DELETE FROM restapi_routes");

vector<ept_info_t> _epts_info { epts_info };
_epts_info.push_back(dummy_ept);

for (const ept_info_t& ept : _epts_info) {
string f_exe_name {};
string_format(ept.file, f_exe_name, ept.name.c_str());

const string script_path { join_path(script_base_path, f_exe_name) };
string insert_query {};
string_format(
t_restapi_insert, insert_query, ept.timeout, ept.method.c_str(), ept.name.c_str(), script_path.c_str()
);

bool duplicate_entry = false;
if (check_endpoint_exists(admin, ept, duplicate_entry)) {
return EXIT_FAILURE;
}

if (!(prevent_dups && duplicate_entry)) {
MYSQL_QUERY(admin, insert_query.c_str());
} else {
diag(
"Warning: Test payload trying to insert invalid duplicated entry - uri: '%s', method: '%s'",
ept.name.c_str(), ept.method.c_str()
);
exit(EXIT_FAILURE);
}
}

MYSQL_QUERY(admin, "LOAD RESTAPI TO RUNTIME");

const string full_endpoint { join_path(base_address, dummy_ept.name) };
int endpoint_timeout = wait_post_enpoint_ready(full_endpoint, "{}", 1000, 100);

if (endpoint_timeout) {
diag("Timeout while trying to reach first valid enpoint");
return EXIT_FAILURE;
}

return EXIT_SUCCESS;
}
76 changes: 72 additions & 4 deletions test/tap/tap/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,12 @@ std::vector<mysql_res_row> extract_mysql_rows(MYSQL_RES* my_res);
* @return '0' in case the endpoint became available before the timeout, or
* '-1' in case the timeout expired.
*/
int wait_until_enpoint_ready(
int wait_post_enpoint_ready(
std::string endpoint, std::string post_params, uint32_t timeout, uint32_t delay=100
);

int wait_get_enpoint_ready(std::string endpoint, uint32_t timeout, uint32_t delay=100);

/**
* @brief Perform a simple POST query to the specified endpoint using the supplied
* 'post_params'.
Expand All @@ -129,16 +131,17 @@ int wait_until_enpoint_ready(
* @param curl_out_err A uint64_t reference returning the result code of the
* query in case it has been performed. In case the query couldn't be
* performed, this value is never initialized.
* @param curl_out_err A string reference to collect the error as a string reported
* @param curl_res_err A string reference to collect the error as a string reported
* by 'libcurl' in case of failure.
*
* @return The response code of the query in case of the query.
*/
CURLcode perform_simple_post(
const std::string& endpoint, const std::string& post_params,
uint64_t& curl_res_code, std::string& curl_out_err
const std::string& endpoint, const std::string& params, uint64_t& curl_res_code, std::string& curl_res_data
);

CURLcode perform_simple_get(const std::string& endpoint, uint64_t& curl_res_code, std::string& curl_res_data);

/**
* @brief Generates a random string of the length of the provider 'strSize'
* parameter.
Expand Down Expand Up @@ -363,4 +366,69 @@ int wait_for_backend_conns(
*/
int get_cur_backend_conns(MYSQL* proxy_admin, const std::string& conn_type, uint32_t& found_conn_num);

/**
* @brief Join two string paths. Appends '/' to the first supplied string if doesn't already finish with one.
* @param p1 First part of the path to be joined.
* @param p2 Second string to append to the first path.
* @return A string holding at least one '/' between the two previously supplied strings.
*/
std::string join_path(const std::string& p1, const std::string& p2);

/**
* @brief Holds the required info for the definition of a RESTAPI endpoint.
*/
struct ept_info_t {
std::string name;
std::string file;
std::string method;
uint64_t timeout;
};

/**
* @brief Represents a RESTAPI endpoint request expected to succeed.
*/
struct honest_req_t {
ept_info_t ept_info;
std::vector<std::string> params;
};

/**
* @brief Holds the test payload information for faulty requests.
*/
struct ept_pl_t {
/* @brief Params to be issued in the request against the endpoint */
std::string params;
/* @brief Expected code to be returned by CURL */
uint64_t curl_rc;
/* @brief Expected response output returned by CURL */
uint64_t script_err;
};

/**
* @brief Represents a RESTAPI endpoint request expected to fail.
*/
struct faulty_req_t {
ept_info_t ept_info;
std::vector<ept_pl_t> ept_pls;
};

/**
* @brief Configure the supplied endpoints using the provided information
*
* @param admin Opened connection to ProxySQL admin interface.
* @param script_base_path Common base path for the scripts location.
* @param epts_info Information of the endpoints to be configured.
* @param dummy_ept Dummy endpoint used to check when interface is ready.
* @param prevent_dups Prevent duplicates when inserting the provided info.
*
* @return EXIT_SUCCESS in case of success, EXIT_FAILURE otherwise. Errors are logged.
*/
int configure_endpoints(
MYSQL* admin,
const std::string& script_base_path,
const std::vector<ept_info_t>& epts_info,
const ept_info_t& dummy_ept,
bool prevent_dups = true
);

#endif // #define UTILS_H
3 changes: 3 additions & 0 deletions test/tap/tests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,6 @@ prepare_statement_err3024_libmysql: prepare_statement_err3024-t.cpp $(TAP_LIBDIR

prepare_statement_err3024_async: prepare_statement_err3024-t.cpp $(TAP_LIBDIR)/libtap.a
$(CXX) -DASYNC_API prepare_statement_err3024-t.cpp $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -lpthread -ldl -std=c++11 -ltap $(STATIC_LIBS) -o prepare_statement_err3024_async-t -DGITVERSION=\"$(GIT_VERSION)\"

test_wexecvp_syscall_failures-t: test_wexecvp_syscall_failures-t.cpp $(TAP_LIBDIR)/libtap.a
$(CXX) $^ $(INCLUDEDIRS) $(LDIRS) $(OPT) $(MYLIBS) -std=c++11 -Wl,--wrap=pipe,--wrap=fcntl,--wrap=read,--wrap=poll -lpthread -ldl -ltap $(STATIC_LIBS) -o $@
Loading

0 comments on commit 5b9f679

Please sign in to comment.