| // Copyright 2026 Google LLC | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // | |
| #include "testing/cbor_utils.h" | |
| #include <string> | |
| #include <utility> | |
| #include "absl/strings/string_view.h" | |
| #include "cbor/parse.h" | |
| #include "cppbor/cppbor.h" | |
| #include "gtest/gtest.h" | |
| #include "nlohmann/json.hpp" | |
| #include "testing/proto/test_message.pb.h" | |
| namespace { | |
| constexpr absl::string_view kTestJsonMap = R"json({ | |
| "padding": "b64'TQ=='", | |
| "foo": "b64'Zm9v'", | |
| "bar": "bar", | |
| "array": ["b64'Zm9v'", "b64'YmFy'", 1, "bar"], | |
| "map": {"foo": "b64'Zm9v'", "bar": "bar"}, | |
| "array_map": [{"foo": "b64'Zm9v'", "bar": "bar"}, {"foo": "b64'Zm9v'", "bar": "bar"}], | |
| "array_array": [["b64'Zm9v'", "b64'YmFy'", 1, "bar"], ["b64'Zm9v'", "b64'YmFy'", 1, "bar"]] | |
| })json"; | |
| constexpr absl::string_view kTestJsonArray = | |
| R"json(["b64'Zm9v'", "b64'YmFy'", 1, "bar"])json"; | |
| TEST(CborUtilsTest, JsonToCborToJson_NoB64) { | |
| auto json = R"json({ | |
| "foo": "bar" | |
| })json"; | |
| auto cbor = cbor::FromJson(json); | |
| auto decoded_json = cbor::ToJson(cbor); | |
| EXPECT_EQ(nlohmann::json::parse(decoded_json), nlohmann::json::parse(json)); | |
| } | |
| TEST(CborUtilsTest, JsonToCborToJson_MapWithB64) { | |
| auto cbor = cbor::FromJson(kTestJsonMap); | |
| EXPECT_EQ(nlohmann::json::parse(cbor::ToJson(cbor)), | |
| nlohmann::json::parse(kTestJsonMap)); | |
| } | |
| TEST(CborUtilsTest, JsonToCborToJson_ArrayWithB64) { | |
| auto cbor = cbor::FromJson(kTestJsonArray); | |
| EXPECT_EQ(nlohmann::json::parse(cbor::ToJson(cbor)), | |
| nlohmann::json::parse(kTestJsonArray)); | |
| } | |
| TEST(CborUtilsTest, ToJson_WithSemanticTag) { | |
| // 0xC0 is Tag 0 (RFC 3339 date/time string). | |
| // Construct a CBOR map {"when": 0xC0 "2026-05-19T00:00:00Z"}. | |
| cppbor::Map orig_map; | |
| orig_map.add("when", | |
| cppbor::SemanticTag(0, cppbor::Tstr("2026-05-19T00:00:00Z"))); | |
| auto bytes = orig_map.encode(); | |
| absl::string_view bytes_view(reinterpret_cast<const char*>(bytes.data()), | |
| bytes.size()); | |
| std::string json = cbor::ToJson(bytes_view); | |
| EXPECT_EQ( | |
| nlohmann::json::parse(json), | |
| nlohmann::json::parse(R"json({"when": "2026-05-19T00:00:00Z"})json")); | |
| } | |
| TEST(CborUtilsTest, JsonToCbor_MapWithB64) { | |
| auto cbor = cbor::FromJson(kTestJsonMap); | |
| auto value1 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value1.ok()); | |
| auto result = std::move(value1).value(); | |
| auto value2 = result->AsMap(); | |
| ASSERT_TRUE(value2.ok()); | |
| auto map = std::move(value2).value(); | |
| auto value3 = map.GetByteString("padding"); | |
| ASSERT_TRUE(value3.ok()); | |
| auto padding = std::move(value3).value(); | |
| EXPECT_EQ(padding, "M"); | |
| auto value4 = map.GetByteString("foo"); | |
| ASSERT_TRUE(value4.ok()); | |
| auto foo = std::move(value4).value(); | |
| EXPECT_EQ(foo, "foo"); | |
| auto value5 = map.GetString("bar"); | |
| ASSERT_TRUE(value5.ok()); | |
| auto bar = std::move(value5).value(); | |
| EXPECT_EQ(bar, "bar"); | |
| auto value6 = map.GetArray("array"); | |
| ASSERT_TRUE(value6.ok()); | |
| auto array = std::move(value6).value(); | |
| ASSERT_EQ(array.size(), 4); | |
| auto value7 = array.GetByteString(0); | |
| ASSERT_TRUE(value7.ok()); | |
| auto array_foo = std::move(value7).value(); | |
| EXPECT_EQ(array_foo, "foo"); | |
| auto value8 = array.GetByteString(1); | |
| ASSERT_TRUE(value8.ok()); | |
| auto array_bar = std::move(value8).value(); | |
| EXPECT_EQ(array_bar, "bar"); | |
| auto value9 = array.GetUint64(2); | |
| ASSERT_TRUE(value9.ok()); | |
| auto array_1 = std::move(value9).value(); | |
| EXPECT_EQ(array_1, 1); | |
| auto value10 = array.GetString(3); | |
| ASSERT_TRUE(value10.ok()); | |
| auto array_bar_2 = std::move(value10).value(); | |
| EXPECT_EQ(array_bar_2, "bar"); | |
| auto value11 = map.GetMap("map"); | |
| ASSERT_TRUE(value11.ok()); | |
| auto map_map = std::move(value11).value(); | |
| auto value12 = map_map.GetByteString("foo"); | |
| ASSERT_TRUE(value12.ok()); | |
| auto map_foo = std::move(value12).value(); | |
| EXPECT_EQ(map_foo, "foo"); | |
| auto value13 = map_map.GetString("bar"); | |
| ASSERT_TRUE(value13.ok()); | |
| auto map_bar = std::move(value13).value(); | |
| EXPECT_EQ(map_bar, "bar"); | |
| auto value14 = map.GetArray("array_map"); | |
| ASSERT_TRUE(value14.ok()); | |
| auto array_map = std::move(value14).value(); | |
| ASSERT_EQ(array_map.size(), 2); | |
| auto value15 = array_map.GetMap(0); | |
| ASSERT_TRUE(value15.ok()); | |
| auto array_map_0 = std::move(value15).value(); | |
| auto value16 = array_map_0.GetByteString("foo"); | |
| ASSERT_TRUE(value16.ok()); | |
| auto array_map_foo_0 = std::move(value16).value(); | |
| EXPECT_EQ(array_map_foo_0, "foo"); | |
| auto value17 = array_map_0.GetString("bar"); | |
| ASSERT_TRUE(value17.ok()); | |
| auto array_map_bar_0 = std::move(value17).value(); | |
| EXPECT_EQ(array_map_bar_0, "bar"); | |
| auto value18 = array_map.GetMap(1); | |
| ASSERT_TRUE(value18.ok()); | |
| auto array_map_1 = std::move(value18).value(); | |
| auto value19 = array_map_1.GetByteString("foo"); | |
| ASSERT_TRUE(value19.ok()); | |
| auto array_map_foo_1 = std::move(value19).value(); | |
| EXPECT_EQ(array_map_foo_1, "foo"); | |
| auto value20 = array_map_1.GetString("bar"); | |
| ASSERT_TRUE(value20.ok()); | |
| auto array_map_bar_1 = std::move(value20).value(); | |
| EXPECT_EQ(array_map_bar_1, "bar"); | |
| auto value21 = map.GetArray("array_array"); | |
| ASSERT_TRUE(value21.ok()); | |
| auto array_array = std::move(value21).value(); | |
| ASSERT_EQ(array_array.size(), 2); | |
| auto value22 = array_array.GetArray(0); | |
| ASSERT_TRUE(value22.ok()); | |
| auto array_array_0 = std::move(value22).value(); | |
| ASSERT_EQ(array_array_0.size(), 4); | |
| auto value23 = array_array_0.GetByteString(0); | |
| ASSERT_TRUE(value23.ok()); | |
| auto array_array_foo_0 = std::move(value23).value(); | |
| EXPECT_EQ(array_array_foo_0, "foo"); | |
| auto value24 = array_array_0.GetString(3); | |
| ASSERT_TRUE(value24.ok()); | |
| auto array_array_bar_0 = std::move(value24).value(); | |
| EXPECT_EQ(array_array_bar_0, "bar"); | |
| auto value25 = array_array.GetArray(1); | |
| ASSERT_TRUE(value25.ok()); | |
| auto array_array_1 = std::move(value25).value(); | |
| ASSERT_EQ(array_array_1.size(), 4); | |
| auto value26 = array_array_1.GetByteString(0); | |
| ASSERT_TRUE(value26.ok()); | |
| auto array_array_foo_1 = std::move(value26).value(); | |
| EXPECT_EQ(array_array_foo_1, "foo"); | |
| auto value27 = array_array_1.GetString(3); | |
| ASSERT_TRUE(value27.ok()); | |
| auto array_array_bar_1 = std::move(value27).value(); | |
| EXPECT_EQ(array_array_bar_1, "bar"); | |
| } | |
| TEST(CborUtilsTest, JsonToCbor_ArrayWithB64) { | |
| auto cbor = cbor::FromJson(kTestJsonArray); | |
| auto value28 = cbor::Parse(cbor); | |
| ASSERT_TRUE(value28.ok()); | |
| auto result = std::move(value28).value(); | |
| auto value29 = result->AsArray(); | |
| ASSERT_TRUE(value29.ok()); | |
| auto array = std::move(value29).value(); | |
| ASSERT_EQ(array.size(), 4); | |
| auto value30 = array.GetByteString(0); | |
| ASSERT_TRUE(value30.ok()); | |
| auto array_foo = std::move(value30).value(); | |
| EXPECT_EQ(array_foo, "foo"); | |
| auto value31 = array.GetByteString(1); | |
| ASSERT_TRUE(value31.ok()); | |
| auto array_bar = std::move(value31).value(); | |
| EXPECT_EQ(array_bar, "bar"); | |
| auto value32 = array.GetUint64(2); | |
| ASSERT_TRUE(value32.ok()); | |
| auto array_1 = std::move(value32).value(); | |
| EXPECT_EQ(array_1, 1); | |
| auto value33 = array.GetString(3); | |
| ASSERT_TRUE(value33.ok()); | |
| auto array_bar_2 = std::move(value33).value(); | |
| EXPECT_EQ(array_bar_2, "bar"); | |
| } | |
| TEST(CborUtilsTest, JsonToCbor_ErrorMalformedBase64Decoding) { | |
| auto json = R"json({ | |
| "foo": "b64'Zm9v`'" | |
| })json"; | |
| EXPECT_DEATH({ cbor::FromJson(json); }, "Failed to decode base64: Zm9v`"); | |
| } | |
| TEST(CborUtilsTest, JsonToCbor_ErrorMalformedBase64Format) { | |
| auto json = R"json({ | |
| "foo": "b64'Zm9v" | |
| })json"; | |
| EXPECT_DEATH( | |
| { cbor::FromJson(json); }, | |
| "The base64-encoded string b64'Zm9v does not end with the suffix '"); | |
| } | |
| TEST(CborUtilsDeathTest, JsonToCbor_Malformed) { | |
| EXPECT_DEATH( | |
| { cbor::FromJson("malformed"); }, "JSON parse error.*syntax error"); | |
| } | |
| TEST(CborUtilsDeathTest, CborToJson_Malformed) { | |
| EXPECT_DEATH({ cbor::ToJson("malformed"); }, "CBOR parsing failed"); | |
| } | |
| } // namespace |