// Тесты HTTP-клиента против in-process httpd. #include "doctest/doctest.h" #include #include #include "ayla/httpc.hpp" #include "ayla/httpd.hpp" #include "ayla/platform/platform.hpp" namespace { struct Ctx { fgl::ayla::HttpRequest last; int calls = 0; int status_to_return = 200; }; bool handler(const fgl::ayla::HttpRequest& req, fgl::ayla::HttpResponse& resp, void* ctx) { auto* c = static_cast(ctx); c->last = req; c->calls++; resp.status = c->status_to_return; static const uint8_t kBody[] = "{\"x\":1}"; resp.body = kBody; resp.body_len = sizeof(kBody) - 1; return true; } } // namespace TEST_CASE("httpc: POST с телом и query, статус пробрасывается") { fgl::ayla::HttpServer srv; Ctx ctx; ctx.status_to_return = 202; REQUIRE(srv.start(0, handler, &ctx)); const uint32_t ip = (127u << 24) | 1u; // для лога, не используется fgl::ayla::HttpcRequest req; req.method = "POST"; req.host = "127.0.0.1"; req.port = srv.port(); req.path = "/local_reg.json"; req.query = "dsn=AC000W00REDACTED"; const char body[] = "{\"local_reg\":{\"notify\":1}}"; req.body = reinterpret_cast(body); req.body_len = strlen(body); req.timeout_ms = 3000; fgl::ayla::HttpcResponse resp; REQUIRE(fgl::ayla::httpc_perform(req, &resp)); CHECK(resp.transport_ok); CHECK(resp.status == 202); CHECK(ctx.calls == 1); CHECK(std::string(ctx.last.method) == "POST"); CHECK(std::string(ctx.last.target) == "/local_reg.json"); CHECK(std::string(ctx.last.query) == "dsn=AC000W00REDACTED"); CHECK(ctx.last.body_len == strlen(body)); CHECK(memcmp(ctx.last.body, body, strlen(body)) == 0); srv.stop(); (void)ip; } TEST_CASE("httpc: ошибки транспорта (соединение отвергнуто)") { // Занимаем и освобождаем порт — соединение точно отвергнётся. fgl::ayla::HttpServer tmp; REQUIRE(tmp.start(0, nullptr, nullptr)); const uint16_t port = tmp.port(); tmp.stop(); fgl::plat::sleep_ms(100); fgl::ayla::HttpcRequest req; req.method = "PUT"; req.host = "127.0.0.1"; req.port = port; req.timeout_ms = 2000; fgl::ayla::HttpcResponse resp; bool ok = fgl::ayla::httpc_perform(req, &resp); // Либо connect сразу отклонён (false), либо таймаут — оба варианта ошибки. if (ok) { FAIL("ожидаля отказ соединения"); } CHECK_FALSE(resp.transport_ok); } TEST_CASE("httpc: PUT без тела, ответ 503 c телом (дренаж)") { fgl::ayla::HttpServer srv; Ctx ctx; ctx.status_to_return = 503; REQUIRE(srv.start(0, handler, &ctx)); fgl::ayla::HttpcRequest req; req.method = "PUT"; req.host = "127.0.0.1"; req.port = srv.port(); req.path = "/local_reg.json"; req.body = nullptr; req.body_len = 0; req.timeout_ms = 3000; fgl::ayla::HttpcResponse resp; REQUIRE(fgl::ayla::httpc_perform(req, &resp)); CHECK(resp.status == 503); CHECK(ctx.calls == 1); CHECK(ctx.last.body_len == 0); srv.stop(); }