99
1010from .conftest import StableConfigWriter , _TestAgentAPI
1111from utils .telemetry_utils import TelemetryUtils
12- from utils import context , scenarios , rfc , features , missing_feature , irrelevant , logger
12+ from utils import context , scenarios , rfc , features , missing_feature , irrelevant , logger , bug
1313
1414
1515telemetry_name_mapping = {
8282 "ruby" : "DD_TRACE_DEBUG" ,
8383 "python" : "DD_TRACE_DEBUG" ,
8484 },
85+ "tags" : {
86+ "java" : "trace_tags" ,
87+ "dotnet" : "DD_TAGS" ,
88+ "python" : "DD_TAGS" ,
89+ },
90+ "trace_propagation_style" : {
91+ "dotnet" : "DD_TRACE_PROPAGATION_STYLE" ,
92+ "php" : "trace.propagation_style" ,
93+ },
8594}
8695
8796
@@ -105,6 +114,48 @@ def _find_configuration_by_origin(config_list: list[dict], origin: str) -> dict
105114 return None
106115
107116
117+ def _check_propagation_style_with_inject_and_extract (
118+ test_agent , configuration_by_name : dict , expected_origin : str , library_name : str
119+ ) -> None :
120+ """Check both inject and extract propagation style keys for languages that report them separately.
121+
122+ Some libraries report propagation style using separate inject and extract keys
123+ instead of a single combined key. This function validates that both keys exist with the
124+ expected origin and have non-empty values.
125+
126+ Raises an AssertionError if either key is missing, has wrong origin, or has empty value
127+ """
128+ # Define the inject and extract key names for each language
129+ if library_name == "python" :
130+ inject_key = "DD_TRACE_PROPAGATION_STYLE_INJECT"
131+ extract_key = "DD_TRACE_PROPAGATION_STYLE_EXTRACT"
132+ elif library_name == "ruby" :
133+ inject_key = "tracing.propagation_style_inject"
134+ extract_key = "tracing.propagation_style_extract"
135+ else :
136+ raise ValueError (f"Unsupported library for inject/extract propagation style: { library_name } " )
137+
138+ # Check inject key
139+ inject_item = test_agent .get_telemetry_config_by_origin (configuration_by_name , inject_key , expected_origin )
140+ assert (
141+ inject_item is not None
142+ ), f"No configuration found for '{ inject_key } ' with origin '{ expected_origin } '. Full configuration_by_name: { configuration_by_name } "
143+ assert (
144+ inject_item ["origin" ] == expected_origin
145+ ), f"Origin mismatch for { inject_item } . Expected origin: '{ expected_origin } ', Actual origin: '{ inject_item .get ('origin' , '<missing>' )} '"
146+ assert inject_item ["value" ], f"Expected non-empty value for '{ inject_key } '"
147+
148+ # Check extract key
149+ extract_item = test_agent .get_telemetry_config_by_origin (configuration_by_name , extract_key , expected_origin )
150+ assert (
151+ extract_item is not None
152+ ), f"No configuration found for '{ extract_key } ' with origin '{ expected_origin } '. Full configuration_by_name: { configuration_by_name } "
153+ assert (
154+ extract_item ["origin" ] == expected_origin
155+ ), f"Origin mismatch for { extract_item } . Expected origin: '{ expected_origin } ', Actual origin: '{ extract_item .get ('origin' , '<missing>' )} '"
156+ assert extract_item ["value" ], f"Expected non-empty value for '{ extract_key } '"
157+
158+
108159@scenarios .parametric
109160@rfc ("https://docs.google.com/document/d/1In4TfVBbKEztLzYg4g0si5H56uzAbYB3OfqzRGP2xhg/edit" )
110161@features .telemetry_app_started_event
@@ -690,6 +741,142 @@ def test_stable_configuration_config_id(
690741 assert telemetry_item ["origin" ] == "local_stable_config"
691742 assert "config_id" not in telemetry_item or telemetry_item ["config_id" ] is None
692743
744+ @pytest .mark .parametrize (
745+ ("local_cfg" , "library_env" , "fleet_cfg" , "expected_origins" ),
746+ [
747+ (
748+ {
749+ "DD_TRACE_PROPAGATION_STYLE" : "tracecontext" ,
750+ "DD_TAGS" : "tag1:value1,tag2:value2" ,
751+ },
752+ {
753+ "DD_TELEMETRY_HEARTBEAT_INTERVAL" : "0.1" , # Decrease the heartbeat/poll intervals to speed up the tests
754+ },
755+ {
756+ "DD_TRACE_PROPAGATION_STYLE" : "datadog" ,
757+ },
758+ {
759+ "tags" : "local_stable_config" ,
760+ "trace_propagation_style" : "fleet_stable_config" ,
761+ },
762+ )
763+ ],
764+ )
765+ @missing_feature (
766+ context .library in ["cpp" , "golang" , "nodejs" ],
767+ reason = "extended configs are not supported" ,
768+ )
769+ @bug (context .library == "python" , reason = "APMAPI-1630" )
770+ @bug (context .library == "ruby" , reason = "APMAPI-1631" )
771+ def test_stable_configuration_origin_extended_configs_good_use_case (
772+ self , local_cfg , library_env , fleet_cfg , test_agent , test_library , expected_origins
773+ ):
774+ """Test that extended configuration options (tags, propagation style) report their origin correctly.
775+
776+ This test verifies that complex configuration values like tag arrays and propagation
777+ styles are properly tracked with their configuration origin (local vs fleet stable config).
778+ """
779+ with test_library :
780+ self .write_stable_config (
781+ {
782+ "apm_configuration_default" : local_cfg ,
783+ },
784+ "/etc/datadog-agent/application_monitoring.yaml" ,
785+ test_library ,
786+ )
787+ self .write_stable_config (
788+ {
789+ "apm_configuration_default" : fleet_cfg ,
790+ },
791+ "/etc/datadog-agent/managed/datadog-agent/stable/application_monitoring.yaml" ,
792+ test_library ,
793+ )
794+ # Sleep between telemetry events to ensure they are recorded with different timestamps, to later reorder them.
795+ # seq_id can't be used to sort because payloads are sent from different tracer sessions.
796+ time .sleep (1 )
797+ test_library .container_restart ()
798+ test_library .dd_start_span ("test" )
799+ configuration_by_name = test_agent .wait_for_telemetry_configurations ()
800+ for cfg_name , expected_origin in expected_origins .items ():
801+ apm_telemetry_name = _mapped_telemetry_name (context , cfg_name )
802+ telemetry_item = test_agent .get_telemetry_config_by_origin (
803+ configuration_by_name , apm_telemetry_name , expected_origin
804+ )
805+ assert (
806+ telemetry_item is not None
807+ ), f"No configuration found for '{ apm_telemetry_name } ' with origin '{ expected_origin } '. Full configuration_by_name: { configuration_by_name } "
808+
809+ actual_origin = telemetry_item .get ("origin" , "<missing>" )
810+ assert (
811+ telemetry_item ["origin" ] == expected_origin
812+ ), f"Origin mismatch for { telemetry_item } . Expected origin: '{ expected_origin } ', Actual origin: '{ actual_origin } '"
813+ assert telemetry_item ["value" ]
814+
815+ @pytest .mark .parametrize (
816+ ("local_cfg" , "library_env" , "fleet_cfg" , "expected_origins" ),
817+ [
818+ (
819+ {
820+ "DD_TRACE_PROPAGATION_STYLE" : "tracecontext" ,
821+ "DD_TAGS" : "tag1:value1,tag2:value2" ,
822+ },
823+ {
824+ "DD_TELEMETRY_HEARTBEAT_INTERVAL" : "0.1" , # Decrease the heartbeat/poll intervals to speed up the tests
825+ },
826+ {
827+ "DD_TRACE_PROPAGATION_STYLE" : "datadog" ,
828+ },
829+ {
830+ "tags" : "local_stable_config" ,
831+ "trace_propagation_style" : "fleet_stable_config" ,
832+ },
833+ )
834+ ],
835+ )
836+ @missing_feature (
837+ context .library in ["cpp" , "golang" , "nodejs" ],
838+ reason = "extended configs are not supported" ,
839+ )
840+ @bug (context .library == "ruby" , reason = "APMAPI-1650" )
841+ @irrelevant (context .library in ["java" , "php" , "dotnet" ], reason = "temporary use case for python and ruby" )
842+ def test_stable_configuration_origin_extended_configs_temporary_use_case (
843+ self , local_cfg , library_env , fleet_cfg , test_agent , test_library , expected_origins
844+ ):
845+ """Test that extended configuration options (tags, propagation style) report their origin correctly.
846+
847+ This test verifies that complex configuration values like tag arrays and propagation
848+ styles are properly tracked with their configuration origin (local vs fleet stable config).
849+ """
850+ with test_library :
851+ self .write_stable_config (
852+ {
853+ "apm_configuration_default" : local_cfg ,
854+ },
855+ "/etc/datadog-agent/application_monitoring.yaml" ,
856+ test_library ,
857+ )
858+ self .write_stable_config (
859+ {
860+ "apm_configuration_default" : fleet_cfg ,
861+ },
862+ "/etc/datadog-agent/managed/datadog-agent/stable/application_monitoring.yaml" ,
863+ test_library ,
864+ )
865+ # Sleep between telemetry events to ensure they are recorded with different timestamps, to later reorder them.
866+ # seq_id can't be used to sort because payloads are sent from different tracer sessions.
867+ time .sleep (1 )
868+ test_library .container_restart ()
869+ test_library .dd_start_span ("test" )
870+ configuration_by_name = test_agent .wait_for_telemetry_configurations ()
871+ for cfg_name , expected_origin in expected_origins .items ():
872+ # Python and Ruby only report inject and extract keys for trace_propagation_style
873+ if cfg_name == "trace_propagation_style" and context .library .name in ["python" , "ruby" ]:
874+ _check_propagation_style_with_inject_and_extract (
875+ test_agent , configuration_by_name , expected_origin , context .library .name
876+ )
877+ if cfg_name == "tags" and context .library .name in ["ruby" ]:
878+ continue
879+
693880
694881DEFAULT_ENVVARS = {
695882 # Decrease the heartbeat/poll intervals to speed up the tests
0 commit comments