| // 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 |