From 6ac0ef034018a50031f0d285116e361e35755951 Mon Sep 17 00:00:00 2001 From: Vickie Boettcher Date: Fri, 23 Oct 2026 00:07:54 -0400 Subject: [PATCH 1/4] test(ffe): verify SDK identity through the Agent Assert producer headers at intake and reject event-level source while preserving existing payload checks and capability gates. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 --- manifests/agent.yml | 3 + manifests/golang.yml | 1 + manifests/python.yml | 1 + tests/ffe/README.md | 9 +++ tests/ffe/test_flag_eval_evp.py | 22 ++++++ tests/ffe/utils/evp.py | 17 +++-- tests/test_the_test/test_ffe_tests.py | 104 +++++++++++++++++++++++--- 7 files changed, 140 insertions(+), 17 deletions(-) diff --git a/manifests/agent.yml b/manifests/agent.yml index b1ee51a8645..7faba7fff26 100644 --- a/manifests/agent.yml +++ b/manifests/agent.yml @@ -46,6 +46,9 @@ manifest: - declaration: bug (DEBUG-5421) component_version: ">=7.79.0-devel <7.84.4" weblog: [chi, echo, fiber-v2-orchestrion, gin, net-http, net-http-orchestrion, uds-echo] + tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_SDK_Identity: + - declaration: missing_feature (Agent forwarding of EVP producer identity requires 7.84) + component_version: '<7.84.0' tests/integrations/test_open_telemetry.py::Test_MsSql::test_error_exception_event: - component_version: "<7.75.0-0" declaration: irrelevant (new behavior implement in 7.75.0) diff --git a/manifests/golang.yml b/manifests/golang.yml index d4b4e0e1934..2e9213f11d2 100644 --- a/manifests/golang.yml +++ b/manifests/golang.yml @@ -1766,6 +1766,7 @@ manifest: tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_ObserveFullData_Absent_Hashed: v2.11.0-dev tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_ObserveFullData_False_Hashed: v2.11.0-dev tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_ObserveFullData_True_Unhashed: v2.11.0-dev # Easy win for chi, echo, gin, net-http, net-http-orchestrion, uds-echo and version 2.10.0 + tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_SDK_Identity: v2.11.0 tests/ffe/test_flag_eval_metrics.py: v2.8.0 tests/ffe/test_flag_eval_metrics.py::Test_FFE_Eval_Metric_Parse_Error_Invalid_Regex: irrelevant (Go validates regex at config load time) tests/ffe/test_flag_eval_metrics.py::Test_FFE_Eval_Nested_Attributes_Ignored: irrelevant (FFL-1980) diff --git a/manifests/python.yml b/manifests/python.yml index f4bfd171bad..a236bc5aabd 100644 --- a/manifests/python.yml +++ b/manifests/python.yml @@ -2271,6 +2271,7 @@ manifest: tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_ObserveFullData_False_Hashed: missing_feature (FFL-2446) tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_ObserveFullData_True_Unhashed: missing_feature (FFL-2446) tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_Runtime_Default: missing_feature (FFL-2446) + tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_SDK_Identity: missing_feature (FFL-2446) tests/ffe/test_flag_eval_metrics.py: v4.7.0 tests/ffe/test_flag_eval_metrics.py::Test_FFE_Eval_Nested_Attributes_Ignored: irrelevant (FFL-1980) tests/integration_frameworks: diff --git a/tests/ffe/README.md b/tests/ffe/README.md index b1d8151715a..b0c1885a660 100644 --- a/tests/ffe/README.md +++ b/tests/ffe/README.md @@ -18,6 +18,15 @@ This directory contains system tests for the Feature Flags & Experimentation (FF ./run.sh FEATURE_FLAGGING_AND_EXPERIMENTATION --library ``` +## SDK identity through the Agent + +`Test_FFE_EVP_Flagevaluation_SDK_Identity` checks SDK origin and version headers on +Agent-to-intake captures while retaining the shared payload and evaluation-count checks. +The new case runs alongside the existing Go and Node.js express5 egress coverage, +with additional gates for Agent 7.84+ and Go 2.11.0+. Other languages retain their +current exclusions; enabling them remains separate. Event-level `source` is rejected: +producer identity belongs in the request headers. + ## Test-contract self-tests The self-tests in `tests/test_the_test/test_ffe_tests.py` validate the FFE assertions using synthetic diff --git a/tests/ffe/test_flag_eval_evp.py b/tests/ffe/test_flag_eval_evp.py index bcf93e46a17..9c671161dab 100644 --- a/tests/ffe/test_flag_eval_evp.py +++ b/tests/ffe/test_flag_eval_evp.py @@ -9,6 +9,7 @@ from tests.ffe.utils.evp import ( assert_agentless_evp_intake_request, assert_agentless_evp_topology, + assert_evp_sdk_identity, feature_flagging_evp_egress, register_expected_evp_capture, ) @@ -163,6 +164,7 @@ def assert_batch_context(batch: JSON) -> None: def assert_event_contract(event: JSON, flag_key: str) -> None: assert_no_reason_field(event) + assert "source" not in event, "SDK identity belongs in EVP headers, not event-level source" assert "targeting_rule" not in event, "targeting_rule must be omitted without real rule metadata" flag = event.get("flag") @@ -322,6 +324,26 @@ class Test_FFE_EVP_Flagevaluation_Egress_Datadog_Agent(FlagevaluationEgressContr pass +@scenarios.feature_flagging_and_experimentation +@features.feature_flags_evp_flagevaluation +class Test_FFE_EVP_Flagevaluation_SDK_Identity(FlagevaluationEgressContract): + # Agent 7.84+ forwards producer headers. The Agent manifest gates this case; + # library manifests retain their existing capability gates (Go additionally + # requires the 2.11.0 release). Older Agents keep payload coverage. + flag_key = "evp-sdk-identity-flag" + + def test_ffe_evp_flagevaluation_egress(self) -> None: + super().test_ffe_evp_flagevaluation_egress() + # interfaces.agent captures Agent-to-intake traffic, after forwarding. + for data in interfaces.agent.get_data(path_filters=EVP_FLAGEVALUATIONS_PATH): + if evp_flagevaluation_events_from_data(cast("JSON", data), self.flag_key, self.targeting_key): + assert_evp_sdk_identity( + cast("JSON", data), + library_name=context.library.name, + library_version=context.library.raw_version, + ) + + @scenario_crash @scenarios.feature_flagging_and_experimentation_agentless_direct @features.feature_flags_evp_flagevaluation diff --git a/tests/ffe/utils/evp.py b/tests/ffe/utils/evp.py index cae88dadab6..912a4708906 100644 --- a/tests/ffe/utils/evp.py +++ b/tests/ffe/utils/evp.py @@ -44,6 +44,7 @@ "golang": "dd-trace-go", "java": "dd-trace-java", "nodejs": "dd-trace-js", + "php": "dd-trace-php", "python": "dd-trace-py", "ruby": "dd-trace-rb", } @@ -134,7 +135,7 @@ def expected_evp_origin(library_name: str) -> str: def expected_evp_origin_version(library_name: str, library_version: str) -> str: - """Return the tracer's canonical wire version for direct EVP identity. + """Return the tracer's canonical wire version for EVP identity. System-tests deliberately removes Go's leading ``v`` when it parses the component version for manifest comparisons. dd-trace-go's public version @@ -161,6 +162,13 @@ def _assert_single_header(data: dict[str, Any], name: str, expected: str | set[s assert values[0] in allowed, f"expected {name} in {sorted(allowed)!r}, got {values[0]!r}" +def assert_evp_sdk_identity(data: dict[str, Any], *, library_name: str, library_version: str) -> None: + """Require the SDK producer identity on an intake-side capture.""" + _assert_single_header(data, "DD-EVP-ORIGIN", expected_evp_origin(library_name)) + assert library_version.strip(), "expected SDK version must be non-empty" + _assert_single_header(data, "DD-EVP-ORIGIN-VERSION", expected_evp_origin_version(library_name, library_version)) + + def assert_agentless_evp_intake_request( data: dict[str, Any], *, @@ -197,12 +205,7 @@ def assert_agentless_evp_intake_request( # SDK local-route identity and credential omission remain covered by SDK unit tests. return - _assert_single_header(data, "DD-EVP-ORIGIN", expected_evp_origin(library_name)) - _assert_single_header( - data, - "DD-EVP-ORIGIN-VERSION", - expected_evp_origin_version(library_name, library_version), - ) + assert_evp_sdk_identity(data, library_name=library_name, library_version=library_version) def assert_no_evp_proxy_requests(captures: Iterable[dict[str, Any]]) -> None: diff --git a/tests/test_the_test/test_ffe_tests.py b/tests/test_the_test/test_ffe_tests.py index 71a67fc439e..99a437c41ec 100644 --- a/tests/test_the_test/test_ffe_tests.py +++ b/tests/test_the_test/test_ffe_tests.py @@ -43,7 +43,80 @@ def test_flagevaluation_egress_validates_captured_data_without_waiting( ) -> None: contract = flag_eval_evp_tests.FlagevaluationEgressContract() contract.responses = [MagicMock(status_code=200)] * contract.evaluation_count - capture = { + capture = _flagevaluation_capture(contract) + interface = MagicMock() + interface.get_data.return_value = [capture] if has_capture else [] + interface.wait_for.side_effect = AssertionError("Validation must not wait after containers have stopped") + monkeypatch.setattr( + flag_eval_evp_tests, "feature_flagging_evp_egress", lambda: FeatureFlaggingEVPEgress(interface, "agent") + ) + + if has_capture: + contract.test_ffe_evp_flagevaluation_egress() + else: + with pytest.raises(AssertionError, match="Expected flagevaluation requests"): + contract.test_ffe_evp_flagevaluation_egress() + interface.wait_for.assert_not_called() + + +@pytest.mark.parametrize( + ("library_name", "origin", "version", "wire_version"), + [ + ("dotnet", "dd-trace-dotnet", "3.57.0", "3.57.0"), + ("golang", "dd-trace-go", "2.11.2", "v2.11.2"), + ("java", "dd-trace-java", "1.66.0", "1.66.0"), + ("nodejs", "dd-trace-js", "6.20.0", "6.20.0"), + ("php", "dd-trace-php", "1.27.0", "1.27.0"), + ("python", "dd-trace-py", "4.16.0", "4.16.0"), + ("ruby", "dd-trace-rb", "2.42.0", "2.42.0"), + ], +) +@scenarios.test_the_test +def test_flagevaluation_agent_sdk_identity_accepts_producer_headers( + monkeypatch: pytest.MonkeyPatch, library_name: str, origin: str, version: str, wire_version: str +) -> None: + # Synthetic identities exercise the shared assertion, not SDK release support. + _check_agent_sdk_identity( + monkeypatch, + library_name=library_name, + version=version, + headers=[["dd-evp-origin", origin], ["Dd-Evp-Origin-Version", wire_version]], + ) + + +@pytest.mark.parametrize( + ("headers", "error"), + [ + ([["DD-EVP-ORIGIN-VERSION", "6.20.0"]], "DD-EVP-ORIGIN"), + ([["DD-EVP-ORIGIN", "dd-trace-js"]], "DD-EVP-ORIGIN-VERSION"), + ([["DD-EVP-ORIGIN", ""], ["DD-EVP-ORIGIN-VERSION", "6.20.0"]], "DD-EVP-ORIGIN"), + ([["DD-EVP-ORIGIN", "trace-agent"], ["DD-EVP-ORIGIN-VERSION", "6.20.0"]], "DD-EVP-ORIGIN"), + ([["DD-EVP-ORIGIN", "dd-trace-js"], ["DD-EVP-ORIGIN-VERSION", " "]], "DD-EVP-ORIGIN-VERSION"), + ([["DD-EVP-ORIGIN", "dd-trace-js"], ["DD-EVP-ORIGIN-VERSION", "7.84.2"]], "DD-EVP-ORIGIN-VERSION"), + ], +) +@scenarios.test_the_test +def test_flagevaluation_agent_sdk_identity_rejects_invalid_headers( + monkeypatch: pytest.MonkeyPatch, headers: list[list[str]], error: str +) -> None: + with pytest.raises(AssertionError, match=error): + _check_agent_sdk_identity(monkeypatch, library_name="nodejs", version="6.20.0", headers=headers) + + +@scenarios.test_the_test +def test_flagevaluation_agent_sdk_identity_rejects_event_source(monkeypatch: pytest.MonkeyPatch) -> None: + with pytest.raises(AssertionError, match="event-level source"): + _check_agent_sdk_identity( + monkeypatch, + library_name="nodejs", + version="6.20.0", + headers=[["DD-EVP-ORIGIN", "dd-trace-js"], ["DD-EVP-ORIGIN-VERSION", "6.20.0"]], + event_source=True, + ) + + +def _flagevaluation_capture(contract: flag_eval_evp_tests.FlagevaluationEgressContract) -> dict[str, Any]: + return { "path": flag_eval_evp_tests.EVP_FLAGEVALUATIONS_PATH, "request": { "content": { @@ -62,19 +135,30 @@ def test_flagevaluation_egress_validates_captured_data_without_waiting( } }, } + + +def _check_agent_sdk_identity( + monkeypatch: pytest.MonkeyPatch, + *, + library_name: str, + version: str, + headers: list[list[str]], + event_source: bool = False, +) -> None: + contract = flag_eval_evp_tests.Test_FFE_EVP_Flagevaluation_SDK_Identity() + contract.responses = [MagicMock(status_code=200)] * contract.evaluation_count + capture = _flagevaluation_capture(contract) + capture["request"]["headers"] = headers + if event_source: + capture["request"]["content"]["flagEvaluations"][0]["source"] = {"name": "dd-trace-js"} interface = MagicMock() - interface.get_data.return_value = [capture] if has_capture else [] - interface.wait_for.side_effect = AssertionError("Validation must not wait after containers have stopped") + interface.get_data.return_value = [capture] monkeypatch.setattr( flag_eval_evp_tests, "feature_flagging_evp_egress", lambda: FeatureFlaggingEVPEgress(interface, "agent") ) - - if has_capture: - contract.test_ffe_evp_flagevaluation_egress() - else: - with pytest.raises(AssertionError, match="Expected flagevaluation requests"): - contract.test_ffe_evp_flagevaluation_egress() - interface.wait_for.assert_not_called() + monkeypatch.setattr(interfaces, "agent", interface) + monkeypatch.setattr(flag_eval_evp_tests, "context", MagicMock(library=ComponentVersion(library_name, version))) + contract.test_ffe_evp_flagevaluation_egress() def _direct_runtime_evidence(library_name: str = "nodejs") -> dict[str, Any]: From db79524668362c29e7b27a989855623a792a16f8 Mon Sep 17 00:00:00 2001 From: Vickie Boettcher Date: Fri, 23 Oct 2026 18:02:14 +0000 Subject: [PATCH 2/4] test(ffe): remove identity assertion self-test changes Restore the existing self-test file following maintainer feedback. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 --- tests/test_the_test/test_ffe_tests.py | 104 +++----------------------- 1 file changed, 10 insertions(+), 94 deletions(-) diff --git a/tests/test_the_test/test_ffe_tests.py b/tests/test_the_test/test_ffe_tests.py index 99a437c41ec..71a67fc439e 100644 --- a/tests/test_the_test/test_ffe_tests.py +++ b/tests/test_the_test/test_ffe_tests.py @@ -43,80 +43,7 @@ def test_flagevaluation_egress_validates_captured_data_without_waiting( ) -> None: contract = flag_eval_evp_tests.FlagevaluationEgressContract() contract.responses = [MagicMock(status_code=200)] * contract.evaluation_count - capture = _flagevaluation_capture(contract) - interface = MagicMock() - interface.get_data.return_value = [capture] if has_capture else [] - interface.wait_for.side_effect = AssertionError("Validation must not wait after containers have stopped") - monkeypatch.setattr( - flag_eval_evp_tests, "feature_flagging_evp_egress", lambda: FeatureFlaggingEVPEgress(interface, "agent") - ) - - if has_capture: - contract.test_ffe_evp_flagevaluation_egress() - else: - with pytest.raises(AssertionError, match="Expected flagevaluation requests"): - contract.test_ffe_evp_flagevaluation_egress() - interface.wait_for.assert_not_called() - - -@pytest.mark.parametrize( - ("library_name", "origin", "version", "wire_version"), - [ - ("dotnet", "dd-trace-dotnet", "3.57.0", "3.57.0"), - ("golang", "dd-trace-go", "2.11.2", "v2.11.2"), - ("java", "dd-trace-java", "1.66.0", "1.66.0"), - ("nodejs", "dd-trace-js", "6.20.0", "6.20.0"), - ("php", "dd-trace-php", "1.27.0", "1.27.0"), - ("python", "dd-trace-py", "4.16.0", "4.16.0"), - ("ruby", "dd-trace-rb", "2.42.0", "2.42.0"), - ], -) -@scenarios.test_the_test -def test_flagevaluation_agent_sdk_identity_accepts_producer_headers( - monkeypatch: pytest.MonkeyPatch, library_name: str, origin: str, version: str, wire_version: str -) -> None: - # Synthetic identities exercise the shared assertion, not SDK release support. - _check_agent_sdk_identity( - monkeypatch, - library_name=library_name, - version=version, - headers=[["dd-evp-origin", origin], ["Dd-Evp-Origin-Version", wire_version]], - ) - - -@pytest.mark.parametrize( - ("headers", "error"), - [ - ([["DD-EVP-ORIGIN-VERSION", "6.20.0"]], "DD-EVP-ORIGIN"), - ([["DD-EVP-ORIGIN", "dd-trace-js"]], "DD-EVP-ORIGIN-VERSION"), - ([["DD-EVP-ORIGIN", ""], ["DD-EVP-ORIGIN-VERSION", "6.20.0"]], "DD-EVP-ORIGIN"), - ([["DD-EVP-ORIGIN", "trace-agent"], ["DD-EVP-ORIGIN-VERSION", "6.20.0"]], "DD-EVP-ORIGIN"), - ([["DD-EVP-ORIGIN", "dd-trace-js"], ["DD-EVP-ORIGIN-VERSION", " "]], "DD-EVP-ORIGIN-VERSION"), - ([["DD-EVP-ORIGIN", "dd-trace-js"], ["DD-EVP-ORIGIN-VERSION", "7.84.2"]], "DD-EVP-ORIGIN-VERSION"), - ], -) -@scenarios.test_the_test -def test_flagevaluation_agent_sdk_identity_rejects_invalid_headers( - monkeypatch: pytest.MonkeyPatch, headers: list[list[str]], error: str -) -> None: - with pytest.raises(AssertionError, match=error): - _check_agent_sdk_identity(monkeypatch, library_name="nodejs", version="6.20.0", headers=headers) - - -@scenarios.test_the_test -def test_flagevaluation_agent_sdk_identity_rejects_event_source(monkeypatch: pytest.MonkeyPatch) -> None: - with pytest.raises(AssertionError, match="event-level source"): - _check_agent_sdk_identity( - monkeypatch, - library_name="nodejs", - version="6.20.0", - headers=[["DD-EVP-ORIGIN", "dd-trace-js"], ["DD-EVP-ORIGIN-VERSION", "6.20.0"]], - event_source=True, - ) - - -def _flagevaluation_capture(contract: flag_eval_evp_tests.FlagevaluationEgressContract) -> dict[str, Any]: - return { + capture = { "path": flag_eval_evp_tests.EVP_FLAGEVALUATIONS_PATH, "request": { "content": { @@ -135,30 +62,19 @@ def _flagevaluation_capture(contract: flag_eval_evp_tests.FlagevaluationEgressCo } }, } - - -def _check_agent_sdk_identity( - monkeypatch: pytest.MonkeyPatch, - *, - library_name: str, - version: str, - headers: list[list[str]], - event_source: bool = False, -) -> None: - contract = flag_eval_evp_tests.Test_FFE_EVP_Flagevaluation_SDK_Identity() - contract.responses = [MagicMock(status_code=200)] * contract.evaluation_count - capture = _flagevaluation_capture(contract) - capture["request"]["headers"] = headers - if event_source: - capture["request"]["content"]["flagEvaluations"][0]["source"] = {"name": "dd-trace-js"} interface = MagicMock() - interface.get_data.return_value = [capture] + interface.get_data.return_value = [capture] if has_capture else [] + interface.wait_for.side_effect = AssertionError("Validation must not wait after containers have stopped") monkeypatch.setattr( flag_eval_evp_tests, "feature_flagging_evp_egress", lambda: FeatureFlaggingEVPEgress(interface, "agent") ) - monkeypatch.setattr(interfaces, "agent", interface) - monkeypatch.setattr(flag_eval_evp_tests, "context", MagicMock(library=ComponentVersion(library_name, version))) - contract.test_ffe_evp_flagevaluation_egress() + + if has_capture: + contract.test_ffe_evp_flagevaluation_egress() + else: + with pytest.raises(AssertionError, match="Expected flagevaluation requests"): + contract.test_ffe_evp_flagevaluation_egress() + interface.wait_for.assert_not_called() def _direct_runtime_evidence(library_name: str = "nodejs") -> dict[str, Any]: From c04c6a8e13ddcec5ba177fca29e1e4a2cfe7e84a Mon Sep 17 00:00:00 2001 From: Vickie Boettcher Date: Fri, 23 Oct 2026 18:34:30 +0000 Subject: [PATCH 3/4] test(ffe): enable Python SDK identity coverage Enable the validated Python 4.16.0 release and name identity parameters source_name and source_version. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 --- manifests/python.yml | 2 +- tests/ffe/README.md | 8 ++++---- tests/ffe/test_flag_eval_evp.py | 4 ++-- tests/ffe/utils/evp.py | 12 ++++++------ 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/manifests/python.yml b/manifests/python.yml index e04b172f314..38fa47fa6a5 100644 --- a/manifests/python.yml +++ b/manifests/python.yml @@ -2286,7 +2286,7 @@ manifest: tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_ObserveFullData_False_Hashed: missing_feature (FFL-2446) tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_ObserveFullData_True_Unhashed: missing_feature (FFL-2446) tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_Runtime_Default: missing_feature (FFL-2446) - tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_SDK_Identity: missing_feature (FFL-2446) + tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_SDK_Identity: v4.16.0 tests/ffe/test_flag_eval_metrics.py: v4.7.0 tests/ffe/test_flag_eval_metrics.py::Test_FFE_Eval_Nested_Attributes_Ignored: irrelevant (FFL-1980) tests/integration_frameworks: diff --git a/tests/ffe/README.md b/tests/ffe/README.md index a0b91ee7553..b22c1df9966 100644 --- a/tests/ffe/README.md +++ b/tests/ffe/README.md @@ -23,10 +23,10 @@ This directory contains system tests for the Feature Flags & Experimentation (FF `Test_FFE_EVP_Flagevaluation_SDK_Identity` checks SDK origin and version headers on Agent-to-intake captures while retaining the shared payload and evaluation-count checks. -The new case runs alongside the existing Go and Node.js express5 egress coverage, -with additional gates for Agent 7.84+ and Go 2.11.0+. Other languages retain their -current exclusions; enabling them remains separate. Event-level `source` is rejected: -producer identity belongs in the request headers. +The case requires Agent 7.84+ and is enabled for Go 2.11.0+, Python 4.16.0+, and +Node.js express5 under its existing version gate. Other languages retain their +current exclusions. Event-level `source` is rejected: source identity belongs in +the request headers. ## Per-subject evaluation telemetry opt-out diff --git a/tests/ffe/test_flag_eval_evp.py b/tests/ffe/test_flag_eval_evp.py index 9c671161dab..c148d941784 100644 --- a/tests/ffe/test_flag_eval_evp.py +++ b/tests/ffe/test_flag_eval_evp.py @@ -339,8 +339,8 @@ def test_ffe_evp_flagevaluation_egress(self) -> None: if evp_flagevaluation_events_from_data(cast("JSON", data), self.flag_key, self.targeting_key): assert_evp_sdk_identity( cast("JSON", data), - library_name=context.library.name, - library_version=context.library.raw_version, + source_name=context.library.name, + source_version=context.library.raw_version, ) diff --git a/tests/ffe/utils/evp.py b/tests/ffe/utils/evp.py index 912a4708906..a6e9cc76fb1 100644 --- a/tests/ffe/utils/evp.py +++ b/tests/ffe/utils/evp.py @@ -162,11 +162,11 @@ def _assert_single_header(data: dict[str, Any], name: str, expected: str | set[s assert values[0] in allowed, f"expected {name} in {sorted(allowed)!r}, got {values[0]!r}" -def assert_evp_sdk_identity(data: dict[str, Any], *, library_name: str, library_version: str) -> None: - """Require the SDK producer identity on an intake-side capture.""" - _assert_single_header(data, "DD-EVP-ORIGIN", expected_evp_origin(library_name)) - assert library_version.strip(), "expected SDK version must be non-empty" - _assert_single_header(data, "DD-EVP-ORIGIN-VERSION", expected_evp_origin_version(library_name, library_version)) +def assert_evp_sdk_identity(data: dict[str, Any], *, source_name: str, source_version: str) -> None: + """Require the SDK source identity on an intake-side capture.""" + _assert_single_header(data, "DD-EVP-ORIGIN", expected_evp_origin(source_name)) + assert source_version.strip(), "expected source version must be non-empty" + _assert_single_header(data, "DD-EVP-ORIGIN-VERSION", expected_evp_origin_version(source_name, source_version)) def assert_agentless_evp_intake_request( @@ -205,7 +205,7 @@ def assert_agentless_evp_intake_request( # SDK local-route identity and credential omission remain covered by SDK unit tests. return - assert_evp_sdk_identity(data, library_name=library_name, library_version=library_version) + assert_evp_sdk_identity(data, source_name=library_name, source_version=library_version) def assert_no_evp_proxy_requests(captures: Iterable[dict[str, Any]]) -> None: From 017f7d1273a1ce7e2f9741f93ac78d258d1fc029 Mon Sep 17 00:00:00 2001 From: Vickie Boettcher Date: Mon, 26 Oct 2026 20:50:24 +0000 Subject: [PATCH 4/4] test(ffe): align identity helper with library naming Use library_name and library_version consistently with existing EVP helpers and callers. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 --- tests/ffe/test_flag_eval_evp.py | 4 ++-- tests/ffe/utils/evp.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/ffe/test_flag_eval_evp.py b/tests/ffe/test_flag_eval_evp.py index c148d941784..9c671161dab 100644 --- a/tests/ffe/test_flag_eval_evp.py +++ b/tests/ffe/test_flag_eval_evp.py @@ -339,8 +339,8 @@ def test_ffe_evp_flagevaluation_egress(self) -> None: if evp_flagevaluation_events_from_data(cast("JSON", data), self.flag_key, self.targeting_key): assert_evp_sdk_identity( cast("JSON", data), - source_name=context.library.name, - source_version=context.library.raw_version, + library_name=context.library.name, + library_version=context.library.raw_version, ) diff --git a/tests/ffe/utils/evp.py b/tests/ffe/utils/evp.py index a6e9cc76fb1..990d1846be5 100644 --- a/tests/ffe/utils/evp.py +++ b/tests/ffe/utils/evp.py @@ -162,11 +162,11 @@ def _assert_single_header(data: dict[str, Any], name: str, expected: str | set[s assert values[0] in allowed, f"expected {name} in {sorted(allowed)!r}, got {values[0]!r}" -def assert_evp_sdk_identity(data: dict[str, Any], *, source_name: str, source_version: str) -> None: +def assert_evp_sdk_identity(data: dict[str, Any], *, library_name: str, library_version: str) -> None: """Require the SDK source identity on an intake-side capture.""" - _assert_single_header(data, "DD-EVP-ORIGIN", expected_evp_origin(source_name)) - assert source_version.strip(), "expected source version must be non-empty" - _assert_single_header(data, "DD-EVP-ORIGIN-VERSION", expected_evp_origin_version(source_name, source_version)) + _assert_single_header(data, "DD-EVP-ORIGIN", expected_evp_origin(library_name)) + assert library_version.strip(), "expected library version must be non-empty" + _assert_single_header(data, "DD-EVP-ORIGIN-VERSION", expected_evp_origin_version(library_name, library_version)) def assert_agentless_evp_intake_request( @@ -205,7 +205,7 @@ def assert_agentless_evp_intake_request( # SDK local-route identity and credential omission remain covered by SDK unit tests. return - assert_evp_sdk_identity(data, source_name=library_name, source_version=library_version) + assert_evp_sdk_identity(data, library_name=library_name, library_version=library_version) def assert_no_evp_proxy_requests(captures: Iterable[dict[str, Any]]) -> None: