// Tests for the #6 ObjectMapper migration — verifies that the JSON envelopes // produced by JsonErrorHandler / Hub::buildEntityEvent / Hub::buildPresenceMsg // escape special characters instead of emitting raw text. The previous // hand-rolled concatenations broke when given a `"`/`\\`/control-char string. // Avoid pulling Hub.hpp here — it includes oatpp-websocket, which is a // separate optional dependency not necessarily on the test target's include // path. The escaping behaviour we care about is purely a property of // ObjectMapper round-tripping the InternalDto types, so we exercise the // DTOs directly. #include "oatpp-authkit/handler/JsonErrorHandler.hpp" #include "oatpp-authkit/dto/InternalDto.hpp" #include "oatpp/parser/json/mapping/ObjectMapper.hpp" #include "oatpp/web/protocol/http/Http.hpp" #include #include #include namespace { int g_failures = 0; #define REQUIRE(expr) do { \ if (!(expr)) { \ std::fprintf(stderr, "FAIL %s:%d %s\n", __FILE__, __LINE__, #expr); \ ++g_failures; \ } \ } while (0) void test_presence_dto_round_trips_special_chars() { auto m = oatpp::parser::json::mapping::ObjectMapper::createShared(); auto dto = oatpp_authkit::dto::WsPresenceUpdateDto::createShared(); dto->type = oatpp::String("presence_update"); dto->booking_id = oatpp::String("id-with-\"-quote"); dto->users = {}; dto->users->push_back(oatpp::String("al\"ice")); dto->users->push_back(oatpp::String("bo\\b")); auto json = m->writeToString(dto); auto rt = m->readFromString>(json); REQUIRE(rt); REQUIRE(std::string(*rt->booking_id) == "id-with-\"-quote"); REQUIRE(rt->users->size() == 2); auto it = rt->users->begin(); REQUIRE(std::string(**it++) == "al\"ice"); REQUIRE(std::string(**it) == "bo\\b"); } void test_entity_event_dto_round_trip() { auto m = oatpp::parser::json::mapping::ObjectMapper::createShared(); auto dto = oatpp_authkit::dto::WsEntityEventDto::createShared(); dto->type = oatpp::String("booking_updated"); dto->id = oatpp::String("id-with-\"-and-\\"); auto json = m->writeToString(dto); auto rt = m->readFromString>(json); REQUIRE(rt); REQUIRE(std::string(*rt->id) == "id-with-\"-and-\\"); } void test_client_msg_dto_rejects_malformed() { auto m = oatpp::parser::json::mapping::ObjectMapper::createShared(); bool threw = false; try { m->readFromString>( oatpp::String("{not json")); } catch (...) { threw = true; } REQUIRE(threw); } void test_json_error_dto_round_trip() { auto m = oatpp::parser::json::mapping::ObjectMapper::createShared(); auto dto = oatpp_authkit::dto::JsonErrorDto::createShared(); dto->status = oatpp::String("I'm a \"teapot\""); dto->code = 418; dto->message = oatpp::String("brew\nfailure"); auto json = m->writeToString(dto); auto rt = m->readFromString>(json); REQUIRE(rt); REQUIRE(std::string(*rt->status) == "I'm a \"teapot\""); REQUIRE(rt->code == 418); REQUIRE(std::string(*rt->message) == "brew\nfailure"); } } // namespace int main() { test_presence_dto_round_trips_special_chars(); test_entity_event_dto_round_trip(); test_client_msg_dto_rejects_malformed(); test_json_error_dto_round_trip(); std::printf("%s (%d failures)\n", g_failures ? "FAIL" : "OK", g_failures); return g_failures ? 1 : 0; }