//     __ _____ _____ _____
//  __|  |   __|     |   | |  JSON for Modern C++ (supporting code)
// |  |  |__   |  |  | | | |  version 3.11.3
// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
//
// Copyright (c) 2013-2022 Niels Lohmann <http://nlohmann.me>.
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT

#include "doctest_compatibility.h"

#include <azure/core/internal/json/json.hpp>
using Azure::Core::Json::_internal::json;

TEST_CASE("modifiers")
{
  SECTION("clear()")
  {
    SECTION("boolean")
    {
      json j = true;
      json const k = j;

      j.clear();
      CHECK(j == json(json::value_t::boolean));
      CHECK(j == json(k.type()));
    }

    SECTION("string")
    {
      json j = "hello world";
      json const k = j;

      j.clear();
      CHECK(j == json(json::value_t::string));
      CHECK(j == json(k.type()));
    }

    SECTION("array")
    {
      SECTION("empty array")
      {
        json j = json::array();
        json const k = j;

        j.clear();
        CHECK(j.empty());
        CHECK(j == json(json::value_t::array));
        CHECK(j == json(k.type()));
      }

      SECTION("filled array")
      {
        json j = {1, 2, 3};
        json const k = j;

        j.clear();
        CHECK(j.empty());
        CHECK(j == json(json::value_t::array));
        CHECK(j == json(k.type()));
      }
    }

    SECTION("object")
    {
      SECTION("empty object")
      {
        json j = json::object();
        json const k = j;

        j.clear();
        CHECK(j.empty());
        CHECK(j == json(json::value_t::object));
        CHECK(j == json(k.type()));
      }

      SECTION("filled object")
      {
        json j = {{"one", 1}, {"two", 2}, {"three", 3}};
        json const k = j;

        j.clear();
        CHECK(j.empty());
        CHECK(j == json(json::value_t::object));
        CHECK(j == json(k.type()));
      }
    }

    SECTION("binary")
    {
      SECTION("empty binary")
      {
        json j = json::binary({});
        json const k = j;

        j.clear();
        CHECK(!j.empty());
        CHECK(j == json(json::value_t::binary));
        CHECK(j == json(k.type()));
      }

      SECTION("filled binary")
      {
        json j = json::binary({1, 2, 3, 4, 5});
        json const k = j;

        j.clear();
        CHECK(!j.empty());
        CHECK(j == json(json::value_t::binary));
        CHECK(j == json(k.type()));
      }
    }

    SECTION("number (integer)")
    {
      json j = 23;
      json const k = j;

      j.clear();
      CHECK(j == json(json::value_t::number_integer));
      CHECK(j == json(k.type()));
    }

    SECTION("number (unsigned)")
    {
      json j = 23u;
      json const k = j;

      j.clear();
      CHECK(j == json(json::value_t::number_integer));
      CHECK(j == json(k.type()));
    }

    SECTION("number (float)")
    {
      json j = 23.42;
      json const k = j;

      j.clear();
      CHECK(j == json(json::value_t::number_float));
      CHECK(j == json(k.type()));
    }

    SECTION("null")
    {
      json j = nullptr;
      json const k = j;

      j.clear();
      CHECK(j == json(json::value_t::null));
      CHECK(j == json(k.type()));
    }
  }

  SECTION("push_back()")
  {
    SECTION("to array")
    {
      SECTION("json&&")
      {
        SECTION("null")
        {
          json j;
          j.push_back(1);
          j.push_back(2);
          CHECK(j.type() == json::value_t::array);
          CHECK(j == json({1, 2}));
        }

        SECTION("array")
        {
          json j = {1, 2, 3};
          j.push_back("Hello");
          CHECK(j.type() == json::value_t::array);
          CHECK(j == json({1, 2, 3, "Hello"}));
        }

        SECTION("other type")
        {
          json j = 1;
          CHECK_THROWS_WITH_AS(
              j.push_back("Hello"),
              "[json.exception.type_error.308] cannot use push_back() with number",
              json::type_error&);
        }
      }

      SECTION("const json&")
      {
        SECTION("null")
        {
          json j;
          json const k(1);
          j.push_back(k);
          j.push_back(k);
          CHECK(j.type() == json::value_t::array);
          CHECK(j == json({1, 1}));
        }

        SECTION("array")
        {
          json j = {1, 2, 3};
          json const k("Hello");
          j.push_back(k);
          CHECK(j.type() == json::value_t::array);
          CHECK(j == json({1, 2, 3, "Hello"}));
        }

        SECTION("other type")
        {
          json j = 1;
          json const k("Hello");
          CHECK_THROWS_WITH_AS(
              j.push_back(k),
              "[json.exception.type_error.308] cannot use push_back() with number",
              json::type_error&);
        }
      }
    }

    SECTION("to object")
    {
      SECTION("null")
      {
        json j;
        j.push_back(json::object_t::value_type({"one", 1}));
        j.push_back(json::object_t::value_type({"two", 2}));
        CHECK(j.type() == json::value_t::object);
        CHECK(j.size() == 2);
        CHECK(j["one"] == json(1));
        CHECK(j["two"] == json(2));
      }

      SECTION("object")
      {
        json j(json::value_t::object);
        j.push_back(json::object_t::value_type({"one", 1}));
        j.push_back(json::object_t::value_type({"two", 2}));
        CHECK(j.size() == 2);
        CHECK(j["one"] == json(1));
        CHECK(j["two"] == json(2));
      }

      SECTION("other type")
      {
        json j = 1;
        json const k("Hello");
        CHECK_THROWS_WITH_AS(
            j.push_back(json::object_t::value_type({"one", 1})),
            "[json.exception.type_error.308] cannot use push_back() with number",
            json::type_error&);
      }
    }

    SECTION("with initializer_list")
    {
      SECTION("null")
      {
        json j;
        j.push_back({"foo", "bar"});
        CHECK(j == json::array({{"foo", "bar"}}));

        json k;
        k.push_back({1, 2, 3});
        CHECK(k == json::array({{1, 2, 3}}));
      }

      SECTION("array")
      {
        json j = {1, 2, 3};
        j.push_back({"foo", "bar"});
        CHECK(j == json({1, 2, 3, {"foo", "bar"}}));

        json k = {1, 2, 3};
        k.push_back({1, 2, 3});
        CHECK(k == json({1, 2, 3, {1, 2, 3}}));
      }

      SECTION("object")
      {
        json j = {{"key1", 1}};
        j.push_back({"key2", "bar"});
        CHECK(j == json({{"key1", 1}, {"key2", "bar"}}));

        // invalid values (no string/val pair)
        CHECK_THROWS_WITH_AS(
            j.push_back({1}),
            "[json.exception.type_error.308] cannot use push_back() with object",
            json::type_error&);
        CHECK_THROWS_WITH_AS(
            j.push_back({1, 2}),
            "[json.exception.type_error.308] cannot use push_back() with object",
            json::type_error&);
        CHECK_THROWS_WITH_AS(
            j.push_back({1, 2, 3, 4}),
            "[json.exception.type_error.308] cannot use push_back() with object",
            json::type_error&);
      }
    }
  }

  SECTION("emplace_back()")
  {
    SECTION("to array")
    {
      SECTION("null")
      {
        json j;
        auto& x1 = j.emplace_back(1);
        CHECK(x1 == 1);
        auto& x2 = j.emplace_back(2);
        CHECK(x2 == 2);
        CHECK(j.type() == json::value_t::array);
        CHECK(j == json({1, 2}));
      }

      SECTION("array")
      {
        json j = {1, 2, 3};
        auto& x = j.emplace_back("Hello");
        CHECK(x == "Hello");
        CHECK(j.type() == json::value_t::array);
        CHECK(j == json({1, 2, 3, "Hello"}));
      }

      SECTION("multiple values")
      {
        json j;
        auto& x = j.emplace_back(3, "foo");
        CHECK(x == json({"foo", "foo", "foo"}));
        CHECK(j.type() == json::value_t::array);
        CHECK(j == json({{"foo", "foo", "foo"}}));
      }
    }

    SECTION("other type")
    {
      json j = 1;
      CHECK_THROWS_WITH_AS(
          j.emplace_back("Hello"),
          "[json.exception.type_error.311] cannot use emplace_back() with number",
          json::type_error&);
    }
  }

  SECTION("emplace()")
  {
    SECTION("to object")
    {
      SECTION("null")
      {
        // start with a null value
        json j;

        // add a new key
        auto res1 = j.emplace("foo", "bar");
        CHECK(res1.second == true);
        CHECK(*res1.first == "bar");

        // the null value is changed to an object
        CHECK(j.type() == json::value_t::object);

        // add a new key
        auto res2 = j.emplace("baz", "bam");
        CHECK(res2.second == true);
        CHECK(*res2.first == "bam");

        // we try to insert at given key - no change
        auto res3 = j.emplace("baz", "bad");
        CHECK(res3.second == false);
        CHECK(*res3.first == "bam");

        // the final object
        CHECK(j == json({{"baz", "bam"}, {"foo", "bar"}}));
      }

      SECTION("object")
      {
        // start with an object
        json j = {{"foo", "bar"}};

        // add a new key
        auto res1 = j.emplace("baz", "bam");
        CHECK(res1.second == true);
        CHECK(*res1.first == "bam");

        // add an existing key
        auto res2 = j.emplace("foo", "bad");
        CHECK(res2.second == false);
        CHECK(*res2.first == "bar");

        // check final object
        CHECK(j == json({{"baz", "bam"}, {"foo", "bar"}}));
      }
    }

    SECTION("other type")
    {
      json j = 1;
      CHECK_THROWS_WITH_AS(
          j.emplace("foo", "bar"),
          "[json.exception.type_error.311] cannot use emplace() with number",
          json::type_error&);
    }
  }

  SECTION("operator+=")
  {
    SECTION("to array")
    {
      SECTION("json&&")
      {
        SECTION("null")
        {
          json j;
          j += 1;
          j += 2;
          CHECK(j.type() == json::value_t::array);
          CHECK(j == json({1, 2}));
        }

        SECTION("array")
        {
          json j = {1, 2, 3};
          j += "Hello";
          CHECK(j.type() == json::value_t::array);
          CHECK(j == json({1, 2, 3, "Hello"}));
        }

        SECTION("other type")
        {
          json j = 1;
          CHECK_THROWS_WITH_AS(
              j += "Hello",
              "[json.exception.type_error.308] cannot use push_back() with number",
              json::type_error&);
        }
      }

      SECTION("const json&")
      {
        SECTION("null")
        {
          json j;
          json const k(1);
          j += k;
          j += k;
          CHECK(j.type() == json::value_t::array);
          CHECK(j == json({1, 1}));
        }

        SECTION("array")
        {
          json j = {1, 2, 3};
          json const k("Hello");
          j += k;
          CHECK(j.type() == json::value_t::array);
          CHECK(j == json({1, 2, 3, "Hello"}));
        }

        SECTION("other type")
        {
          json j = 1;
          json const k("Hello");
          CHECK_THROWS_WITH_AS(
              j += k,
              "[json.exception.type_error.308] cannot use push_back() with number",
              json::type_error&);
        }
      }
    }

    SECTION("to object")
    {
      SECTION("null")
      {
        json j;
        j += json::object_t::value_type({"one", 1});
        j += json::object_t::value_type({"two", 2});
        CHECK(j.type() == json::value_t::object);
        CHECK(j.size() == 2);
        CHECK(j["one"] == json(1));
        CHECK(j["two"] == json(2));
      }

      SECTION("object")
      {
        json j(json::value_t::object);
        j += json::object_t::value_type({"one", 1});
        j += json::object_t::value_type({"two", 2});
        CHECK(j.size() == 2);
        CHECK(j["one"] == json(1));
        CHECK(j["two"] == json(2));
      }

      SECTION("other type")
      {
        json j = 1;
        json const k("Hello");
        CHECK_THROWS_WITH_AS(
            j += json::object_t::value_type({"one", 1}),
            "[json.exception.type_error.308] cannot use push_back() with number",
            json::type_error&);
      }
    }

    SECTION("with initializer_list")
    {
      SECTION("null")
      {
        json j;
        j += {"foo", "bar"};
        CHECK(j == json::array({{"foo", "bar"}}));

        json k;
        k += {1, 2, 3};
        CHECK(k == json::array({{1, 2, 3}}));
      }

      SECTION("array")
      {
        json j = {1, 2, 3};
        j += {"foo", "bar"};
        CHECK(j == json({1, 2, 3, {"foo", "bar"}}));

        json k = {1, 2, 3};
        k += {1, 2, 3};
        CHECK(k == json({1, 2, 3, {1, 2, 3}}));
      }

      SECTION("object")
      {
        json j = {{"key1", 1}};
        j += {"key2", "bar"};
        CHECK(j == json({{"key1", 1}, {"key2", "bar"}}));

        json k = {{"key1", 1}};
        CHECK_THROWS_WITH_AS(
            (k += {1, 2, 3, 4}),
            "[json.exception.type_error.308] cannot use push_back() with object",
            json::type_error&);
      }
    }
  }

  SECTION("insert()")
  {
    json j_array = {1, 2, 3, 4};
    json j_value = 5;

    SECTION("value at position")
    {
      SECTION("insert before begin()")
      {
        auto it = j_array.insert(j_array.begin(), j_value);
        CHECK(j_array.size() == 5);
        CHECK(*it == j_value);
        CHECK(j_array.begin() == it);
        CHECK(j_array == json({5, 1, 2, 3, 4}));
      }

      SECTION("insert in the middle")
      {
        auto it = j_array.insert(j_array.begin() + 2, j_value);
        CHECK(j_array.size() == 5);
        CHECK(*it == j_value);
        CHECK((it - j_array.begin()) == 2);
        CHECK(j_array == json({1, 2, 5, 3, 4}));
      }

      SECTION("insert before end()")
      {
        auto it = j_array.insert(j_array.end(), j_value);
        CHECK(j_array.size() == 5);
        CHECK(*it == j_value);
        CHECK((j_array.end() - it) == 1);
        CHECK(j_array == json({1, 2, 3, 4, 5}));
      }
    }

    SECTION("rvalue at position")
    {
      SECTION("insert before begin()")
      {
        auto it = j_array.insert(j_array.begin(), 5);
        CHECK(j_array.size() == 5);
        CHECK(*it == j_value);
        CHECK(j_array.begin() == it);
        CHECK(j_array == json({5, 1, 2, 3, 4}));
      }

      SECTION("insert in the middle")
      {
        auto it = j_array.insert(j_array.begin() + 2, 5);
        CHECK(j_array.size() == 5);
        CHECK(*it == j_value);
        CHECK((it - j_array.begin()) == 2);
        CHECK(j_array == json({1, 2, 5, 3, 4}));
      }

      SECTION("insert before end()")
      {
        auto it = j_array.insert(j_array.end(), 5);
        CHECK(j_array.size() == 5);
        CHECK(*it == j_value);
        CHECK((j_array.end() - it) == 1);
        CHECK(j_array == json({1, 2, 3, 4, 5}));
      }
    }

    SECTION("copies at position")
    {
      SECTION("insert before begin()")
      {
        auto it = j_array.insert(j_array.begin(), 3, 5);
        CHECK(j_array.size() == 7);
        CHECK(*it == j_value);
        CHECK(j_array.begin() == it);
        CHECK(j_array == json({5, 5, 5, 1, 2, 3, 4}));
      }

      SECTION("insert in the middle")
      {
        auto it = j_array.insert(j_array.begin() + 2, 3, 5);
        CHECK(j_array.size() == 7);
        CHECK(*it == j_value);
        CHECK((it - j_array.begin()) == 2);
        CHECK(j_array == json({1, 2, 5, 5, 5, 3, 4}));
      }

      SECTION("insert before end()")
      {
        auto it = j_array.insert(j_array.end(), 3, 5);
        CHECK(j_array.size() == 7);
        CHECK(*it == j_value);
        CHECK((j_array.end() - it) == 3);
        CHECK(j_array == json({1, 2, 3, 4, 5, 5, 5}));
      }

      SECTION("insert nothing (count = 0)")
      {
        auto it = j_array.insert(j_array.end(), 0, 5);
        CHECK(j_array.size() == 4);
        // the returned iterator points to the first inserted element;
        // there were 4 elements, so it should point to the 5th
        CHECK(it == j_array.begin() + 4);
        CHECK(j_array == json({1, 2, 3, 4}));
      }
    }

    SECTION("range for array")
    {
      json j_other_array = {"first", "second"};

      SECTION("proper usage")
      {
        auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.end());
        CHECK(j_array.size() == 6);
        CHECK(*it == *j_other_array.begin());
        CHECK((j_array.end() - it) == 2);
        CHECK(j_array == json({1, 2, 3, 4, "first", "second"}));
      }

      SECTION("empty range")
      {
        auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.begin());
        CHECK(j_array.size() == 4);
        CHECK(it == j_array.end());
        CHECK(j_array == json({1, 2, 3, 4}));
      }

      SECTION("invalid iterators")
      {
        json j_other_array2 = {"first", "second"};

        CHECK_THROWS_WITH_AS(
            j_array.insert(j_array.end(), j_array.begin(), j_array.end()),
            "[json.exception.invalid_iterator.211] passed iterators may not belong to container",
            json::invalid_iterator&);
        CHECK_THROWS_WITH_AS(
            j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()),
            "[json.exception.invalid_iterator.210] iterators do not fit",
            json::invalid_iterator&);
      }
    }

    SECTION("range for object")
    {
      json j_object1 = {{"one", "eins"}, {"two", "zwei"}};
      json j_object2 = {{"eleven", "elf"}, {"seventeen", "siebzehn"}};

      SECTION("proper usage")
      {
        j_object1.insert(j_object2.begin(), j_object2.end());
        CHECK(j_object1.size() == 4);
      }

      SECTION("empty range")
      {
        j_object1.insert(j_object2.begin(), j_object2.begin());
        CHECK(j_object1.size() == 2);
      }

      SECTION("invalid iterators")
      {
        json const j_other_array2 = {"first", "second"};

        CHECK_THROWS_WITH_AS(
            j_array.insert(j_object2.begin(), j_object2.end()),
            "[json.exception.type_error.309] cannot use insert() with array",
            json::type_error&);
        CHECK_THROWS_WITH_AS(
            j_object1.insert(j_object1.begin(), j_object2.end()),
            "[json.exception.invalid_iterator.210] iterators do not fit",
            json::invalid_iterator&);
        CHECK_THROWS_WITH_AS(
            j_object1.insert(j_array.begin(), j_array.end()),
            "[json.exception.invalid_iterator.202] iterators first and last must point to objects",
            json::invalid_iterator&);
      }
    }

    SECTION("initializer list at position")
    {
      SECTION("insert before begin()")
      {
        auto it = j_array.insert(j_array.begin(), {7, 8, 9});
        CHECK(j_array.size() == 7);
        CHECK(*it == json(7));
        CHECK(j_array.begin() == it);
        CHECK(j_array == json({7, 8, 9, 1, 2, 3, 4}));
      }

      SECTION("insert in the middle")
      {
        auto it = j_array.insert(j_array.begin() + 2, {7, 8, 9});
        CHECK(j_array.size() == 7);
        CHECK(*it == json(7));
        CHECK((it - j_array.begin()) == 2);
        CHECK(j_array == json({1, 2, 7, 8, 9, 3, 4}));
      }

      SECTION("insert before end()")
      {
        auto it = j_array.insert(j_array.end(), {7, 8, 9});
        CHECK(j_array.size() == 7);
        CHECK(*it == json(7));
        CHECK((j_array.end() - it) == 3);
        CHECK(j_array == json({1, 2, 3, 4, 7, 8, 9}));
      }
    }

    SECTION("invalid iterator")
    {
      // pass iterator to a different array
      json j_another_array = {1, 2};
      json j_yet_another_array = {"first", "second"};
      CHECK_THROWS_WITH_AS(
          j_array.insert(j_another_array.end(), 10),
          "[json.exception.invalid_iterator.202] iterator does not fit current value",
          json::invalid_iterator&);
      CHECK_THROWS_WITH_AS(
          j_array.insert(j_another_array.end(), j_value),
          "[json.exception.invalid_iterator.202] iterator does not fit current value",
          json::invalid_iterator&);
      CHECK_THROWS_WITH_AS(
          j_array.insert(j_another_array.end(), 10, 11),
          "[json.exception.invalid_iterator.202] iterator does not fit current value",
          json::invalid_iterator&);
      CHECK_THROWS_WITH_AS(
          j_array.insert(
              j_another_array.end(), j_yet_another_array.begin(), j_yet_another_array.end()),
          "[json.exception.invalid_iterator.202] iterator does not fit current value",
          json::invalid_iterator&);
      CHECK_THROWS_WITH_AS(
          j_array.insert(j_another_array.end(), {1, 2, 3, 4}),
          "[json.exception.invalid_iterator.202] iterator does not fit current value",
          json::invalid_iterator&);
    }

    SECTION("non-array type")
    {
      // call insert on a non-array type
      json j_nonarray = 3;
      json j_yet_another_array = {"first", "second"};
      CHECK_THROWS_WITH_AS(
          j_nonarray.insert(j_nonarray.end(), 10),
          "[json.exception.type_error.309] cannot use insert() with number",
          json::type_error&);
      CHECK_THROWS_WITH_AS(
          j_nonarray.insert(j_nonarray.end(), j_value),
          "[json.exception.type_error.309] cannot use insert() with number",
          json::type_error&);
      CHECK_THROWS_WITH_AS(
          j_nonarray.insert(j_nonarray.end(), 10, 11),
          "[json.exception.type_error.309] cannot use insert() with number",
          json::type_error&);
      CHECK_THROWS_WITH_AS(
          j_nonarray.insert(
              j_nonarray.end(), j_yet_another_array.begin(), j_yet_another_array.end()),
          "[json.exception.type_error.309] cannot use insert() with number",
          json::type_error&);
      CHECK_THROWS_WITH_AS(
          j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}),
          "[json.exception.type_error.309] cannot use insert() with number",
          json::type_error&);
    }
  }

  SECTION("update()")
  {
    SECTION("non-recursive (default)")
    {
      json j_object1 = {{"one", "eins"}, {"two", "zwei"}};
      json j_object2 = {{"three", "drei"}, {"two", "zwo"}};
      json j_array = {1, 2, 3, 4};

      SECTION("const reference")
      {
        SECTION("proper usage")
        {
          j_object1.update(j_object2);
          CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwo"}, {"three", "drei"}}));

          json j_null;
          j_null.update(j_object2);
          CHECK(j_null == j_object2);
        }

        SECTION("wrong types")
        {
          CHECK_THROWS_WITH_AS(
              j_array.update(j_object1),
              "[json.exception.type_error.312] cannot use update() with array",
              json::type_error&);

          CHECK_THROWS_WITH_AS(
              j_object1.update(j_array),
              "[json.exception.type_error.312] cannot use update() with array",
              json::type_error&);
        }
      }

      SECTION("iterator range")
      {
        SECTION("proper usage")
        {
          j_object1.update(j_object2.begin(), j_object2.end());
          CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwo"}, {"three", "drei"}}));

          json j_null;
          j_null.update(j_object2.begin(), j_object2.end());
          CHECK(j_null == j_object2);
        }

        SECTION("empty range")
        {
          j_object1.update(j_object2.begin(), j_object2.begin());
          CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwei"}}));
        }

        SECTION("invalid iterators")
        {
          json const j_other_array2 = {"first", "second"};

          CHECK_THROWS_WITH_AS(
              j_array.update(j_object2.begin(), j_object2.end()),
              "[json.exception.type_error.312] cannot use update() with array",
              json::type_error&);
          CHECK_THROWS_WITH_AS(
              j_object1.update(j_object1.begin(), j_object2.end()),
              "[json.exception.invalid_iterator.210] iterators do not fit",
              json::invalid_iterator&);
          CHECK_THROWS_WITH_AS(
              j_object1.update(j_array.begin(), j_array.end()),
              "[json.exception.type_error.312] cannot use update() with array",
              json::type_error&);
        }
      }
    }

    SECTION("recursive")
    {
      SECTION("const reference")
      {
        SECTION("extend object")
        {
          json j1 = {{"string", "s"}, {"numbers", {{"one", 1}}}};
          json const j2 = {{"string", "t"}, {"numbers", {{"two", 2}}}};
          j1.update(j2, true);
          CHECK(j1 == json({{"string", "t"}, {"numbers", {{"one", 1}, {"two", 2}}}}));
        }

        SECTION("replace object")
        {
          json j1 = {{"string", "s"}, {"numbers", {{"one", 1}}}};
          json const j2 = {{"string", "t"}, {"numbers", 1}};
          j1.update(j2, true);
          CHECK(j1 == json({{"string", "t"}, {"numbers", 1}}));
        }
      }
    }
  }

  SECTION("swap()")
  {
    SECTION("json")
    {
      SECTION("member swap")
      {
        json j("hello world");
        json k(42.23);

        j.swap(k);

        CHECK(j == json(42.23));
        CHECK(k == json("hello world"));
      }

      SECTION("nonmember swap")
      {
        json j("hello world");
        json k(42.23);

        using std::swap;
        swap(j, k);

        CHECK(j == json(42.23));
        CHECK(k == json("hello world"));
      }
    }

    SECTION("array_t")
    {
      SECTION("array_t type")
      {
        json j = {1, 2, 3, 4};
        json::array_t a = {"foo", "bar", "baz"};

        j.swap(a);

        CHECK(j == json({"foo", "bar", "baz"}));

        j.swap(a);

        CHECK(j == json({1, 2, 3, 4}));
      }

      SECTION("non-array_t type")
      {
        json j = 17;
        json::array_t a = {"foo", "bar", "baz"};

        CHECK_THROWS_WITH_AS(
            j.swap(a),
            "[json.exception.type_error.310] cannot use swap(array_t&) with number",
            json::type_error&);
      }
    }

    SECTION("object_t")
    {
      SECTION("object_t type")
      {
        json j = {{"one", 1}, {"two", 2}};
        json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}};

        j.swap(o);

        CHECK(j == json({{"cow", "Kuh"}, {"chicken", "Huhn"}}));

        j.swap(o);

        CHECK(j == json({{"one", 1}, {"two", 2}}));
      }

      SECTION("non-object_t type")
      {
        json j = 17;
        json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}};

        CHECK_THROWS_WITH_AS(
            j.swap(o),
            "[json.exception.type_error.310] cannot use swap(object_t&) with number",
            json::type_error&);
      }
    }

    SECTION("string_t")
    {
      SECTION("string_t type")
      {
        json j = "Hello world";
        json::string_t s = "Hallo Welt";

        j.swap(s);

        CHECK(j == json("Hallo Welt"));

        j.swap(s);

        CHECK(j == json("Hello world"));
      }

      SECTION("non-string_t type")
      {
        json j = 17;
        json::string_t s = "Hallo Welt";

        CHECK_THROWS_WITH_AS(
            j.swap(s),
            "[json.exception.type_error.310] cannot use swap(string_t&) with number",
            json::type_error&);
      }
    }

    SECTION("binary_t")
    {
      SECTION("binary_t type")
      {
        json j = json::binary({1, 2, 3, 4});
        json::binary_t s = {{5, 6, 7, 8}};

        j.swap(s);

        CHECK(j == json::binary({5, 6, 7, 8}));

        j.swap(s);

        CHECK(j == json::binary({1, 2, 3, 4}));
      }

      SECTION("binary_t::container_type type")
      {
        json j = json::binary({1, 2, 3, 4});
        std::vector<std::uint8_t> s = {{5, 6, 7, 8}};

        j.swap(s);

        CHECK(j == json::binary({5, 6, 7, 8}));

        j.swap(s);

        CHECK(j == json::binary({1, 2, 3, 4}));
      }

      SECTION("non-binary_t type")
      {
        json j = 17;
        json::binary_t s1 = {{1, 2, 3, 4}};
        std::vector<std::uint8_t> s2 = {{5, 6, 7, 8}};

        CHECK_THROWS_WITH_AS(
            j.swap(s1),
            "[json.exception.type_error.310] cannot use swap(binary_t&) with number",
            json::type_error);
        CHECK_THROWS_WITH_AS(
            j.swap(s2),
            "[json.exception.type_error.310] cannot use swap(binary_t::container_type&) with "
            "number",
            json::type_error);
      }
    }
  }
}
