test_json_parse.cpp 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. #include <cassert>
  2. #include <cstring>
  3. #include <iostream>
  4. #include <string>
  5. #include <nlohmann/json.hpp>
  6. #include "json_parse.hpp"
  7. using nlohmann::json;
  8. using smartbotic::db::parse_to_nlohmann;
  9. namespace {
  10. void check(bool cond, const char* msg) {
  11. if (!cond) {
  12. std::cerr << "FAIL: " << msg << "\n";
  13. std::abort();
  14. }
  15. }
  16. void test_empty_object() {
  17. auto j = parse_to_nlohmann("{}");
  18. check(j.is_object() && j.empty(), "empty object");
  19. }
  20. void test_empty_array() {
  21. auto j = parse_to_nlohmann("[]");
  22. check(j.is_array() && j.empty(), "empty array");
  23. }
  24. void test_primitives() {
  25. check(parse_to_nlohmann("true").get<bool>() == true, "true");
  26. check(parse_to_nlohmann("false").get<bool>() == false, "false");
  27. check(parse_to_nlohmann("null").is_null(), "null");
  28. check(parse_to_nlohmann("42").get<int64_t>() == 42, "int");
  29. check(parse_to_nlohmann("-7").get<int64_t>() == -7, "negative int");
  30. check(parse_to_nlohmann("3.14").get<double>() == 3.14, "double");
  31. check(parse_to_nlohmann("\"hello\"").get<std::string>() == "hello", "string");
  32. }
  33. void test_nested() {
  34. std::string s = R"({"a":1,"b":[2,3,{"c":"x"}],"d":null,"e":true})";
  35. auto y = parse_to_nlohmann(s);
  36. auto n = json::parse(s);
  37. check(y == n, "nested equality with nlohmann");
  38. }
  39. void test_unicode_string() {
  40. std::string s = R"({"name":"Árvíztűrő tükörfúrógép","emoji":"🚀"})";
  41. auto y = parse_to_nlohmann(s);
  42. auto n = json::parse(s);
  43. check(y == n, "unicode equality");
  44. }
  45. void test_large_int() {
  46. auto j = parse_to_nlohmann("9223372036854775807");
  47. check(j.get<int64_t>() == 9223372036854775807LL, "int64 max");
  48. }
  49. void test_string_view_overload() {
  50. std::string s = "{\"k\":1}";
  51. auto j = parse_to_nlohmann(std::string_view(s));
  52. check(j["k"].get<int>() == 1, "string_view overload");
  53. }
  54. void test_invalid_json_throws() {
  55. // Contract: parse_to_nlohmann throws nlohmann::json::parse_error so the
  56. // 17 downstream callsites that catch (nlohmann::json::parse_error&) keep
  57. // working unchanged. Catching the specific type guards against a future
  58. // refactor silently changing the throw type.
  59. bool threw = false;
  60. try {
  61. (void)parse_to_nlohmann("{not json");
  62. } catch (const nlohmann::json::parse_error&) {
  63. threw = true;
  64. }
  65. check(threw, "invalid json throws parse_error");
  66. }
  67. void test_empty_string_throws_parse_error() {
  68. // After the v1.10 fix, empty input is no longer pre-rejected with a
  69. // bespoke "empty input" message — it falls through to yyjson, which
  70. // emits a positional "unexpected end of data" error wrapped in
  71. // parse_error. Verifies the message-path consistency the reviewer asked
  72. // for so that operators see the same error shape for empty vs. malformed.
  73. bool threw = false;
  74. try {
  75. (void)parse_to_nlohmann("");
  76. } catch (const nlohmann::json::parse_error&) {
  77. threw = true;
  78. }
  79. check(threw, "empty string throws parse_error");
  80. }
  81. void test_corpus_equivalence() {
  82. // Sample of doc shapes the production service sees.
  83. const char* corpus[] = {
  84. R"({"_id":"abc","name":"x","tags":["a","b"],"count":0})",
  85. R"({"_id":"xyz","_vector":[0.1,-0.2,0.3,0.4],"meta":{"k":"v"}})",
  86. R"({"_id":"e1","_event":"call.ended","at":1731628800123})",
  87. R"([])",
  88. R"([1,2,3,4,5,6,7,8,9,10])",
  89. R"({"deeply":{"nested":{"object":{"is":{"fine":true}}}}})",
  90. };
  91. for (const char* s : corpus) {
  92. check(parse_to_nlohmann(s) == json::parse(s), s);
  93. }
  94. }
  95. } // namespace
  96. int main() {
  97. test_empty_object();
  98. test_empty_array();
  99. test_primitives();
  100. test_nested();
  101. test_unicode_string();
  102. test_large_int();
  103. test_string_view_overload();
  104. test_invalid_json_throws();
  105. test_empty_string_throws_parse_error();
  106. test_corpus_equivalence();
  107. std::cout << "test_json_parse: all passed\n";
  108. return 0;
  109. }