From 6c22e89d1b62fc4c73fab24a9a406cb3cc2ad0b4 Mon Sep 17 00:00:00 2001 From: Bhumika-1432006 Date: Fri, 16 Oct 2026 20:35:18 +0530 Subject: [PATCH] test(bind): add behavioral tests for cwist_bind_write_error_response (#342) cwist_bind_write_error_response has no direct test (issue #342 lists it under "cwist_bind_write_error_response -- an error path with no direct test"). The existing test_bind.c exercises it only indirectly through the cwist_app_req_bind_json_or_400 convenience wrapper. This adds tests/test_bind_write_error_response.c with four cases: - single error: status_code == CWIST_HTTP_BAD_REQUEST, Content-Type is application/json, body contains "success":false and the field/message from the error struct. - multiple errors: both field names appear in the body. - zero errors: status is still 400, body has empty errors array. - null res: safe no-op (no crash). The test is wired into make test via a new Makefile target. --- Makefile | 5 ++ tests/test_bind_write_error_response.c | 114 +++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 tests/test_bind_write_error_response.c diff --git a/Makefile b/Makefile index de5bc71d..703dd92d 100644 --- a/Makefile +++ b/Makefile @@ -959,6 +959,7 @@ TEST_TARGETS = test_worker_affinity \ test_log \ nuke_missing_user_test \ test_bind \ + test_bind_write_error_response \ test_metrics \ test_access_log \ test_rate_limit \ @@ -1358,6 +1359,10 @@ test_bind: $(LIB_NAME) tests/test_bind.c $(CC) $(CFLAGS) -o test_bind tests/test_bind.c $(LIB_NAME) $(LIBS) ./test_bind +test_bind_write_error_response: $(LIB_NAME) tests/test_bind_write_error_response.c + $(CC) $(CFLAGS) -o test_bind_write_error_response tests/test_bind_write_error_response.c $(LIB_NAME) $(LIBS) + ./test_bind_write_error_response + test_metrics: $(LIB_NAME) tests/test_metrics.c $(CC) $(CFLAGS) -o test_metrics tests/test_metrics.c $(LIB_NAME) $(LIBS) ./test_metrics diff --git a/tests/test_bind_write_error_response.c b/tests/test_bind_write_error_response.c new file mode 100644 index 00000000..49726a5e --- /dev/null +++ b/tests/test_bind_write_error_response.c @@ -0,0 +1,114 @@ +/** + * @file test_bind_write_error_response.c + * @brief Behavioral tests for cwist_bind_write_error_response (issue #342). + * + * cwist_bind_write_error_response is the lowest-level path for turning a + * failed cwist_bind_result_t into a 400 response. It is tested here + * directly -- not via the cwist_app_req_bind_json_or_400 convenience wrapper + * that test_bind.c already covers -- so the function's own contract is + * verified: + * + * - status_code is CWIST_HTTP_BAD_REQUEST (400). + * - Content-Type header is application/json. + * - Body is a JSON object with "success":false and an "errors" array + * containing one entry per cwist_bind_error_t in the result. + * - NULL result or zero error_count produces {"success":false,"errors":[]}. + * - NULL res is a safe no-op. + */ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include + +/* Build a result with n synthetic errors. */ +static cwist_bind_result_t make_result(int n) { + cwist_bind_result_t r; + memset(&r, 0, sizeof(r)); + r.ok = false; + r.error_count = (size_t)n; + for (int i = 0; i < n; i++) { + snprintf(r.errors[i].key, CWIST_BIND_KEY_MAX, "field%d", i); + snprintf(r.errors[i].message, CWIST_BIND_MSG_MAX, "error for field%d", i); + } + return r; +} + +/* One error: status, content-type, and body shape. */ +static void test_single_error(void) { + printf("test_bind_write_error_response single error...\n"); + + cwist_http_response *res = cwist_http_response_create(); + cwist_bind_result_t result = make_result(1); + cwist_bind_write_error_response(res, &result); + + assert(res->status_code == CWIST_HTTP_BAD_REQUEST); + + const char *ct = cwist_http_header_get(res->headers, "Content-Type"); + assert(ct != NULL); + assert(strstr(ct, "application/json") != NULL); + + const char *body = res->body ? res->body->data : ""; + assert(strstr(body, "\"success\"") != NULL); + assert(strstr(body, "false") != NULL); + assert(strstr(body, "\"errors\"") != NULL); + assert(strstr(body, "\"field0\"") != NULL); + assert(strstr(body, "\"error for field0\"") != NULL); + + cwist_http_response_destroy(res); + printf(" OK\n"); +} + +/* Two errors: both field names appear in the body. */ +static void test_multiple_errors(void) { + printf("test_bind_write_error_response multiple errors...\n"); + + cwist_http_response *res = cwist_http_response_create(); + cwist_bind_result_t result = make_result(2); + cwist_bind_write_error_response(res, &result); + + assert(res->status_code == CWIST_HTTP_BAD_REQUEST); + + const char *body = res->body ? res->body->data : ""; + assert(strstr(body, "\"field0\"") != NULL); + assert(strstr(body, "\"field1\"") != NULL); + + cwist_http_response_destroy(res); + printf(" OK\n"); +} + +/* Zero errors: body has empty array, status is still 400. */ +static void test_zero_errors(void) { + printf("test_bind_write_error_response zero errors...\n"); + + cwist_http_response *res = cwist_http_response_create(); + cwist_bind_result_t result = make_result(0); + cwist_bind_write_error_response(res, &result); + + assert(res->status_code == CWIST_HTTP_BAD_REQUEST); + + const char *body = res->body ? res->body->data : ""; + assert(strstr(body, "\"success\"") != NULL); + assert(strstr(body, "\"errors\"") != NULL); + + cwist_http_response_destroy(res); + printf(" OK\n"); +} + +/* NULL res: must not crash. */ +static void test_null_response(void) { + printf("test_bind_write_error_response null res...\n"); + cwist_bind_result_t result = make_result(1); + cwist_bind_write_error_response(NULL, &result); + printf(" OK\n"); +} + +int main(void) { + test_single_error(); + test_multiple_errors(); + test_zero_errors(); + test_null_response(); + printf("All test_bind_write_error_response tests passed.\n"); + return 0; +}