From 69dc58eaa5101633201d95f63c4438726d6bbd3f Mon Sep 17 00:00:00 2001 From: Damien Mehala Date: Thu, 4 Sep 2025 13:18:27 +0200 Subject: [PATCH] fix(rc): handle null values in RC APM Tracing payloads Ensures system-tests pass by properly ignoring null values APM Tracing RC payload. --- src/datadog/config_manager.cpp | 10 ++- test/test_config_manager.cpp | 137 +++++++++++++++++++++++++++++---- 2 files changed, 129 insertions(+), 18 deletions(-) diff --git a/src/datadog/config_manager.cpp b/src/datadog/config_manager.cpp index 59500994..ce6c51b9 100644 --- a/src/datadog/config_manager.cpp +++ b/src/datadog/config_manager.cpp @@ -221,7 +221,7 @@ Expected parse_dynamic_config(const nlohmann::json& j) { ConfigManager::Update config_update; if (auto sampling_rate_it = j.find("tracing_sampling_rate"); - sampling_rate_it != j.cend()) { + sampling_rate_it != j.cend() && !sampling_rate_it->is_null()) { if (!sampling_rate_it->is_number_float()) { return make_err_property_msg("tracing_sampling_rate", sampling_rate_it->type_name()); @@ -236,7 +236,8 @@ Expected parse_dynamic_config(const nlohmann::json& j) { config_update.trace_sampling_rate = *maybe_rate; } - if (auto tags_it = j.find("tracing_tags"); tags_it != j.cend()) { + if (auto tags_it = j.find("tracing_tags"); + tags_it != j.cend() && !tags_it->is_null()) { if (!tags_it->is_array()) { return make_err_property_msg("tracing_tags", tags_it->type_name()); } @@ -250,7 +251,7 @@ Expected parse_dynamic_config(const nlohmann::json& j) { } if (auto tracing_enabled_it = j.find("tracing_enabled"); - tracing_enabled_it != j.cend()) { + tracing_enabled_it != j.cend() && !tracing_enabled_it->is_null()) { if (!tracing_enabled_it->is_boolean()) { return make_err_property_msg("tracing_enabled", tracing_enabled_it->type_name()); @@ -260,7 +261,8 @@ Expected parse_dynamic_config(const nlohmann::json& j) { } if (auto tracing_sampling_rules_it = j.find("tracing_sampling_rules"); - tracing_sampling_rules_it != j.cend()) { + tracing_sampling_rules_it != j.cend() && + !tracing_sampling_rules_it->is_null()) { if (!tracing_sampling_rules_it->is_array()) { return make_err_property_msg("tracing_sampling_rules", tracing_sampling_rules_it->type_name()); diff --git a/test/test_config_manager.cpp b/test/test_config_manager.cpp index 9c67133c..ef5171e5 100644 --- a/test/test_config_manager.cpp +++ b/test/test_config_manager.cpp @@ -109,10 +109,31 @@ CONFIG_MANAGER_TEST("remote configuration handling") { } SECTION( - "an RC payload without the `tracing_sampling_rate` does not raise an " - "error nor update the sampling rate") { - config_update.content = R"({ + "an RC payload without the `tracing_sampling_rate` or with a null " + "value does not raise an error nor update the sampling rate") { + struct TestCase { + size_t line; + std::string_view name; + std::string_view input; + }; + + const auto test_case = GENERATE(values({ + { + __LINE__, + "tracing_sampling_rate is missing", + "", + }, + { + __LINE__, + "tracing_sampling_rate is null", + R"("tracing_sampling_rate": null,)", + }, + })); + + char payload[1024]; + std::snprintf(payload, 1024, R"({ "lib_config": { + %s "library_language": "all", "library_version": "latest", "service_name": "testsvc", @@ -122,7 +143,13 @@ CONFIG_MANAGER_TEST("remote configuration handling") { "service": "testsvc", "env": "test" } - })"; + })", + test_case.input.data()); + + config_update.content = payload; + + CAPTURE(test_case.line); + CAPTURE(test_case.name); const auto old_trace_sampler_config = config_manager.trace_sampler()->config_json(); @@ -232,10 +259,35 @@ CONFIG_MANAGER_TEST("remote configuration handling") { } SECTION( - "payload without `tracing_tags` does not raise an error nor update the " + "payload without `tracing_tags` or with a null value does not raise an " + "error nor update the " "list of tags") { - config_update.content = R"({ + struct TestCase { + size_t line; + std::string_view name; + std::string_view input; + }; + + const auto test_case = GENERATE(values({ + { + __LINE__, + "tracing_tags is missing", + "", + }, + { + __LINE__, + "tracing_tags is null", + R"("tracing_tags": null,)", + }, + })); + + CAPTURE(test_case.line); + CAPTURE(test_case.name); + + char payload[1024]; + std::snprintf(payload, 1024, R"({ "lib_config": { + %s "library_language": "all", "library_version": "latest", "service_name": "testsvc", @@ -245,7 +297,10 @@ CONFIG_MANAGER_TEST("remote configuration handling") { "service": "testsvc", "env": "test" } - })"; + })", + test_case.input.data()); + + config_update.content = payload; const auto old_tags = config_manager.span_defaults()->tags; @@ -357,10 +412,34 @@ CONFIG_MANAGER_TEST("remote configuration handling") { } SECTION( - "An RC payload without `tracing_enabled` does not raise an error nor " - "update the value") { - config_update.content = R"({ + "An RC payload without `tracing_enabled` or with a null value does not " + "raise an error nor update the value") { + struct TestCase { + size_t line; + std::string_view name; + std::string_view input; + }; + + const auto test_case = GENERATE(values({ + { + __LINE__, + "tracing_enabled is absent from the RC payload", + "", + }, + { + __LINE__, + "tracing_enabled is null", + R"("tracing_enabled": null,)", + }, + })); + + CAPTURE(test_case.line); + CAPTURE(test_case.name); + + char payload[1024]; + std::snprintf(payload, 1024, R"({ "lib_config": { + %s "library_language": "all", "library_version": "latest", "service_name": "testsvc", @@ -370,7 +449,10 @@ CONFIG_MANAGER_TEST("remote configuration handling") { "service": "testsvc", "env": "test" } - })"; + })", + test_case.input.data()); + + config_update.content = payload; const auto old_tracing_status = config_manager.report_traces(); @@ -526,9 +608,33 @@ CONFIG_MANAGER_TEST("remote configuration handling") { CHECK(old_sampler_cfg == new_sampler_cfg); } - SECTION("empty") { - config_update.content = R"({ + SECTION("null value or the absence of the field is ignored") { + struct TestCase { + size_t line; + std::string_view name; + std::string_view input; + }; + + const auto test_case = GENERATE(values({ + { + __LINE__, + "tracing_sampling_rules is absent from the RC payload", + "", + }, + { + __LINE__, + "tracing_sampling_rules is null", + R"("tracing_sampling_rules": null,)", + }, + })); + + CAPTURE(test_case.line); + CAPTURE(test_case.name); + + char payload[1024]; + std::snprintf(payload, 1024, R"({ "lib_config": { + %s "library_language": "all", "library_version": "latest", "service_name": "testsvc", @@ -538,7 +644,10 @@ CONFIG_MANAGER_TEST("remote configuration handling") { "service": "testsvc", "env": "test" } - })"; + })", + test_case.input.data()); + + config_update.content = payload; const auto old_sampler_cfg = config_manager.trace_sampler()->config_json();