Mercurial
view mrjunejune/test/integration_test.c @ 186:8cf4ec5e2191 hg-web
Fixed merge conflict.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Fri, 23 Jan 2026 22:38:59 -0800 |
| parents | f3084bca7317 |
| children |
line wrap: on
line source
#include "mrjunejune/test/test.h" // Test case structure typedef struct { const char *path; int expected_status; const char *expected_content; // Loaded from file char *expected_file_path; // Path to expected snapshot file char *actual_response; size_t response_len; } TestCase; void debug_diff(const char *expected, const char *actual) { printf("\n--- DIFF (Expected vs Actual) ---\n"); // Create copies to use with strtok (strtok modifies the string) char *exp_copy = strdup(expected); char *act_copy = strdup(actual); char *exp_line = strtok(exp_copy, "\n"); char *act_line = strtok(act_copy, "\n"); int line_num = 1; while (exp_line != NULL || act_line != NULL) { if (exp_line && act_line && strcmp(exp_line, act_line) == 0) { // Lines match - optional: print nothing or a dot } else { printf("Line %d mismatch:\n", line_num); printf(" EXP: [%s]\n", exp_line ? exp_line : "(end of string)"); printf(" ACT: [%s]\n", act_line ? act_line : "(end of string)"); printf(" -----------------------------------\n"); } exp_line = strtok(NULL, "\n"); act_line = strtok(NULL, "\n"); line_num++; } free(exp_copy); free(act_copy); } // Helper: Convert URL path to filename // "/" -> "root.snapshot" // "/index.html" -> "index.html.snapshot" // "/api/users" -> "api_users.snapshot" void path_to_filename(const char *path, char *filename, size_t max_len) { if (strcmp(path, "/") == 0) { snprintf(filename, max_len, "root.snapshot"); return; } // Remove leading slash and convert remaining slashes to underscores const char *p = path; if (*p == '/') { p++; } char *out = filename; size_t remaining = max_len - 1; while (*p && remaining > 0) { if (*p == '/') { *out++ = '_'; remaining--; } else { *out++ = *p; remaining--; } p++; } // Add .snapshot extension snprintf(out, remaining, ".snapshot"); } // Helper: Read file contents into buffer char* read_file(const char *filepath, size_t *size_out) { FILE *f = fopen(filepath, "rb"); if (!f) { return NULL; } fseek(f, 0, SEEK_END); long fsize = ftell(f); fseek(f, 0, SEEK_SET); char *buffer = malloc(fsize + 1); if (!buffer) { fclose(f); return NULL; } size_t read_size = fread(buffer, 1, fsize, f); buffer[read_size] = '\0'; fclose(f); if (size_out) { *size_out = read_size; } return buffer; } // Helper: Load expected content for a test case int load_expected_content(TestCase *test) { if (!test->expected_file_path) { return -1; } size_t size; test->expected_content = read_file(test->expected_file_path, &size); if (!test->expected_content) { return -1; } return 0; } int execute_test_case(TestCase *test, pid_t server_pid) { printf(" Testing: GET %s (expecting %d)\n", test->path, test->expected_status); int32 max_url_length = 1024*3; char *url = malloc(sizeof(char)*max_url_length); snprintf(url, max_url_length, "%s%s", TEST_URL, test->path); Seobeo_Client_Request *p_req = Seobeo_Client_Request_Create(url); if (!p_req) { printf("Can't create requests"); return -1; } Seobeo_Client_Response *p_resp = Seobeo_Client_Request_Execute(p_req); if (!p_resp) { printf("No response"); return -1; } if (p_resp->status_code != test->expected_status) { printf(" ✗ Status mismatch: expected %d, got %d\n", test->expected_status, p_resp->status_code); Seobeo_Client_Response_Destroy(p_resp); Seobeo_Client_Request_Destroy(p_req); return -1; } printf(" ✓ Status code: %d\n", p_resp->status_code); if (p_resp->status_code == 200) { if (!test->expected_content) { printf(" ✗ No expected snapshot found: %s\n", test->expected_file_path); printf(" → Run: bazel run //mrjunejune:create_snapshots\n"); Seobeo_Client_Response_Destroy(p_resp); Seobeo_Client_Request_Destroy(p_req); return -1; } if (strcmp(p_resp->body, test->expected_content) != 0) { printf(" ✗ Response does not match expected snapshot\n"); printf(" Expected file: %s\n", test->expected_file_path); debug_diff(p_resp->body, test->expected_content); Seobeo_Client_Response_Destroy(p_resp); Seobeo_Client_Request_Destroy(p_req); return -1; } printf(" ✓ Response matches snapshot (%zu bytes)\n", p_resp->body_length); } Seobeo_Client_Response_Destroy(p_resp); Seobeo_Client_Request_Destroy(p_req); return 0; } int send_post_file(Seobeo_Handle *p_req, const char *path, const char *file_data, size_t file_size) { char request_buffer[8192]; int header_len = snprintf( request_buffer, sizeof(request_buffer), "POST %s HTTP/1.1\r\n" "Host: %s\r\n" "Content-Type: application/octet-stream\r\n" "Content-Length: %zu\r\n" "Connection: close\r\n" "\r\n", path, TEST_HOST, file_size ); if (header_len < 0 || header_len >= sizeof(request_buffer)) { fprintf(stderr, "Request header too large\n"); return -1; } // Send headers Seobeo_Handle_Queue(p_req, (uint8*)request_buffer, (uint32)header_len); // Send file data in chunks if needed size_t remaining = file_size; const char *ptr = file_data; while (remaining > 0) { size_t chunk_size = remaining > 4096 ? 4096 : remaining; Seobeo_Handle_Queue(p_req, (uint8*)ptr, (uint32)chunk_size); ptr += chunk_size; remaining -= chunk_size; } return Seobeo_Handle_Flush(p_req); } char* extract_json_field(const char *json, const char *field, char *buffer, size_t buffer_size) { char search_pattern[256]; snprintf(search_pattern, sizeof(search_pattern), "\"%s\":\"", field); const char *start = strstr(json, search_pattern); if (!start) { return NULL; } start += strlen(search_pattern); const char *end = strchr(start, '"'); if (!end) { return NULL; } size_t len = end - start; if (len >= buffer_size) { len = buffer_size - 1; } memcpy(buffer, start, len); buffer[len] = '\0'; return buffer; } int test_file_conversion(const char *endpoint, const char *test_file_path, const char *expected_format, pid_t server_pid) { printf(" Testing: POST %s\n", endpoint); // Read test file size_t file_size; char *file_data = read_file(test_file_path, &file_size); if (!file_data) { printf(" ✗ Failed to read test file: %s\n", test_file_path); return -1; } char url[1024]; snprintf(url, sizeof(url), "%s%s", TEST_URL, endpoint); Seobeo_Client_Request *p_req = Seobeo_Client_Request_Create(url); Seobeo_Client_Request_Set_Method(p_req, "POST"); Seobeo_Client_Request_Set_Body(p_req, (uint8*)file_data, (uint32)file_size); Seobeo_Client_Request_Add_Header_Array(p_req, "Content-Type: application/octet-stream"); Seobeo_Client_Response *p_resp = Seobeo_Client_Request_Execute(p_req); free(file_data); if (!p_resp || p_resp->status_code != 200) { printf(" ✗ Conversion failed with status: %d, %s\n", p_resp ? p_resp->status_code : 0, p_resp->body); if (p_resp) Seobeo_Client_Response_Destroy(p_resp); Seobeo_Client_Request_Destroy(p_req); return -1; } // Extract download URL from JSON body char download_url_path[512]; if (!extract_json_field((char*)p_resp->body, "download_url", download_url_path, sizeof(download_url_path))) { printf(" ✗ Failed to extract download_url\n"); Seobeo_Client_Response_Destroy(p_resp); Seobeo_Client_Request_Destroy(p_req); return -1; } printf(" ✓ Conversion succeeded. Download URL: %s\n", download_url_path); Seobeo_Client_Response_Destroy(p_resp); Seobeo_Client_Request_Destroy(p_req); usleep(100000); printf(" → Testing download: GET %s\n", download_url_path); char full_download_url[1024]; snprintf(full_download_url, sizeof(full_download_url), "%s%s", TEST_URL, download_url_path); p_req = Seobeo_Client_Request_Create(full_download_url); p_resp = Seobeo_Client_Request_Execute(p_req); if (!p_resp || p_resp->status_code != 200) { printf(" ✗ Download failed\n"); printf("%s\n", p_resp->body); if (p_resp) Seobeo_Client_Response_Destroy(p_resp); Seobeo_Client_Request_Destroy(p_req); return -1; } printf(" ✓ Downloaded converted file (%u bytes)\n", p_resp->body_length); Seobeo_Client_Response_Destroy(p_resp); Seobeo_Client_Request_Destroy(p_req); return 0; } // Helper: Initialize test case with snapshot file void init_test_case(TestCase *test) { char filename[256]; path_to_filename(test->path, filename, sizeof(filename)); test->expected_file_path = malloc(512); snprintf(test->expected_file_path, 512, "%s/%s", SNAPSHOT_DIR, filename); // Load expected content from snapshot file load_expected_content(test); } // Helper: Cleanup test case void cleanup_test_case(TestCase *test) { if (test->actual_response) { free(test->actual_response); } if (test->expected_file_path) { free(test->expected_file_path); } if (test->expected_content) { free((void*)test->expected_content); } } // Main integration test int test_server_client_integration(const char *server_binary) { printf("=== Server-p_req Integration Test ===\n"); printf("MODE: Verifying Against Snapshots\n\n"); char cwd[1024]; if (getcwd(cwd, sizeof(cwd)) != NULL) printf("Working directory: %s\n", cwd); if (access(server_binary, X_OK) != 0) { printf("Server binary not found: %s\n", server_binary); perror("access"); return -1; } printf("Server binary: %s\n", server_binary); printf("Snapshot directory: %s\n\n", SNAPSHOT_DIR); pid_t server_pid = start_test_server(server_binary); if (server_pid < 0) return -1; int failed_tests = 0; int passed_tests = 0; TestCase success_tests[] = { {"/", 200, NULL, NULL, NULL, 0}, {"/resume", 200, NULL, NULL, NULL, 0}, {"/tools", 200, NULL, NULL, NULL, 0}, {"/tools/markdown_to_html", 200, NULL, NULL, NULL, 0}, {"/tools/file_converter", 200, NULL, NULL, NULL, 0}, {"/talk", 200, NULL, NULL, NULL, 0}, }; int num_success_tests = sizeof(success_tests) / sizeof(success_tests[0]); TestCase redirect_tests[] = { {"/index.html", 301, NULL, NULL, NULL, 0}, {"/resume/index.html", 301, NULL, NULL, NULL, 0}, {"/tools/index.html", 301, NULL, NULL, NULL, 0}, {"/tools/markdown_to_html/index.html", 301, NULL, NULL, NULL, 0}, {"/tools/file_converter/index.html", 301, NULL, NULL, NULL, 0}, }; int num_redirect_tests = sizeof(redirect_tests) / sizeof(redirect_tests[0]); TestCase failure_tests[] = { {"/nonexistent", 404, NULL, NULL, NULL, 0}, {"/does/not/exist", 404, NULL, NULL, NULL, 0}, {"/missing.html", 404, NULL, NULL, NULL, 0}, }; int num_failure_tests = sizeof(failure_tests) / sizeof(failure_tests[0]); for (int i = 0; i < num_success_tests; i++) init_test_case(&success_tests[i]); for (int i = 0; i < num_redirect_tests; i++) init_test_case(&redirect_tests[i]); for (int i = 0; i < num_failure_tests; i++) init_test_case(&failure_tests[i]); printf("Running tests for paths that should succeed:\n"); for (int i = 0; i < num_success_tests; i++) { if (execute_test_case(&success_tests[i], server_pid) == 0) passed_tests++; else failed_tests++; } printf("\n"); printf("Running tests for paths that should redirect:\n"); for (int i = 0; i < num_redirect_tests; i++) { if (execute_test_case(&redirect_tests[i], server_pid) == 0) passed_tests++; else failed_tests++; } printf("\n"); printf("Running tests for paths that should fail:\n"); for (int i = 0; i < num_failure_tests; i++) { if (execute_test_case(&failure_tests[i], server_pid) == 0) passed_tests++; else failed_tests++; } printf("\n"); printf("Running tests for POST conversion endpoints:\n"); if (test_file_conversion("/api/convert/image-to-webp", "mrjunejune/test/shiba.webp", "image/webp", server_pid) == 0) passed_tests++; else failed_tests++; printf("\n"); if (test_file_conversion("/api/convert/video-to-mp4", "mrjunejune/test/test_avi.avi", "video/mp4", server_pid) == 0) passed_tests++; else failed_tests++; for (int i = 0; i < num_success_tests; i++) cleanup_test_case(&success_tests[i]); for (int i = 0; i < num_redirect_tests; i++) cleanup_test_case(&redirect_tests[i]); for (int i = 0; i < num_failure_tests; i++) cleanup_test_case(&failure_tests[i]); stop_test_server(server_pid); printf("\n=== Test Summary ===\n"); printf("Passed: %d\n", passed_tests); printf("Failed: %d\n", failed_tests); return (failed_tests == 0) ? 0 : -1; } int main(int argc, char *argv[]) { printf("=== Seobeo Integration Tests ===\n\n"); const char *server_binary = "./mrjunejune_server"; if (argc > 1) server_binary = argv[1]; int result = test_server_client_integration(server_binary); if (result == 0) printf("\n✓ All tests passed!\n"); else printf("\n✗ Some tests failed\n"); return result; }