1//     __ _____ _____ _____
2//  __|  |   __|     |   | |  JSON for Modern C++ (supporting code)
3// |  |  |__   |  |  | | | |  version 3.11.2
4// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
5//
6// Copyright (c) 2013-2022 Niels Lohmann <http://nlohmann.me>.
7// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
8// SPDX-License-Identifier: MIT
9
10#include "doctest_compatibility.h"
11
12#ifdef JSON_DIAGNOSTICS
13    #undef JSON_DIAGNOSTICS
14#endif
15
16#define JSON_DIAGNOSTICS 1
17
18#include <nlohmann/json.hpp>
19using nlohmann::json;
20
21TEST_CASE("Better diagnostics")
22{
23    SECTION("empty JSON Pointer")
24    {
25        json j = 1;
26        std::string s;
27        CHECK_THROWS_WITH_AS(s = j.get<std::string>(), "[json.exception.type_error.302] type must be string, but is number", json::type_error);
28    }
29
30    SECTION("invalid type")
31    {
32        json j;
33        j["a"]["b"]["c"] = 1;
34        std::string s;
35        CHECK_THROWS_WITH_AS(s = j["a"]["b"]["c"].get<std::string>(), "[json.exception.type_error.302] (/a/b/c) type must be string, but is number", json::type_error);
36    }
37
38    SECTION("missing key")
39    {
40        json j;
41        j["object"]["object"] = true;
42        CHECK_THROWS_WITH_AS(j["object"].at("not_found"), "[json.exception.out_of_range.403] (/object) key 'not_found' not found", json::out_of_range);
43    }
44
45    SECTION("array index out of range")
46    {
47        json j;
48        j["array"][4] = true;
49        CHECK_THROWS_WITH_AS(j["array"].at(5), "[json.exception.out_of_range.401] (/array) array index 5 is out of range", json::out_of_range);
50    }
51
52    SECTION("array index at wrong type")
53    {
54        json j;
55        j["array"][4] = true;
56        CHECK_THROWS_WITH_AS(j["array"][4][5], "[json.exception.type_error.305] (/array/4) cannot use operator[] with a numeric argument with boolean", json::type_error);
57    }
58
59    SECTION("wrong iterator")
60    {
61        json j;
62        j["array"] = json::array();
63        CHECK_THROWS_WITH_AS(j["array"].erase(j.begin()), "[json.exception.invalid_iterator.202] (/array) iterator does not fit current value", json::invalid_iterator);
64    }
65
66    SECTION("JSON Pointer escaping")
67    {
68        json j;
69        j["a/b"]["m~n"] = 1;
70        std::string s;
71        CHECK_THROWS_WITH_AS(s = j["a/b"]["m~n"].get<std::string>(), "[json.exception.type_error.302] (/a~1b/m~0n) type must be string, but is number", json::type_error);
72    }
73
74    SECTION("Parse error")
75    {
76        json _;
77        CHECK_THROWS_WITH_AS(_ = json::parse(""), "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error);
78    }
79
80    SECTION("Wrong type in update()")
81    {
82        json j = {{"foo", "bar"}};
83        json k = {{"bla", 1}};
84
85        CHECK_THROWS_WITH_AS(j.update(k["bla"].begin(), k["bla"].end()), "[json.exception.type_error.312] (/bla) cannot use update() with number", json::type_error);
86        CHECK_THROWS_WITH_AS(j.update(k["bla"]), "[json.exception.type_error.312] (/bla) cannot use update() with number", json::type_error);
87    }
88}
89
90TEST_CASE("Regression tests for extended diagnostics")
91{
92    SECTION("Regression test for https://github.com/nlohmann/json/pull/2562#pullrequestreview-574858448")
93    {
94        CHECK_THROWS_WITH_AS(json({"0", "0"})[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error);
95        CHECK_THROWS_WITH_AS(json({"0", "1"})[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error);
96    }
97
98    SECTION("Regression test for https://github.com/nlohmann/json/pull/2562/files/380a613f2b5d32425021129cd1f371ddcfd54ddf#r563259793")
99    {
100        json j;
101        j["/foo"] = {1, 2, 3};
102        CHECK_THROWS_WITH_AS(j.unflatten(), "[json.exception.type_error.315] (/~1foo) values in object must be primitive", json::type_error);
103    }
104
105    SECTION("Regression test for issue #2838 - Assertion failure when inserting into arrays with JSON_DIAGNOSTICS set")
106    {
107        // void push_back(basic_json&& val)
108        {
109            json j_arr = json::array();
110            j_arr.push_back(json::object());
111            j_arr.push_back(json::object());
112            j_arr.push_back(json::object());
113            j_arr.push_back(json::object());
114            json j_obj = json::object();
115            j_obj["key"] = j_arr;
116        }
117
118        // void push_back(const basic_json& val)
119        {
120            json j_arr = json::array();
121            auto object = json::object();
122            j_arr.push_back(object);
123            j_arr.push_back(object);
124            j_arr.push_back(object);
125            j_arr.push_back(object);
126            json j_obj = json::object();
127            j_obj["key"] = j_arr;
128        }
129
130        // reference emplace_back(Args&& ... args)
131        {
132            json j_arr = json::array();
133            j_arr.emplace_back(json::object());
134            j_arr.emplace_back(json::object());
135            j_arr.emplace_back(json::object());
136            j_arr.emplace_back(json::object());
137            json j_obj = json::object();
138            j_obj["key"] = j_arr;
139        }
140
141        // iterator insert(const_iterator pos, const basic_json& val)
142        {
143            json j_arr = json::array();
144            j_arr.insert(j_arr.begin(), json::object());
145            j_arr.insert(j_arr.begin(), json::object());
146            j_arr.insert(j_arr.begin(), json::object());
147            j_arr.insert(j_arr.begin(), json::object());
148            json j_obj = json::object();
149            j_obj["key"] = j_arr;
150        }
151
152        // iterator insert(const_iterator pos, size_type cnt, const basic_json& val)
153        {
154            json j_arr = json::array();
155            j_arr.insert(j_arr.begin(), 2, json::object());
156            json j_obj = json::object();
157            j_obj["key"] = j_arr;
158        }
159
160        // iterator insert(const_iterator pos, const_iterator first, const_iterator last)
161        {
162            json j_arr = json::array();
163            json j_objects = {json::object(), json::object()};
164            j_arr.insert(j_arr.begin(), j_objects.begin(), j_objects.end());
165            json j_obj = json::object();
166            j_obj["key"] = j_arr;
167        }
168    }
169
170    SECTION("Regression test for issue #2962 - JSON_DIAGNOSTICS assertion for ordered_json")
171    {
172        nlohmann::ordered_json j;
173        nlohmann::ordered_json j2;
174        const std::string value;
175        j["first"] = value;
176        j["second"] = value;
177        j2["something"] = j;
178    }
179
180    SECTION("Regression test for issue #3007 - Parent pointers properly set when using update()")
181    {
182        // void update(const_reference j)
183        {
184            json j = json::object();
185
186            {
187                json j2 = json::object();
188                j2["one"] = 1;
189
190                j.update(j2);
191            }
192
193            // Must call operator[] on const element, otherwise m_parent gets updated.
194            auto const& constJ = j;
195            CHECK_THROWS_WITH_AS(constJ["one"].at(0), "[json.exception.type_error.304] (/one) cannot use at() with number", json::type_error);
196        }
197
198        // void update(const_iterator first, const_iterator last)
199        {
200            json j = json::object();
201
202            {
203                json j2 = json::object();
204                j2["one"] = 1;
205
206                j.update(j2.begin(), j2.end());
207            }
208
209            // Must call operator[] on const element, otherwise m_parent gets updated.
210            auto const& constJ = j;
211            CHECK_THROWS_WITH_AS(constJ["one"].at(0), "[json.exception.type_error.304] (/one) cannot use at() with number", json::type_error);
212        }
213
214        // Code from #3007 triggering unwanted assertion without fix to update().
215        {
216            json root = json::array();
217            json lower = json::object();
218
219            {
220                json lowest = json::object();
221                lowest["one"] = 1;
222
223                lower.update(lowest);
224            }
225
226            root.push_back(lower);
227        }
228    }
229
230    SECTION("Regression test for issue #3032 - Yet another assertion failure when inserting into arrays with JSON_DIAGNOSTICS set")
231    {
232        // reference operator[](size_type idx)
233        {
234            json j_arr = json::array();
235            j_arr[0] = 0;
236            j_arr[1] = 1;
237            j_arr[2] = 2;
238            j_arr[3] = 3;
239            j_arr[4] = 4;
240            j_arr[5] = 5;
241            j_arr[6] = 6;
242            j_arr[7] = 7;
243            json j_arr_copy = j_arr;
244        }
245    }
246}
247