FFL-2783: add tests for observeFullEvaluationData PII protection (gated as missing_feature)#7316
FFL-2783: add tests for observeFullEvaluationData PII protection (gated as missing_feature)#7316vjfridge wants to merge 11 commits into
Conversation
Adds three non-blocking test classes covering the truth-table matrix for the new per-env UFC field: absent (backcompat), false (explicit disable), and true. Asserts hashed sha256_-prefixed targeting_key + omitted context.evaluation for the first two cells, and raw targeting_key + full context.evaluation for the third. Uses a canonical PII vector (jane.doe@datadoghq.com) so every SDK's L1 unit tests can assert against the same known hash output. All classes gated missing_feature (FFL-2783) on Go; other SDK manifests already skip the whole file. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
|
|
🎉 All green!🧪 All tests passed 🔗 Commit SHA: e9a5752 | Docs | Datadog PR Page | Give us feedback! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d1ad980b2f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| rc.tracer_rc_state.reset().set_config( | ||
| f"{RC_PATH}/{config_id}/config", | ||
| make_ufc_fixture(self.flag_key), | ||
| ).apply() |
There was a problem hiding this comment.
Move fallible setup work into the test
These new setup_* methods perform remote-config application and the weblog request before the test body; if either path raises or times out in CI, pytest reports a setup error and the scenario can stop instead of recording a normal test failure. .cursor/rules/pr-review.mdc says setup methods must only trigger observability and failures/assertions belong in test_*, so keep setup minimal and move the fallible validation path into the matching test method.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Not addressing this one. The other eight test classes in this file (Test_FFE_EVP_Flagevaluation_Basic, _Count, _Context_Bounds, _Runtime_Default, _Load_Aggregation, _Burst_Aggregation, _High_Cardinality_Aggregation, _Degradation) all place rc...apply() and evaluate_flag(...) in setup_* and only run assert self.r.status_code == 200 in test_*. Diverging in only the three new classes would create an inconsistency reviewers would flag; if the setup-vs-test split needs enforcement here, it belongs in a dedicated refactor across the whole file.
The three new missing_feature rows should reference FFL-2784 (Go SDK implementation) rather than FFL-2783 (this L3 test ticket) so an SDK dev lands directly on the work that unblocks activation. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
- Rename CANONICAL_* to PII_* and move constants + helpers into their existing sections (top-of-file constants, mid-file helpers) for consistency with the rest of the file. - Iterate every matching flagevaluation event in all three PII test classes so a clean first event cannot mask a leaky later event. - Add assert_no_raw_pii_in_event helper that walks the serialized event and asserts none of the raw PII values (targeting key or attributes) appear anywhere — guards against SDKs that route raw PII into unexpected fields (e.g., context.user_email) even when context.evaluation is correctly omitted. Applied to both hashed cells. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
cbeauchesne
left a comment
There was a problem hiding this comment.
From framework usage, just a small request change.
Cold you also get a review from someone familiar with the feature for the test logic ?
|
|
||
| @scenarios.feature_flagging_and_experimentation | ||
| @features.feature_flags_evp_flagevaluation | ||
| @pytest.mark.skip_if_xfail |
There was a problem hiding this comment.
There are semantic decorators in utils that allow to achieve the same, and also declare why we want to skip them : @slow and @scenario_crash, could you use them ?
There was a problem hiding this comment.
Thanks for raising this! You're right that using @pytest.mark.skip_if_xfail directly is too raw. We looked at @slow and @scenario_crash but neither quite fits — these tests aren't slow, and they don't crash the scenario. They're gated on SDK implementation status.
So we added a new alias @not_yet_implemented in utils/_decorators.py (same underlying skip_if_xfail mark, just with a clearer name) and used it consistently across all 11 classes in the file.
There was a problem hiding this comment.
Why is it not enough to mark the tests as xfails in that case?
…ly in test_flag_eval_evp.py @slow and @scenario_crash both alias skip_if_xfail but carry wrong semantics for tests gated on SDK implementation status. Add @not_yet_implemented as a clearer alias and use it throughout the FFE EVP test file. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
leoromanovsky
left a comment
There was a problem hiding this comment.
logic is good, before merging please:
- add a env var=false case
- adjust labels on existing test cases
| @pytest.mark.skip_if_xfail | ||
| @not_yet_implemented |
There was a problem hiding this comment.
hehe @cbeauchesne skipped these because they are not set up correctly; I needed to break system test convention to get them to work with PHP, which has its own sidecar that needs an additional await to count received EVP events in the backend. It's on my backlog to address, but a more correct skip decorator would be flaky or bug
There was a problem hiding this comment.
Thanks for raising Leo! I looked into this more carefully. The existing @flaky/@bug decorators are all-or-nothing across SDKs. Per-SDK skipping lives in the manifests, so I think it would be best to wait until PHP gets enabled and then add class-level manifest entries in manifests/php.yml.
For now I've added a docstring note to Burst, HighCardinality, and Degradation pointing to FFL-2529 and the manifest pattern, so whenever we drop the file-level missing_feature for PHP we will know to add those entries at the same time.
I left @not_yet_implemented on all three for now since that's still the accurate for PHP and most SDKs. Let me know if this works for you or if you were looking for something different!
| assert "targeting_key" not in event, f"degraded event must omit targeting_key: {event}" | ||
| assert object_key(event.get("variant"), "variant") == "on" | ||
| assert object_key(event.get("allocation"), "allocation") == "default-allocation" | ||
|
|
There was a problem hiding this comment.
Please add a test for DD_FLAGGING_EVALUATION_COUNTS_ENABLED=false + observeFullEvaluationData=true - sorry I missed adding coverage for the false case.
There was a problem hiding this comment.
Happy to add coverage for DD_FLAGGING_EVALUATION_COUNTS_ENABLED =false + observeFullEvaluationData=true, but I think it lives better at L1 unit tests/L2 SDK integration layers.
The assertion is "SDK-level killswitch overrides UFC opt-in" which is purely internal to the SDK and doesn't exercise anything system-level. Adding an L3 scenario here means a new weblog boot per SDK (~minutes of CI × N languages) to verify the boolean killswitch.
I propose we cover this in each SDK's unit or integration suite instead. Let me know if you have strong feelings otherwise!
Per dd-source PR #22826, observeFullEvaluationData is a top-level field on UniversalFlagConfiguration, not nested under environment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Addresses leoromanovsky review comment on tests/ffe/test_flag_eval_evp.py:26. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Degradation tests When PHP is enabled for test_flag_eval_evp.py, these three classes will need per-class flaky entries in manifests/php.yml due to the PHP sidecar's async flush hop making EVP event counts racy. Docstring notes point future enablers to FFL-2529 and the manifest pattern. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Local system-tests validation — Java SDK ✅Ran the three new All three pass:
Run command: ./run.sh +l java FEATURE_FLAGGING_AND_EXPERIMENTATION \
-F tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_ObserveFullData_Absent_Hashed \
-F tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_ObserveFullData_False_Hashed \
-F tests/ffe/test_flag_eval_evp.py::Test_FFE_EVP_Flagevaluation_ObserveFullData_True_UnhashedNote: an earlier build ( |
Motivation
FFL-2783 — part of FFL-2780 (Protecting PII in flagevaluations track)
See informal RFC for more context
Summary
tests/ffe/test_flag_eval_evp.pycovering the new per-env UFCobserveFullEvaluationDatafieldmake_ufc_fixturewith anobserve_full_evaluation_data: bool | None = Nonekwarg.Noneomits the field entirely, so existing tests are unaffectedmissing_feature (FFL-2783)on Go — the only SDK where the file-level gate would otherwise activate them. Other SDK manifests already deactivate the whole file viamissing_feature (FFL-2446)orirrelevant.Truth-table coverage
observeFullEvaluationDatatargeting_keycontext.evaluationsha256_-prefixed (71 chars, lowercase hex suffix)falsesha256_-prefixedtrueCanonical PII vector (shared across SDKs)
Every SDK's unit tests should assert against the same input/output. Input:
"jane.doe@datadoghq.com"→"sha256_b4698f9b6d186781fa8dc59e533578fa2d8379a46b1cf6db85cda6aa9c99e51b". Canonical attributes cover string, integer, dotted-key, and email PII shapes.Forward-compat for existing SDKs
Introducing a new field inside the UFC
environmentobject could theoretically break SDKs if their UFC parser rejects unknown fields. That risk is already covered bytests/ffe/test_dynamic_evaluation.py::Test_FFE_Unknown_Fields_Tolerance, which injects random unknown fields at every UFC level (including insideenvironment) and asserts each SDK continues to evaluate flags correctly. That test runs on every SDK wheretest_dynamic_evaluation.pyis active, so no per-fixture retrofit is needed here.Out of scope
DD_FLAGGING_EVALUATION_COUNTS_ENABLED) — per-SDK unit test territory, nosystem-testsscenario variant needed.Test plan
./format.shclean (mypy, ruff, node linters)missing_feature (FFL-2783)rows tov2.X.Y-devand confirm all three cells pass🤖 Generated with Claude Code