blob: 51e9b5af2e8ee06c6b6dae8f254fbe105ce60f38 [file]
// 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