#include "goldfish.hpp" #include #include #include #include #include using namespace goldfish; using namespace std; class TestRequest : public IRequest { private: HttpMethod method_; string_view path_; string_view query_; string_view body_; public: TestRequest(HttpMethod method, string_view path, string_view query = "", string_view body = "") : method_(method), path_(path), query_(query), body_(body) {} HttpMethod method() const override { return method_; } string_view path() const override { return path_; } string_view query() const override { return query_; } string_view body() const override { return body_; } string_view get_header(string_view) const override { return {}; } }; class TestResponse : public IResponse { private: int status_ = 200; string body_; string content_type_ = "text/plain"; public: void set_status(int status) override { status_ = status; } void set_header(string_view, string_view) override {} void set_body(string_view body) override { body_ = string(body); } void set_content_type(string_view content_type) override { content_type_ = string(content_type); } int status() const { return status_; } const string& body() const { return body_; } }; void test_basic_routing() { Router router; bool handler_called = false; router.get("/test", [&](IRequest&, IResponse& res, Params) { handler_called = true; res.set_body("success"); }); TestRequest req(HttpMethod::GET, "/test"); TestResponse res; assert(router.handle(req, res)); assert(handler_called); assert(res.body() == "success"); cout << "✓ Basic routing test passed\n"; } void test_parameter_extraction() { Router router; string extracted_id; router.get("/users/:id", [&](IRequest&, IResponse& res, Params params) { extracted_id = string(params[0]); res.set_body("user found"); }); TestRequest req(HttpMethod::GET, "/users/123"); TestResponse res; assert(router.handle(req, res)); assert(extracted_id == "123"); cout << "✓ Parameter extraction test passed\n"; } void test_multiple_parameters() { Router router; string user_id, post_id; router.get("/users/:userId/posts/:postId", [&](IRequest&, IResponse&, Params params) { user_id = string(params[0]); post_id = string(params[1]); }); TestRequest req(HttpMethod::GET, "/users/456/posts/789"); TestResponse res; assert(router.handle(req, res)); assert(user_id == "456"); assert(post_id == "789"); cout << "✓ Multiple parameters test passed\n"; } void test_method_routing() { Router router; string method_called; router.get("/api", [&](IRequest&, IResponse&, Params) { method_called = "GET"; }); router.post("/api", [&](IRequest&, IResponse&, Params) { method_called = "POST"; }); TestRequest get_req(HttpMethod::GET, "/api"); TestRequest post_req(HttpMethod::POST, "/api"); TestResponse res; assert(router.handle(get_req, res)); assert(method_called == "GET"); assert(router.handle(post_req, res)); assert(method_called == "POST"); cout << "✓ Method routing test passed\n"; } void test_no_match() { Router router; router.get("/existing", [](IRequest&, IResponse&, Params) {}); TestRequest req(HttpMethod::GET, "/nonexistent"); TestResponse res; assert(!router.handle(req, res)); cout << "✓ No match test passed\n"; } void test_mixed_static_and_dynamic() { Router router; string result; router.get("/api/users", [&](IRequest&, IResponse&, Params) { result = "static"; }); router.get("/api/:resource", [&](IRequest&, IResponse&, Params params) { result = "dynamic:" + string(params[0]); }); TestRequest static_req(HttpMethod::GET, "/api/users"); TestRequest dynamic_req(HttpMethod::GET, "/api/posts"); TestResponse res; assert(router.handle(static_req, res)); assert(result == "static"); assert(router.handle(dynamic_req, res)); assert(result == "dynamic:posts"); cout << "✓ Mixed static/dynamic routing test passed\n"; } void benchmark_routing() { cout << "\nBenchmarking router performance...\n"; Router router; vector test_paths; mt19937 rng(42); uniform_int_distribution<> path_len(2, 6); uniform_int_distribution<> segment_len(3, 12); uniform_int_distribution<> param_chance(1, 4); const int num_routes = 10000; const int num_lookups = 1000000; // Generate random routes for (int i = 0; i < num_routes; ++i) { string path = "/"; int segments = path_len(rng); for (int j = 0; j < segments; ++j) { if (j > 0) path += "/"; if (param_chance(rng) == 1) { path += ":param" + std::to_string(j); } else { int len = segment_len(rng); for (int k = 0; k < len; ++k) { path += 'a' + (rng() % 26); } } } router.get(path, [](IRequest&, IResponse&, Params) {}); test_paths.push_back(path); } // Create lookup paths with realistic hit/miss ratio vector lookup_paths; uniform_int_distribution<> hit_chance(1, 3); for (int i = 0; i < num_lookups; ++i) { if (hit_chance(rng) == 1 && !test_paths.empty()) { string path = test_paths[rng() % test_paths.size()]; size_t pos = 0; while ((pos = path.find(":param", pos)) != string::npos) { size_t end = path.find("/", pos); if (end == string::npos) end = path.length(); path.replace(pos, end - pos, std::to_string(rng() % 1000)); pos += 3; } lookup_paths.push_back(path); } else { string path = "/missing/" + std::to_string(rng() % 10000); lookup_paths.push_back(path); } } // Benchmark lookups int successful_routes = 0; auto start = std::chrono::high_resolution_clock::now(); for (const auto& path : lookup_paths) { TestRequest req(HttpMethod::GET, path); TestResponse res; if (router.handle(req, res)) { successful_routes++; } } auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); double avg_lookup_time = double(duration.count()) / num_lookups; double lookups_per_second = 1000000.0 / avg_lookup_time; cout << "Routes registered: " << num_routes << "\n"; cout << "Lookups performed: " << num_lookups << "\n"; cout << "Successful routes: " << successful_routes << " (" << (100.0 * successful_routes / num_lookups) << "%)\n"; cout << "Total time: " << duration.count() << " μs\n"; cout << "Average lookup: " << avg_lookup_time << " μs\n"; cout << "Lookups/second: " << int(lookups_per_second) << "\n"; } int main() { cout << "Running Goldfish Router Tests...\n\n"; test_basic_routing(); test_parameter_extraction(); test_multiple_parameters(); test_method_routing(); test_no_match(); test_mixed_static_and_dynamic(); cout << "\n✅ All tests passed!\n"; benchmark_routing(); return 0; }