| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- #include <cassert>
- #include <cstring>
- #include <iostream>
- #include <string>
- #include <nlohmann/json.hpp>
- #include "json_parse.hpp"
- using nlohmann::json;
- using smartbotic::db::parse_to_nlohmann;
- namespace {
- void check(bool cond, const char* msg) {
- if (!cond) {
- std::cerr << "FAIL: " << msg << "\n";
- std::abort();
- }
- }
- void test_empty_object() {
- auto j = parse_to_nlohmann("{}");
- check(j.is_object() && j.empty(), "empty object");
- }
- void test_empty_array() {
- auto j = parse_to_nlohmann("[]");
- check(j.is_array() && j.empty(), "empty array");
- }
- void test_primitives() {
- check(parse_to_nlohmann("true").get<bool>() == true, "true");
- check(parse_to_nlohmann("false").get<bool>() == false, "false");
- check(parse_to_nlohmann("null").is_null(), "null");
- check(parse_to_nlohmann("42").get<int64_t>() == 42, "int");
- check(parse_to_nlohmann("-7").get<int64_t>() == -7, "negative int");
- check(parse_to_nlohmann("3.14").get<double>() == 3.14, "double");
- check(parse_to_nlohmann("\"hello\"").get<std::string>() == "hello", "string");
- }
- void test_nested() {
- std::string s = R"({"a":1,"b":[2,3,{"c":"x"}],"d":null,"e":true})";
- auto y = parse_to_nlohmann(s);
- auto n = json::parse(s);
- check(y == n, "nested equality with nlohmann");
- }
- void test_unicode_string() {
- std::string s = R"({"name":"Árvíztűrő tükörfúrógép","emoji":"🚀"})";
- auto y = parse_to_nlohmann(s);
- auto n = json::parse(s);
- check(y == n, "unicode equality");
- }
- void test_large_int() {
- auto j = parse_to_nlohmann("9223372036854775807");
- check(j.get<int64_t>() == 9223372036854775807LL, "int64 max");
- }
- void test_string_view_overload() {
- std::string s = "{\"k\":1}";
- auto j = parse_to_nlohmann(std::string_view(s));
- check(j["k"].get<int>() == 1, "string_view overload");
- }
- void test_invalid_json_throws() {
- // Contract: parse_to_nlohmann throws nlohmann::json::parse_error so the
- // 17 downstream callsites that catch (nlohmann::json::parse_error&) keep
- // working unchanged. Catching the specific type guards against a future
- // refactor silently changing the throw type.
- bool threw = false;
- try {
- (void)parse_to_nlohmann("{not json");
- } catch (const nlohmann::json::parse_error&) {
- threw = true;
- }
- check(threw, "invalid json throws parse_error");
- }
- void test_empty_string_throws_parse_error() {
- // After the v1.10 fix, empty input is no longer pre-rejected with a
- // bespoke "empty input" message — it falls through to yyjson, which
- // emits a positional "unexpected end of data" error wrapped in
- // parse_error. Verifies the message-path consistency the reviewer asked
- // for so that operators see the same error shape for empty vs. malformed.
- bool threw = false;
- try {
- (void)parse_to_nlohmann("");
- } catch (const nlohmann::json::parse_error&) {
- threw = true;
- }
- check(threw, "empty string throws parse_error");
- }
- void test_corpus_equivalence() {
- // Sample of doc shapes the production service sees.
- const char* corpus[] = {
- R"({"_id":"abc","name":"x","tags":["a","b"],"count":0})",
- R"({"_id":"xyz","_vector":[0.1,-0.2,0.3,0.4],"meta":{"k":"v"}})",
- R"({"_id":"e1","_event":"call.ended","at":1731628800123})",
- R"([])",
- R"([1,2,3,4,5,6,7,8,9,10])",
- R"({"deeply":{"nested":{"object":{"is":{"fine":true}}}}})",
- };
- for (const char* s : corpus) {
- check(parse_to_nlohmann(s) == json::parse(s), s);
- }
- }
- } // namespace
- int main() {
- test_empty_object();
- test_empty_array();
- test_primitives();
- test_nested();
- test_unicode_string();
- test_large_int();
- test_string_view_overload();
- test_invalid_json_throws();
- test_empty_string_throws_parse_error();
- test_corpus_equivalence();
- std::cout << "test_json_parse: all passed\n";
- return 0;
- }
|