ZeroTierOne/ext/opentelemetry-cpp-1.21.0/api/test/baggage/baggage_test.cc
2025-07-11 10:37:21 -07:00

222 lines
7.6 KiB
C++

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
#include <assert.h>
#include <gtest/gtest.h>
#include <stddef.h>
#include <string>
#include <vector>
#include "opentelemetry/baggage/baggage.h"
#include "opentelemetry/common/kv_properties.h"
#include "opentelemetry/nostd/function_ref.h"
#include "opentelemetry/nostd/shared_ptr.h"
#include "opentelemetry/nostd/string_view.h"
using namespace opentelemetry::baggage;
std::string header_with_custom_entries(size_t num_entries)
{
std::string header;
for (size_t i = 0; i < num_entries; i++)
{
std::string key = "key" + std::to_string(i);
std::string value = "value" + std::to_string(i);
header.append(key).append("=").append(value);
if (i != num_entries - 1)
{
header += ",";
}
}
return header;
}
std::string header_with_custom_size(size_t key_value_size, size_t num_entries)
{
std::string header = "";
for (size_t i = 0; i < num_entries; i++)
{
std::string str = std::to_string(i + 1);
str += "=";
assert(key_value_size > str.size());
for (size_t j = str.size(); j < key_value_size; j++)
{
str += "a";
}
header += str + ',';
}
header.pop_back();
return header;
}
TEST(BaggageTest, ValidateExtractHeader)
{
auto invalid_key_value_size_header = header_with_custom_size(Baggage::kMaxKeyValueSize + 5, 1);
struct
{
const char *input;
std::vector<const char *> keys;
std::vector<const char *> values;
} testcases[] = {
{"k1=v1", {"k1"}, {"v1"}},
{"k1=V1,K2=v2;metadata,k3=v3",
{"k1", "K2", "k3"},
{"V1", "v2;metadata", "v3"}}, // metadata is part of value
{",k1 =v1,k2=v2 ; metadata,",
{"k1", "k2"},
{"v1", "v2; metadata"}}, // key and value are trimmed
{"1a-2f%40foo=bar%251,a%2A%2Ffoo-_%2Fbar=bar+4",
{"1a-2f@foo", "a*/foo-_/bar"},
{"bar%1", "bar 4"}}, // decoding is done properly
{"k1=v1,invalidmember,k2=v2", {"k1", "k2"}, {"v1", "v2"}}, // invalid member is skipped
{",", {}, {}},
{",=,", {}, {}},
{"", {}, {}},
{"k1=%5zv", {}, {}}, // invalid hex : invalid second digit
{"k1=%5", {}, {}}, // invalid hex : missing two digits
{"k%z2=v1", {}, {}}, // invalid hex : invalid first digit
{"k%00=v1", {}, {}}, // key not valid
{"k=v%7f", {}, {}}, // value not valid
{invalid_key_value_size_header.data(), {}, {}}};
for (auto &testcase : testcases)
{
auto baggage = Baggage::FromHeader(testcase.input);
size_t index = 0;
baggage->GetAllEntries([&testcase, &index](opentelemetry::nostd::string_view key,
opentelemetry::nostd::string_view value) {
EXPECT_EQ(key, testcase.keys[index]);
EXPECT_EQ(value, testcase.values[index]);
index++;
return true;
});
}
// For header with maximum threshold pairs, no pair is dropped
auto max_pairs_header = header_with_custom_entries(Baggage::kMaxKeyValuePairs);
EXPECT_EQ(Baggage::FromHeader(max_pairs_header.data())->ToHeader(), max_pairs_header.data());
// Entries beyond threshold are dropped
auto baggage = Baggage::FromHeader(header_with_custom_entries(Baggage::kMaxKeyValuePairs + 1));
auto header = baggage->ToHeader();
opentelemetry::common::KeyValueStringTokenizer kv_str_tokenizer(header);
int expected_tokens = Baggage::kMaxKeyValuePairs;
EXPECT_EQ(kv_str_tokenizer.NumTokens(), expected_tokens);
// For header with total size more than threshold, baggage is empty
int num_pairs_with_max_size = Baggage::kMaxSize / Baggage::kMaxKeyValueSize;
auto invalid_total_size_header =
header_with_custom_size(Baggage::kMaxKeyValueSize, num_pairs_with_max_size + 1);
EXPECT_EQ(Baggage::FromHeader(invalid_total_size_header.data())->ToHeader(), "");
}
TEST(BaggageTest, ValidateInjectHeader)
{
struct
{
std::vector<const char *> keys;
std::vector<const char *> values;
const char *header;
} testcases[] = {{{"k1"}, {"v1"}, "k1=v1"},
{{"k3", "k2", "k1"}, {"", "v2", "v1"}, "k1=v1,k2=v2,k3="}, // empty value
{{"1a-2f@foo", "a*/foo-_/bar"},
{"bar%1", "bar 4"},
"a%2A%2Ffoo-_%2Fbar=bar+4,1a-2f%40foo=bar%251"}, // encoding is done properly
{{"foo 1"},
{"bar 1; metadata ; ;;"},
"foo+1=bar+1; metadata ; ;;"}}; // metadata is added without encoding
for (auto &testcase : testcases)
{
opentelemetry::nostd::shared_ptr<Baggage> baggage(new Baggage{});
for (size_t i = 0; i < testcase.keys.size(); i++)
{
baggage = baggage->Set(testcase.keys[i], testcase.values[i]);
}
EXPECT_EQ(baggage->ToHeader(), testcase.header);
}
}
TEST(BaggageTest, BaggageGet)
{
auto header = header_with_custom_entries(Baggage::kMaxKeyValuePairs);
auto baggage = Baggage::FromHeader(header);
std::string value;
EXPECT_TRUE(baggage->GetValue("key0", value));
EXPECT_EQ(value, "value0");
EXPECT_TRUE(baggage->GetValue("key16", value));
EXPECT_EQ(value, "value16");
EXPECT_TRUE(baggage->GetValue("key31", value));
EXPECT_EQ(value, "value31");
EXPECT_FALSE(baggage->GetValue("key181", value));
}
TEST(BaggageTest, BaggageSet)
{
std::string header = "k1=v1,k2=v2";
auto baggage = Baggage::FromHeader(header);
std::string value;
baggage = baggage->Set("k3", "v3");
EXPECT_TRUE(baggage->GetValue("k3", value));
EXPECT_EQ(value, "v3");
baggage = baggage->Set("k3", "v3_1"); // key should be updated with the latest value
EXPECT_TRUE(baggage->GetValue("k3", value));
EXPECT_EQ(value, "v3_1");
header = header_with_custom_entries(Baggage::kMaxKeyValuePairs);
baggage = Baggage::FromHeader(header);
baggage = baggage->Set("key0", "0"); // updating on max list should work
EXPECT_TRUE(baggage->GetValue("key0", value));
EXPECT_EQ(value, "0");
header = "k1=v1,k2=v2";
baggage = Baggage::FromHeader(header);
baggage = baggage->Set("", "n_v1"); // adding invalid key, should return copy of same baggage
EXPECT_EQ(baggage->ToHeader(), header);
header = "k1=v1,k2=v2";
baggage = Baggage::FromHeader(header);
baggage = baggage->Set("k1", "\x1A"); // adding invalid value, should return copy of same baggage
EXPECT_EQ(baggage->ToHeader(), header);
}
TEST(BaggageTest, BaggageRemove)
{
auto header = header_with_custom_entries(Baggage::kMaxKeyValuePairs);
auto baggage = Baggage::FromHeader(header);
std::string value;
// existing key is removed
EXPECT_TRUE(baggage->GetValue("key0", value));
auto new_baggage = baggage->Delete("key0");
EXPECT_FALSE(new_baggage->GetValue("key0", value));
// trying Delete on non existent key
EXPECT_FALSE(baggage->GetValue("key181", value));
auto new_baggage_2 = baggage->Delete("key181");
EXPECT_FALSE(new_baggage_2->GetValue("key181", value));
}
TEST(BaggageTest, BaggageGetAll)
{
std::string baggage_header = "k1=v1,k2=v2,k3=v3";
auto baggage = Baggage::FromHeader(baggage_header);
const int kNumPairs = 3;
opentelemetry::nostd::string_view keys[kNumPairs] = {"k1", "k2", "k3"};
opentelemetry::nostd::string_view values[kNumPairs] = {"v1", "v2", "v3"};
size_t index = 0;
baggage->GetAllEntries([&keys, &values, &index](opentelemetry::nostd::string_view key,
opentelemetry::nostd::string_view value) {
EXPECT_EQ(key, keys[index]);
EXPECT_EQ(value, values[index]);
index++;
return true;
});
}