From 04e1fb13df245b2420be2766a83d05a019ad5506 Mon Sep 17 00:00:00 2001 From: dudik <8845578+dudikeleti@users.noreply.github.com> Date: Thu, 9 Jul 2026 12:00:09 +0200 Subject: [PATCH 1/8] Introduce initial debugger guardrail coverage for capture incomplete reasons and evaluation timeout behavior, with new rows disabled in manifests until tracer support is ready. --- manifests/dotnet.yml | 2 + manifests/golang.yml | 2 + manifests/java.yml | 2 + manifests/nodejs.yml | 2 + manifests/php.yml | 2 + manifests/python.yml | 2 + manifests/ruby.yml | 2 + tests/debugger/test_debugger_guardrails.py | 79 +++++++++++++++++++ .../debugger/test_debugger_probe_snapshot.py | 23 ++++++ ..._evaluation_timeout_collection_filter.json | 38 +++++++++ .../probe_evaluation_timeout_regex.json | 26 ++++++ 11 files changed, 180 insertions(+) create mode 100644 tests/debugger/test_debugger_guardrails.py create mode 100644 tests/debugger/utils/probes/probe_evaluation_timeout_collection_filter.json create mode 100644 tests/debugger/utils/probes/probe_evaluation_timeout_regex.json diff --git a/manifests/dotnet.yml b/manifests/dotnet.yml index 43c8159b871..2bb4b655803 100644 --- a/manifests/dotnet.yml +++ b/manifests/dotnet.yml @@ -654,6 +654,7 @@ manifest: tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_nulls_true: bug (DEBUG-2618) ? tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_string_operations : bug (DEBUG-2560) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: bug (DEBUG-4637) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin_Default_On: missing_feature (Temporarily disabled; enable from v3.42.0 later) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Dynamic_Instrumentation: bug (DEBUG-4637) @@ -674,6 +675,7 @@ manifest: excluded_weblog: [uds, poc] tests/debugger/test_debugger_probe_budgets.py::Test_Debugger_Probe_Budgets::test_span_probe_expression_budgets: irrelevant (Dotnet emits evaluation errors as span tags, not error snapshots to the debugger endpoint) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots: v2.53.0 + tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_capture_limits_emit_incomplete: missing_feature (Debugger incomplete capture reason coverage not implemented yet) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_default_max_length: bug (DEBUG-4669) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_process_tags_snapshot: - declaration: missing_feature (Not implemented in older versions) diff --git a/manifests/golang.yml b/manifests/golang.yml index b7f2ac65709..4d88d718817 100644 --- a/manifests/golang.yml +++ b/manifests/golang.yml @@ -1074,6 +1074,7 @@ manifest: tests/debugger/test_debugger_exception_replay.py::Test_Debugger_Exception_Replay::test_exception_replay_recursion_inlined: irrelevant (Test for specific bug in dotnet) tests/debugger/test_debugger_exception_replay.py::Test_Debugger_Exception_Replay::test_exception_replay_stackoverflow: missing_feature (Implemented only for dotnet) tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language: missing_feature (feature not implemented) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: missing_feature tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin_Default_On: irrelevant (Scoped to dotnet rollout in this PR) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Dynamic_Instrumentation: missing_feature @@ -1083,6 +1084,7 @@ manifest: tests/debugger/test_debugger_probe_budgets.py::Test_Debugger_Probe_Budgets: v2.2.3 tests/debugger/test_debugger_probe_budgets.py::Test_Debugger_Probe_Budgets::test_span_probe_expression_budgets: missing_feature (span decoration probes not yet implemented for Go) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots: v2.2.3 + tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_capture_limits_emit_incomplete: missing_feature (Debugger incomplete capture reason coverage not implemented yet) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_default_max_collection_size: missing_feature (no /debugger/snapshot/limits endpoint) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_default_max_field_count: missing_feature (no /debugger/snapshot/limits endpoint) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_default_max_length: missing_feature (no /debugger/snapshot/limits endpoint) diff --git a/manifests/java.yml b/manifests/java.yml index 3b9d80209a8..d5d488fc551 100644 --- a/manifests/java.yml +++ b/manifests/java.yml @@ -3041,6 +3041,7 @@ manifest: spring-boot-undertow: v1.38.0 spring-boot-wildfly: v1.38.0 uds-spring-boot: v1.33.0 + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: - weblog_declaration: "*": missing_feature @@ -3120,6 +3121,7 @@ manifest: spring-boot-undertow: v1.38.0 spring-boot-wildfly: v1.38.0 uds-spring-boot: v1.38.0 + tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_capture_limits_emit_incomplete: missing_feature (Debugger incomplete capture reason coverage not implemented yet) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_process_tags_snapshot: - weblog_declaration: spring-boot-3-native: missing_feature (Not yet implemented) diff --git a/manifests/nodejs.yml b/manifests/nodejs.yml index 426150110ec..c83e2db749f 100644 --- a/manifests/nodejs.yml +++ b/manifests/nodejs.yml @@ -1605,6 +1605,7 @@ manifest: - weblog_declaration: "*": *ref_5_48_0 nextjs: irrelevant + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) ? tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_access_exception : missing_feature (Not yet implemented) ? tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_contextual_variables @@ -1654,6 +1655,7 @@ manifest: - weblog_declaration: "*": *ref_5_85_0 nextjs: irrelevant + tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_capture_limits_emit_incomplete: missing_feature (Debugger incomplete capture reason coverage not implemented yet) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_log_line_snapshot: - weblog_declaration: "*": *ref_5_85_0 diff --git a/manifests/php.yml b/manifests/php.yml index 14f93e3e087..573b2c685bd 100644 --- a/manifests/php.yml +++ b/manifests/php.yml @@ -651,6 +651,7 @@ manifest: tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_nulls_true: '>=1.16.0' ? tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_string_operations : '>=1.16.0' + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: missing_feature (code origin entry spans not captured in PHP-FPM; RC toggle works but no spans to verify) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin_Default_On: irrelevant (Scoped to dotnet rollout in this PR) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Dynamic_Instrumentation: @@ -671,6 +672,7 @@ manifest: tests/debugger/test_debugger_probe_budgets.py::Test_Debugger_Probe_Budgets::test_log_line_capture_expression_budgets: missing_feature tests/debugger/test_debugger_probe_budgets.py::Test_Debugger_Probe_Budgets::test_span_probe_expression_budgets: irrelevant tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots: missing_feature + tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_capture_limits_emit_incomplete: missing_feature (Debugger incomplete capture reason coverage not implemented yet) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_process_tags_snapshot: missing_feature (Not yet implemented) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_process_tags_snapshot_svc: missing_feature tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots_With_SCM: missing_feature diff --git a/manifests/python.yml b/manifests/python.yml index e8fd37f7ab0..3abef8d2498 100644 --- a/manifests/python.yml +++ b/manifests/python.yml @@ -1223,6 +1223,7 @@ manifest: - weblog_declaration: "*": missing_feature *flask: v2.11.0 + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: - weblog_declaration: "*": missing_feature @@ -1272,6 +1273,7 @@ manifest: - weblog_declaration: "*": missing_feature *flask: v2.11.0 + tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_capture_limits_emit_incomplete: missing_feature (Debugger incomplete capture reason coverage not implemented yet) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_log_line_snapshot_debug_track: - declaration: missing_feature (Python 3.15.0 introduced the track change) component_version: <3.15.0 diff --git a/manifests/ruby.yml b/manifests/ruby.yml index 1937f9c35e9..906a0283213 100644 --- a/manifests/ruby.yml +++ b/manifests/ruby.yml @@ -1775,6 +1775,7 @@ manifest: rails72: v2.20.0 rails80: v2.20.0 uds-rails: v2.20.0 + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_hash_operations: - declaration: missing_feature (Hash length not implemented) component_version: <=2.22.0 @@ -1812,6 +1813,7 @@ manifest: rails72: v2.8.0 rails80: v2.8.0 uds-rails: v2.12.0 + tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_capture_limits_emit_incomplete: missing_feature (Debugger incomplete capture reason coverage not implemented yet) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_default_max_reference_depth: - declaration: bug (DEBUG-4675) component_version: '<2.34.0-dev' diff --git a/tests/debugger/test_debugger_guardrails.py b/tests/debugger/test_debugger_guardrails.py new file mode 100644 index 00000000000..c0052b2c59a --- /dev/null +++ b/tests/debugger/test_debugger_guardrails.py @@ -0,0 +1,79 @@ +# Unless explicitly stated otherwise all files in this repository are licensed under the the Apache License Version 2.0. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2021 Datadog, Inc. + +from typing import Any + +import tests.debugger.utils as debugger +from utils import context, features, scenarios, weblog + + +@features.debugger_expression_language +@scenarios.debugger_probes_snapshot +class Test_Debugger_Evaluation_Timeout(debugger.BaseDebuggerTest): + """RFC guardrail: expensive expression evaluation must stop before normal snapshot creation.""" + + def _convert_to_line_probe_if_needed(self, probe: dict[str, Any], method: str) -> None: + if context.library.name not in ("nodejs", "ruby"): + return + + where = probe["where"] + where.pop("methodName", None) + where["typeName"] = None + where["sourceFile"] = "ACTUAL_SOURCE_FILE" + where["lines"] = self.method_and_language_to_line_number(method, context.library.name) + + def _setup_evaluation_timeout(self, probes_name: str, method: str, request_path: str) -> None: + self.initialize_weblog_remote_config() + + probes = debugger.read_probes(probes_name) + for probe in probes: + probe["id"] = debugger.generate_probe_id("log") + self._convert_to_line_probe_if_needed(probe, method) + + self.set_probes(probes) + self.send_rc_probes() + if not self.wait_for_all_probes(statuses=["INSTALLED"], timeout=30): + self.setup_failures.append("Probes did not reach INSTALLED status within 30s") + + self.weblog_responses.append(weblog.get(request_path, timeout=15)) + if not self.wait_for_all_probes(statuses=["EMITTING"], timeout=5): + self.setup_failures.append("Probes did not reach EMITTING status within 5s") + + # A conforming tracer skips the event, so waiting should time out. A non-conforming + # tracer that evaluates the expression fully will emit a snapshot and fail below. + self.wait_for_all_snapshots(timeout=3) + time.sleep(2) + + def setup_evaluation_timeout_regex(self) -> None: + self._setup_evaluation_timeout( + "probe_evaluation_timeout_regex", + "StringOperations", + f"/debugger/expression/strings?strValue={'a' * 30000}!", + ) + + def test_evaluation_timeout_regex(self) -> None: + self._assert_no_normal_snapshot() + + def setup_evaluation_timeout_collection_filter(self) -> None: + self._setup_evaluation_timeout( + "probe_evaluation_timeout_collection_filter", + "SnapshotLimits", + "/debugger/snapshot/limits?collectionSize=100000", + ) + + def test_evaluation_timeout_collection_filter(self) -> None: + self._assert_no_normal_snapshot() + + def _assert_no_normal_snapshot(self) -> None: + self.collect() + self.assert_setup_ok() + self.assert_rc_state_not_error() + self.assert_all_weblog_responses_ok() + + for probe_id in self.probe_ids: + snapshots = self.probe_snapshots.get(probe_id, []) + assert not snapshots, ( + f"Probe emitted {len(snapshots)} snapshot(s) after evaluation timeout. " + "A conforming tracer must skip event creation when expression evaluation exceeds 1 second." + ) diff --git a/tests/debugger/test_debugger_probe_snapshot.py b/tests/debugger/test_debugger_probe_snapshot.py index 67b55fe37d9..1d3fd250263 100644 --- a/tests/debugger/test_debugger_probe_snapshot.py +++ b/tests/debugger/test_debugger_probe_snapshot.py @@ -252,6 +252,10 @@ def setup_default_max_length(self): """Setup test for default maxLength""" self._setup_default_capture_limits() + def setup_capture_limits_emit_incomplete(self) -> None: + """Setup test for RFC guardrail incomplete-capture reason coverage.""" + self._setup_default_capture_limits() + def _setup_default_capture_limits(self): """Shared setup method for default capture limit tests""" test_depth = self.DEFAULT_MAX_REFERENCE_DEPTH + 7 @@ -382,6 +386,25 @@ def test_default_max_length(self): f"longString should have length {self.DEFAULT_MAX_LENGTH}, got: {len(string_value)}" ) + def test_capture_limits_emit_incomplete(self) -> None: + """Test that all structural guardrails report incomplete capture reasons.""" + deep_object = self._get_snapshot_locals_variable("deepObject") + many_fields = self._get_snapshot_locals_variable("manyFields") + large_collection = self._get_snapshot_locals_variable("largeCollection") + long_string = self._get_snapshot_locals_variable("longString") + + self._measure_captured_depth(deep_object) + assert many_fields.get("notCapturedReason") == "fieldCount", ( + f"manyFields should have notCapturedReason='fieldCount', got: {many_fields.get('notCapturedReason')}" + ) + assert large_collection.get("notCapturedReason") == "collectionSize", ( + "largeCollection should have notCapturedReason='collectionSize', " + f"got: {large_collection.get('notCapturedReason')}" + ) + assert long_string.get("notCapturedReason") == "stringLength", ( + f"longString should have notCapturedReason='stringLength', got: {long_string.get('notCapturedReason')}" + ) + def setup_log_line_snapshot_debug_track(self): self.use_debugger_endpoint = True self._setup("probe_snapshot_log_line", "/debugger/log", "log", lines=None) diff --git a/tests/debugger/utils/probes/probe_evaluation_timeout_collection_filter.json b/tests/debugger/utils/probes/probe_evaluation_timeout_collection_filter.json new file mode 100644 index 00000000000..0917b1f0ff2 --- /dev/null +++ b/tests/debugger/utils/probes/probe_evaluation_timeout_collection_filter.json @@ -0,0 +1,38 @@ +[ + { + "language": "", + "type": "", + "id": "", + "version": 0, + "where": { + "typeName": "ACTUAL_TYPE_NAME", + "methodName": "SnapshotLimits", + "sourceFile": null + }, + "when": { + "dsl": "largeCollection.filter(@it >= 0).length > 0", + "json": { + "gt": [ + { + "len": { + "filter": [ + {"ref": "largeCollection"}, + { + "ge": [ + {"ref": "@it"}, + 0 + ] + } + ] + } + }, + 0 + ] + } + }, + "captureSnapshot": true, + "segments": [ + {"str": "collection evaluation timeout probe fired"} + ] + } +] diff --git a/tests/debugger/utils/probes/probe_evaluation_timeout_regex.json b/tests/debugger/utils/probes/probe_evaluation_timeout_regex.json new file mode 100644 index 00000000000..961e761e515 --- /dev/null +++ b/tests/debugger/utils/probes/probe_evaluation_timeout_regex.json @@ -0,0 +1,26 @@ +[ + { + "language": "", + "type": "", + "id": "", + "version": 0, + "where": { + "typeName": "ACTUAL_TYPE_NAME", + "methodName": "StringOperations", + "sourceFile": null + }, + "when": { + "dsl": "strValue.matches(\"^(a+)+$\")", + "json": { + "matches": [ + {"ref": "strValue"}, + "^(a+)+$" + ] + } + }, + "captureSnapshot": true, + "segments": [ + {"str": "regex evaluation timeout probe fired"} + ] + } +] From 3ef4275f707953ac1db4473d3a40ea8b5551b3d2 Mon Sep 17 00:00:00 2001 From: dudik <8845578+dudikeleti@users.noreply.github.com> Date: Thu, 9 Jul 2026 12:10:51 +0200 Subject: [PATCH 2/8] Adds debugger guardrail coverage for snapshot size trimming and capture timeout reporting. 1MB snapshot cap and notCapturedReason=timeout behavior. --- manifests/dotnet.yml | 1 + manifests/golang.yml | 1 + manifests/java.yml | 1 + manifests/nodejs.yml | 1 + manifests/php.yml | 1 + manifests/python.yml | 1 + manifests/ruby.yml | 1 + tests/debugger/test_debugger_guardrails.py | 131 +++++++++++++++++- .../probes/probe_capture_timeout_reason.json | 20 +++ .../utils/probes/probe_snapshot_size_cap.json | 20 +++ 10 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 tests/debugger/utils/probes/probe_capture_timeout_reason.json create mode 100644 tests/debugger/utils/probes/probe_snapshot_size_cap.json diff --git a/manifests/dotnet.yml b/manifests/dotnet.yml index 2bb4b655803..07fe10856d2 100644 --- a/manifests/dotnet.yml +++ b/manifests/dotnet.yml @@ -655,6 +655,7 @@ manifest: ? tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_string_operations : bug (DEBUG-2560) tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: bug (DEBUG-4637) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin_Default_On: missing_feature (Temporarily disabled; enable from v3.42.0 later) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Dynamic_Instrumentation: bug (DEBUG-4637) diff --git a/manifests/golang.yml b/manifests/golang.yml index 4d88d718817..a8fee8847ab 100644 --- a/manifests/golang.yml +++ b/manifests/golang.yml @@ -1075,6 +1075,7 @@ manifest: tests/debugger/test_debugger_exception_replay.py::Test_Debugger_Exception_Replay::test_exception_replay_stackoverflow: missing_feature (Implemented only for dotnet) tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language: missing_feature (feature not implemented) tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: missing_feature tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin_Default_On: irrelevant (Scoped to dotnet rollout in this PR) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Dynamic_Instrumentation: missing_feature diff --git a/manifests/java.yml b/manifests/java.yml index d5d488fc551..4c12c7dda8d 100644 --- a/manifests/java.yml +++ b/manifests/java.yml @@ -3042,6 +3042,7 @@ manifest: spring-boot-wildfly: v1.38.0 uds-spring-boot: v1.33.0 tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: - weblog_declaration: "*": missing_feature diff --git a/manifests/nodejs.yml b/manifests/nodejs.yml index c83e2db749f..3db106d9956 100644 --- a/manifests/nodejs.yml +++ b/manifests/nodejs.yml @@ -1606,6 +1606,7 @@ manifest: "*": *ref_5_48_0 nextjs: irrelevant tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) ? tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_access_exception : missing_feature (Not yet implemented) ? tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_contextual_variables diff --git a/manifests/php.yml b/manifests/php.yml index 573b2c685bd..4a2c43ab8ed 100644 --- a/manifests/php.yml +++ b/manifests/php.yml @@ -652,6 +652,7 @@ manifest: ? tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_string_operations : '>=1.16.0' tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: missing_feature (code origin entry spans not captured in PHP-FPM; RC toggle works but no spans to verify) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin_Default_On: irrelevant (Scoped to dotnet rollout in this PR) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Dynamic_Instrumentation: diff --git a/manifests/python.yml b/manifests/python.yml index 3abef8d2498..867881ddff7 100644 --- a/manifests/python.yml +++ b/manifests/python.yml @@ -1224,6 +1224,7 @@ manifest: "*": missing_feature *flask: v2.11.0 tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: - weblog_declaration: "*": missing_feature diff --git a/manifests/ruby.yml b/manifests/ruby.yml index 906a0283213..825664f209b 100644 --- a/manifests/ruby.yml +++ b/manifests/ruby.yml @@ -1776,6 +1776,7 @@ manifest: rails80: v2.20.0 uds-rails: v2.20.0 tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_hash_operations: - declaration: missing_feature (Hash length not implemented) component_version: <=2.22.0 diff --git a/tests/debugger/test_debugger_guardrails.py b/tests/debugger/test_debugger_guardrails.py index c0052b2c59a..cca555ed203 100644 --- a/tests/debugger/test_debugger_guardrails.py +++ b/tests/debugger/test_debugger_guardrails.py @@ -2,10 +2,15 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2021 Datadog, Inc. +import json +import time from typing import Any import tests.debugger.utils as debugger -from utils import context, features, scenarios, weblog +from utils import context, features, scenarios, slow, weblog + + +MAX_SNAPSHOT_BYTES = 1024 * 1024 @features.debugger_expression_language @@ -77,3 +82,127 @@ def _assert_no_normal_snapshot(self) -> None: f"Probe emitted {len(snapshots)} snapshot(s) after evaluation timeout. " "A conforming tracer must skip event creation when expression evaluation exceeds 1 second." ) + + +@features.debugger_line_probe +@scenarios.debugger_probes_snapshot +@slow +class Test_Debugger_Snapshot_Guardrails(debugger.BaseDebuggerTest): + """RFC guardrails for completed snapshot size and capture-time budget.""" + + SNAPSHOT_SIZE_COLLECTION_ITEMS = 300000 + CAPTURE_TIMEOUT_COLLECTION_ITEMS = 1000000 + + def _setup_snapshot_guardrail(self, probes_name: str, collection_size: int) -> None: + self.initialize_weblog_remote_config() + + probes = debugger.read_probes(probes_name) + for probe in probes: + probe["id"] = debugger.generate_probe_id("log") + if "methodName" in probe["where"]: + del probe["where"]["methodName"] + probe["where"]["lines"] = [ + str(line) for line in self.method_and_language_to_line_number("SnapshotLimits", context.library.name) + ] + probe["where"]["sourceFile"] = "ACTUAL_SOURCE_FILE" + probe["where"]["typeName"] = None + + self.set_probes(probes) + self.send_rc_probes() + if not self.wait_for_all_probes(statuses=["INSTALLED"], timeout=30): + self.setup_failures.append("Probes did not reach INSTALLED status within 30s") + + self.weblog_responses.append(weblog.get(f"/debugger/snapshot/limits?collectionSize={collection_size}", timeout=30)) + if not self.wait_for_all_probes(statuses=["EMITTING"], timeout=10): + self.setup_failures.append("Probes did not reach EMITTING status within 10s") + if not self.wait_for_all_snapshots(timeout=30): + self.setup_failures.append("Snapshot was not received within 30s") + + def setup_snapshot_size_cap(self) -> None: + self._setup_snapshot_guardrail("probe_snapshot_size_cap", self.SNAPSHOT_SIZE_COLLECTION_ITEMS) + + def test_snapshot_size_cap(self) -> None: + _, snapshot = self._get_single_snapshot() + serialized_size = len(json.dumps(snapshot, separators=(",", ":")).encode()) + assert serialized_size <= MAX_SNAPSHOT_BYTES, ( + f"Snapshot should be trimmed to <= {MAX_SNAPSHOT_BYTES} bytes, got {serialized_size} bytes" + ) + + large_collection = self._get_captured_local(snapshot, "largeCollection") + assert self._is_partially_captured(large_collection, self.SNAPSHOT_SIZE_COLLECTION_ITEMS), ( + "Expected the oversized snapshot to contain only part of largeCollection after trimming, " + f"got: {large_collection!r}" + ) + + def setup_capture_timeout_reason(self) -> None: + self._setup_snapshot_guardrail("probe_capture_timeout_reason", self.CAPTURE_TIMEOUT_COLLECTION_ITEMS) + + def test_capture_timeout_reason(self) -> None: + _, snapshot = self._get_single_snapshot() + reasons = set(self._iter_not_captured_reasons(snapshot)) + assert "timeout" in reasons, f"Expected notCapturedReason='timeout', got reasons: {sorted(reasons)}" + unexpected_reasons = {"collectionSize", "stringLength", "fieldCount", "depth"} & reasons + assert not unexpected_reasons, ( + "Capture timeout fixture should avoid structural limit reasons, " + f"but found: {sorted(unexpected_reasons)}" + ) + + def _get_single_snapshot(self) -> tuple[dict[str, Any], dict[str, Any]]: + self.collect() + self.assert_setup_ok() + self.assert_rc_state_not_error() + self.assert_all_probes_are_emitting() + self.assert_all_weblog_responses_ok() + + for probe_id in self.probe_ids: + snapshots = self.probe_snapshots.get(probe_id, []) + assert len(snapshots) == 1, f"Expected exactly 1 snapshot for {probe_id}, got {len(snapshots)}" + envelope = snapshots[0] + snapshot = envelope.get("debugger", {}).get("snapshot") or envelope.get("debugger.snapshot") + assert isinstance(snapshot, dict), f"Snapshot data not found in expected format for {probe_id}" + return envelope, snapshot + + raise AssertionError("No probe IDs were registered") + + def _get_captured_local(self, snapshot: dict[str, Any], variable_name: str) -> dict[str, Any]: + captures = snapshot.get("captures", {}) + lines = captures.get("lines", {}) + assert isinstance(lines, dict) and len(lines) == 1, f"Expected one line capture, got: {lines!r}" + + line_data = next(iter(lines.values())) + locals_data = line_data.get("locals", {}) + assert variable_name in locals_data, f"{variable_name!r} is missing from snapshot locals" + value = locals_data[variable_name] + assert isinstance(value, dict), f"Expected {variable_name!r} to be a captured object, got: {value!r}" + return value + + def _is_partially_captured(self, value: Any, expected_items: int) -> bool: + if not isinstance(value, dict): + return False + + if value.get("notCapturedReason") == "payloadTooLarge" or value.get("pruned") is True: + return True + + elements = value.get("elements") or value.get("entries") + if isinstance(elements, list) and len(elements) < expected_items: + return True + + return any(self._is_partially_captured(child, expected_items) for child in value.values()) + + def _iter_not_captured_reasons(self, value: Any) -> list[str]: + if isinstance(value, dict): + reasons = [] + reason = value.get("notCapturedReason") + if isinstance(reason, str): + reasons.append(reason) + for child in value.values(): + reasons.extend(self._iter_not_captured_reasons(child)) + return reasons + + if isinstance(value, list): + reasons = [] + for child in value: + reasons.extend(self._iter_not_captured_reasons(child)) + return reasons + + return [] diff --git a/tests/debugger/utils/probes/probe_capture_timeout_reason.json b/tests/debugger/utils/probes/probe_capture_timeout_reason.json new file mode 100644 index 00000000000..f3e65d4f1ab --- /dev/null +++ b/tests/debugger/utils/probes/probe_capture_timeout_reason.json @@ -0,0 +1,20 @@ +[ + { + "language": "", + "type": "", + "id": "", + "version": 0, + "captureSnapshot": true, + "where": { + "typeName": "ACTUAL_TYPE_NAME", + "methodName": "SnapshotLimits", + "sourceFile": null + }, + "capture": { + "maxReferenceDepth": 20, + "maxCollectionSize": 2000000, + "maxLength": 1000000, + "maxFieldCount": 1000 + } + } +] diff --git a/tests/debugger/utils/probes/probe_snapshot_size_cap.json b/tests/debugger/utils/probes/probe_snapshot_size_cap.json new file mode 100644 index 00000000000..84bc7c9bd78 --- /dev/null +++ b/tests/debugger/utils/probes/probe_snapshot_size_cap.json @@ -0,0 +1,20 @@ +[ + { + "language": "", + "type": "", + "id": "", + "version": 0, + "captureSnapshot": true, + "where": { + "typeName": "ACTUAL_TYPE_NAME", + "methodName": "SnapshotLimits", + "sourceFile": null + }, + "capture": { + "maxReferenceDepth": 20, + "maxCollectionSize": 1000000, + "maxLength": 1000000, + "maxFieldCount": 1000 + } + } +] From 1d74e93918c916f4a7a382fed2cb8681cbf95647 Mon Sep 17 00:00:00 2001 From: dudik <8845578+dudikeleti@users.noreply.github.com> Date: Thu, 9 Jul 2026 14:32:49 +0200 Subject: [PATCH 3/8] Adds the guardrail RFC link to the new debugger test classes and removes the setup-time negative snapshot wait so evaluation-timeout assertions rely on collected scenario output instead of a fixed sleep. --- tests/debugger/test_debugger_guardrails.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/debugger/test_debugger_guardrails.py b/tests/debugger/test_debugger_guardrails.py index cca555ed203..defec871ee0 100644 --- a/tests/debugger/test_debugger_guardrails.py +++ b/tests/debugger/test_debugger_guardrails.py @@ -3,16 +3,19 @@ # Copyright 2021 Datadog, Inc. import json -import time from typing import Any import tests.debugger.utils as debugger -from utils import context, features, scenarios, slow, weblog +from utils import context, features, rfc, scenarios, slow, weblog MAX_SNAPSHOT_BYTES = 1024 * 1024 +GUARDRAILS_RFC = ( + "https://docs.google.com/document/d/1OhCH3SMuS_B4Ickays94GpqDlqKcc9b9gLos1T85F-Q/edit?usp=sharing" +) +@rfc(GUARDRAILS_RFC) @features.debugger_expression_language @scenarios.debugger_probes_snapshot class Test_Debugger_Evaluation_Timeout(debugger.BaseDebuggerTest): @@ -45,11 +48,6 @@ def _setup_evaluation_timeout(self, probes_name: str, method: str, request_path: if not self.wait_for_all_probes(statuses=["EMITTING"], timeout=5): self.setup_failures.append("Probes did not reach EMITTING status within 5s") - # A conforming tracer skips the event, so waiting should time out. A non-conforming - # tracer that evaluates the expression fully will emit a snapshot and fail below. - self.wait_for_all_snapshots(timeout=3) - time.sleep(2) - def setup_evaluation_timeout_regex(self) -> None: self._setup_evaluation_timeout( "probe_evaluation_timeout_regex", @@ -84,6 +82,7 @@ def _assert_no_normal_snapshot(self) -> None: ) +@rfc(GUARDRAILS_RFC) @features.debugger_line_probe @scenarios.debugger_probes_snapshot @slow From 9f01c867f403eb1abb5aad92635476ee114d9e97 Mon Sep 17 00:00:00 2001 From: dudik <8845578+dudikeleti@users.noreply.github.com> Date: Thu, 9 Jul 2026 15:15:42 +0200 Subject: [PATCH 4/8] Address review feedback on the debugger guardrail tests --- manifests/dotnet.yml | 1 - manifests/golang.yml | 1 - manifests/java.yml | 1 - manifests/nodejs.yml | 1 - manifests/php.yml | 1 - manifests/python.yml | 1 - manifests/ruby.yml | 1 - .../test_debugger_condition_errors.py | 34 +-------- tests/debugger/test_debugger_guardrails.py | 69 +++++++++++++------ .../debugger/test_debugger_probe_snapshot.py | 20 ------ tests/debugger/utils.py | 31 +++++++++ 11 files changed, 80 insertions(+), 81 deletions(-) diff --git a/manifests/dotnet.yml b/manifests/dotnet.yml index 07fe10856d2..63d93bd6f69 100644 --- a/manifests/dotnet.yml +++ b/manifests/dotnet.yml @@ -676,7 +676,6 @@ manifest: excluded_weblog: [uds, poc] tests/debugger/test_debugger_probe_budgets.py::Test_Debugger_Probe_Budgets::test_span_probe_expression_budgets: irrelevant (Dotnet emits evaluation errors as span tags, not error snapshots to the debugger endpoint) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots: v2.53.0 - tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_capture_limits_emit_incomplete: missing_feature (Debugger incomplete capture reason coverage not implemented yet) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_default_max_length: bug (DEBUG-4669) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_process_tags_snapshot: - declaration: missing_feature (Not implemented in older versions) diff --git a/manifests/golang.yml b/manifests/golang.yml index a8fee8847ab..dc35c8b05d6 100644 --- a/manifests/golang.yml +++ b/manifests/golang.yml @@ -1085,7 +1085,6 @@ manifest: tests/debugger/test_debugger_probe_budgets.py::Test_Debugger_Probe_Budgets: v2.2.3 tests/debugger/test_debugger_probe_budgets.py::Test_Debugger_Probe_Budgets::test_span_probe_expression_budgets: missing_feature (span decoration probes not yet implemented for Go) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots: v2.2.3 - tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_capture_limits_emit_incomplete: missing_feature (Debugger incomplete capture reason coverage not implemented yet) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_default_max_collection_size: missing_feature (no /debugger/snapshot/limits endpoint) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_default_max_field_count: missing_feature (no /debugger/snapshot/limits endpoint) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_default_max_length: missing_feature (no /debugger/snapshot/limits endpoint) diff --git a/manifests/java.yml b/manifests/java.yml index 4c12c7dda8d..ea5071c2e4b 100644 --- a/manifests/java.yml +++ b/manifests/java.yml @@ -3122,7 +3122,6 @@ manifest: spring-boot-undertow: v1.38.0 spring-boot-wildfly: v1.38.0 uds-spring-boot: v1.38.0 - tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_capture_limits_emit_incomplete: missing_feature (Debugger incomplete capture reason coverage not implemented yet) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_process_tags_snapshot: - weblog_declaration: spring-boot-3-native: missing_feature (Not yet implemented) diff --git a/manifests/nodejs.yml b/manifests/nodejs.yml index 3db106d9956..ab6ae917203 100644 --- a/manifests/nodejs.yml +++ b/manifests/nodejs.yml @@ -1656,7 +1656,6 @@ manifest: - weblog_declaration: "*": *ref_5_85_0 nextjs: irrelevant - tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_capture_limits_emit_incomplete: missing_feature (Debugger incomplete capture reason coverage not implemented yet) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_log_line_snapshot: - weblog_declaration: "*": *ref_5_85_0 diff --git a/manifests/php.yml b/manifests/php.yml index 4a2c43ab8ed..a72033f4c51 100644 --- a/manifests/php.yml +++ b/manifests/php.yml @@ -673,7 +673,6 @@ manifest: tests/debugger/test_debugger_probe_budgets.py::Test_Debugger_Probe_Budgets::test_log_line_capture_expression_budgets: missing_feature tests/debugger/test_debugger_probe_budgets.py::Test_Debugger_Probe_Budgets::test_span_probe_expression_budgets: irrelevant tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots: missing_feature - tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_capture_limits_emit_incomplete: missing_feature (Debugger incomplete capture reason coverage not implemented yet) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_process_tags_snapshot: missing_feature (Not yet implemented) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_process_tags_snapshot_svc: missing_feature tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots_With_SCM: missing_feature diff --git a/manifests/python.yml b/manifests/python.yml index 867881ddff7..8319b09fa11 100644 --- a/manifests/python.yml +++ b/manifests/python.yml @@ -1274,7 +1274,6 @@ manifest: - weblog_declaration: "*": missing_feature *flask: v2.11.0 - tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_capture_limits_emit_incomplete: missing_feature (Debugger incomplete capture reason coverage not implemented yet) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_log_line_snapshot_debug_track: - declaration: missing_feature (Python 3.15.0 introduced the track change) component_version: <3.15.0 diff --git a/manifests/ruby.yml b/manifests/ruby.yml index 825664f209b..e403a7edd94 100644 --- a/manifests/ruby.yml +++ b/manifests/ruby.yml @@ -1814,7 +1814,6 @@ manifest: rails72: v2.8.0 rails80: v2.8.0 uds-rails: v2.12.0 - tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_capture_limits_emit_incomplete: missing_feature (Debugger incomplete capture reason coverage not implemented yet) tests/debugger/test_debugger_probe_snapshot.py::Test_Debugger_Line_Probe_Snaphots::test_default_max_reference_depth: - declaration: bug (DEBUG-4675) component_version: '<2.34.0-dev' diff --git a/tests/debugger/test_debugger_condition_errors.py b/tests/debugger/test_debugger_condition_errors.py index c5dc8af6b27..303b023ff27 100644 --- a/tests/debugger/test_debugger_condition_errors.py +++ b/tests/debugger/test_debugger_condition_errors.py @@ -7,40 +7,10 @@ import time import tests.debugger.utils as debugger +from tests.debugger.utils import captures_contain_data from utils import context, features, scenarios -def _capture_point_contains_data(point: object) -> bool: - """Return True iff a single capture point (entry / return / one line entry) contains data.""" - if not isinstance(point, dict): - return False - for key in ("arguments", "locals", "staticFields"): - if point.get(key): - return True - return bool(point.get("throwable")) - - -def _captures_contain_data(captures: object) -> bool: - """Return True iff the ``captures`` field of a snapshot contains any captured probe data. - - A snapshot whose ``captures`` is missing, ``None``, or only contains empty - ``entry`` / ``return`` / ``lines`` sub-structures (i.e. no captured arguments, - locals, static fields, or throwable) returns False -- it is an empty captures - container and acceptable as part of an evaluation-error snapshot. - """ - if not isinstance(captures, dict): - return False - for key, value in captures.items(): - # `entry` and `return` map directly to a capture-point dict. - # `lines` maps line-numbers to capture-point dicts, so descend one level. - if key == "lines" and isinstance(value, dict): - if any(_capture_point_contains_data(v) for v in value.values()): - return True - elif _capture_point_contains_data(value): - return True - return False - - class _ConditionTestBase(debugger.BaseDebuggerTest): """Shared base for the condition-error compliance tests.""" @@ -214,7 +184,7 @@ def test_runtime_condition_error_emits_error_only_snapshot(self) -> None: ) captures = snap.get("captures") - assert not _captures_contain_data(captures), ( + assert not captures_contain_data(captures), ( f"The probe emitted a snapshot whose ``captures`` field contains captured " f"data ({captures!r}); an eval-error snapshot must have empty captures " f"because the condition was not successfully evaluated." diff --git a/tests/debugger/test_debugger_guardrails.py b/tests/debugger/test_debugger_guardrails.py index defec871ee0..8b720fd29f8 100644 --- a/tests/debugger/test_debugger_guardrails.py +++ b/tests/debugger/test_debugger_guardrails.py @@ -10,16 +10,24 @@ MAX_SNAPSHOT_BYTES = 1024 * 1024 -GUARDRAILS_RFC = ( - "https://docs.google.com/document/d/1OhCH3SMuS_B4Ickays94GpqDlqKcc9b9gLos1T85F-Q/edit?usp=sharing" -) +# `^(a+)+$` against a non-matching input backtracks exponentially (~2^n), so a few dozen +# characters already blow past any evaluation-time budget. Keep it small so the value fits +# comfortably in the request URL. +REDOS_INPUT_LENGTH = 50 +GUARDRAILS_RFC = "https://docs.google.com/document/d/1OhCH3SMuS_B4Ickays94GpqDlqKcc9b9gLos1T85F-Q/edit?usp=sharing" @rfc(GUARDRAILS_RFC) @features.debugger_expression_language @scenarios.debugger_probes_snapshot +@slow class Test_Debugger_Evaluation_Timeout(debugger.BaseDebuggerTest): - """RFC guardrail: expensive expression evaluation must stop before normal snapshot creation.""" + """RFC guardrail: expensive expression evaluation must stop before normal snapshot creation. + + A conforming tracer aborts the expensive ``when`` evaluation and surfaces the failure as an + evaluation-error snapshot (non-empty ``evaluationErrors[]`` with no captured user data), + exactly like a runtime condition error -- it must not produce a normal, fully-captured snapshot. + """ def _convert_to_line_probe_if_needed(self, probe: dict[str, Any], method: str) -> None: if context.library.name not in ("nodejs", "ruby"): @@ -45,18 +53,20 @@ def _setup_evaluation_timeout(self, probes_name: str, method: str, request_path: self.setup_failures.append("Probes did not reach INSTALLED status within 30s") self.weblog_responses.append(weblog.get(request_path, timeout=15)) - if not self.wait_for_all_probes(statuses=["EMITTING"], timeout=5): - self.setup_failures.append("Probes did not reach EMITTING status within 5s") + # A conforming tracer emits an evaluation-error snapshot; wait for it to land before + # collect(). Tracers that (wrongly) emit nothing just hit the timeout and fail the + # snapshot-presence assertion in the test with a clear message. + self.wait_for_all_snapshots(timeout=10) def setup_evaluation_timeout_regex(self) -> None: self._setup_evaluation_timeout( "probe_evaluation_timeout_regex", "StringOperations", - f"/debugger/expression/strings?strValue={'a' * 30000}!", + f"/debugger/expression/strings?strValue={'a' * REDOS_INPUT_LENGTH}!", ) def test_evaluation_timeout_regex(self) -> None: - self._assert_no_normal_snapshot() + self._assert_evaluation_timeout_snapshot() def setup_evaluation_timeout_collection_filter(self) -> None: self._setup_evaluation_timeout( @@ -66,9 +76,9 @@ def setup_evaluation_timeout_collection_filter(self) -> None: ) def test_evaluation_timeout_collection_filter(self) -> None: - self._assert_no_normal_snapshot() + self._assert_evaluation_timeout_snapshot() - def _assert_no_normal_snapshot(self) -> None: + def _assert_evaluation_timeout_snapshot(self) -> None: self.collect() self.assert_setup_ok() self.assert_rc_state_not_error() @@ -76,9 +86,24 @@ def _assert_no_normal_snapshot(self) -> None: for probe_id in self.probe_ids: snapshots = self.probe_snapshots.get(probe_id, []) - assert not snapshots, ( - f"Probe emitted {len(snapshots)} snapshot(s) after evaluation timeout. " - "A conforming tracer must skip event creation when expression evaluation exceeds 1 second." + assert snapshots, ( + "Probe emitted no snapshot; a conforming tracer must surface the aborted " + "expression evaluation via a probe result carrying evaluationErrors[]." + ) + + envelope = snapshots[0] + snapshot = envelope.get("debugger", {}).get("snapshot") or envelope.get("debugger.snapshot") or {} + + evaluation_errors = snapshot.get("evaluationErrors") or [] + assert evaluation_errors, ( + "Evaluation-timeout snapshot has an empty evaluationErrors[]; the tracer must " + "report that expression evaluation was aborted." + ) + + captures = snapshot.get("captures") + assert not debugger.captures_contain_data(captures), ( + f"Evaluation-timeout snapshot leaked captured data ({captures!r}); a conforming " + "tracer must stop before normal snapshot creation." ) @@ -100,9 +125,7 @@ def _setup_snapshot_guardrail(self, probes_name: str, collection_size: int) -> N probe["id"] = debugger.generate_probe_id("log") if "methodName" in probe["where"]: del probe["where"]["methodName"] - probe["where"]["lines"] = [ - str(line) for line in self.method_and_language_to_line_number("SnapshotLimits", context.library.name) - ] + probe["where"]["lines"] = self.method_and_language_to_line_number("SnapshotLimits", context.library.name) probe["where"]["sourceFile"] = "ACTUAL_SOURCE_FILE" probe["where"]["typeName"] = None @@ -111,7 +134,9 @@ def _setup_snapshot_guardrail(self, probes_name: str, collection_size: int) -> N if not self.wait_for_all_probes(statuses=["INSTALLED"], timeout=30): self.setup_failures.append("Probes did not reach INSTALLED status within 30s") - self.weblog_responses.append(weblog.get(f"/debugger/snapshot/limits?collectionSize={collection_size}", timeout=30)) + self.weblog_responses.append( + weblog.get(f"/debugger/snapshot/limits?collectionSize={collection_size}", timeout=30) + ) if not self.wait_for_all_probes(statuses=["EMITTING"], timeout=10): self.setup_failures.append("Probes did not reach EMITTING status within 10s") if not self.wait_for_all_snapshots(timeout=30): @@ -142,8 +167,7 @@ def test_capture_timeout_reason(self) -> None: assert "timeout" in reasons, f"Expected notCapturedReason='timeout', got reasons: {sorted(reasons)}" unexpected_reasons = {"collectionSize", "stringLength", "fieldCount", "depth"} & reasons assert not unexpected_reasons, ( - "Capture timeout fixture should avoid structural limit reasons, " - f"but found: {sorted(unexpected_reasons)}" + f"Capture timeout fixture should avoid structural limit reasons, but found: {sorted(unexpected_reasons)}" ) def _get_single_snapshot(self) -> tuple[dict[str, Any], dict[str, Any]]: @@ -166,7 +190,8 @@ def _get_single_snapshot(self) -> tuple[dict[str, Any], dict[str, Any]]: def _get_captured_local(self, snapshot: dict[str, Any], variable_name: str) -> dict[str, Any]: captures = snapshot.get("captures", {}) lines = captures.get("lines", {}) - assert isinstance(lines, dict) and len(lines) == 1, f"Expected one line capture, got: {lines!r}" + assert isinstance(lines, dict), f"Expected line captures to be a dict, got: {lines!r}" + assert len(lines) == 1, f"Expected one line capture, got: {lines!r}" line_data = next(iter(lines.values())) locals_data = line_data.get("locals", {}) @@ -175,7 +200,7 @@ def _get_captured_local(self, snapshot: dict[str, Any], variable_name: str) -> d assert isinstance(value, dict), f"Expected {variable_name!r} to be a captured object, got: {value!r}" return value - def _is_partially_captured(self, value: Any, expected_items: int) -> bool: + def _is_partially_captured(self, value: object, expected_items: int) -> bool: if not isinstance(value, dict): return False @@ -188,7 +213,7 @@ def _is_partially_captured(self, value: Any, expected_items: int) -> bool: return any(self._is_partially_captured(child, expected_items) for child in value.values()) - def _iter_not_captured_reasons(self, value: Any) -> list[str]: + def _iter_not_captured_reasons(self, value: object) -> list[str]: if isinstance(value, dict): reasons = [] reason = value.get("notCapturedReason") diff --git a/tests/debugger/test_debugger_probe_snapshot.py b/tests/debugger/test_debugger_probe_snapshot.py index 1d3fd250263..afb4a8e1020 100644 --- a/tests/debugger/test_debugger_probe_snapshot.py +++ b/tests/debugger/test_debugger_probe_snapshot.py @@ -252,10 +252,6 @@ def setup_default_max_length(self): """Setup test for default maxLength""" self._setup_default_capture_limits() - def setup_capture_limits_emit_incomplete(self) -> None: - """Setup test for RFC guardrail incomplete-capture reason coverage.""" - self._setup_default_capture_limits() - def _setup_default_capture_limits(self): """Shared setup method for default capture limit tests""" test_depth = self.DEFAULT_MAX_REFERENCE_DEPTH + 7 @@ -385,22 +381,6 @@ def test_default_max_length(self): assert len(string_value) <= self.DEFAULT_MAX_LENGTH, ( f"longString should have length {self.DEFAULT_MAX_LENGTH}, got: {len(string_value)}" ) - - def test_capture_limits_emit_incomplete(self) -> None: - """Test that all structural guardrails report incomplete capture reasons.""" - deep_object = self._get_snapshot_locals_variable("deepObject") - many_fields = self._get_snapshot_locals_variable("manyFields") - large_collection = self._get_snapshot_locals_variable("largeCollection") - long_string = self._get_snapshot_locals_variable("longString") - - self._measure_captured_depth(deep_object) - assert many_fields.get("notCapturedReason") == "fieldCount", ( - f"manyFields should have notCapturedReason='fieldCount', got: {many_fields.get('notCapturedReason')}" - ) - assert large_collection.get("notCapturedReason") == "collectionSize", ( - "largeCollection should have notCapturedReason='collectionSize', " - f"got: {large_collection.get('notCapturedReason')}" - ) assert long_string.get("notCapturedReason") == "stringLength", ( f"longString should have notCapturedReason='stringLength', got: {long_string.get('notCapturedReason')}" ) diff --git a/tests/debugger/utils.py b/tests/debugger/utils.py index 3ebba34bd82..82ec0037c63 100644 --- a/tests/debugger/utils.py +++ b/tests/debugger/utils.py @@ -114,6 +114,37 @@ def get_env_bool(env_var_name: str, *, default: bool = False) -> bool: return value in {"true", "True", "1"} +def capture_point_contains_data(point: object) -> bool: + """Return True iff a single capture point (entry / return / one line entry) contains data.""" + if not isinstance(point, dict): + return False + for key in ("arguments", "locals", "staticFields"): + if point.get(key): + return True + return bool(point.get("throwable")) + + +def captures_contain_data(captures: object) -> bool: + """Return True iff the ``captures`` field of a snapshot contains any captured probe data. + + A snapshot whose ``captures`` is missing, ``None``, or only contains empty + ``entry`` / ``return`` / ``lines`` sub-structures (i.e. no captured arguments, + locals, static fields, or throwable) returns False -- it is an empty captures + container and acceptable as part of an evaluation-error snapshot. + """ + if not isinstance(captures, dict): + return False + for key, value in captures.items(): + # `entry` and `return` map directly to a capture-point dict. + # `lines` maps line-numbers to capture-point dicts, so descend one level. + if key == "lines" and isinstance(value, dict): + if any(capture_point_contains_data(v) for v in value.values()): + return True + elif capture_point_contains_data(value): + return True + return False + + class BaseDebuggerTest: tracer: dict[str, str] = {} From 8b0d7f82ffc993f65178cf112c27f504098abd07 Mon Sep 17 00:00:00 2001 From: dudik <8845578+dudikeleti@users.noreply.github.com> Date: Fri, 10 Jul 2026 16:58:52 +0200 Subject: [PATCH 5/8] fix ci --- manifests/nodejs.yml | 4 ++-- manifests/ruby.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/manifests/nodejs.yml b/manifests/nodejs.yml index ab6ae917203..02f56fe8ea2 100644 --- a/manifests/nodejs.yml +++ b/manifests/nodejs.yml @@ -1605,8 +1605,6 @@ manifest: - weblog_declaration: "*": *ref_5_48_0 nextjs: irrelevant - tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) - tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) ? tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_access_exception : missing_feature (Not yet implemented) ? tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_contextual_variables @@ -1614,6 +1612,8 @@ manifest: tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_instance_of: - weblog_declaration: express4-typescript: bug (DEBUG-3715) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: - weblog_declaration: "*": *ref_5_83_0 diff --git a/manifests/ruby.yml b/manifests/ruby.yml index e403a7edd94..41f5bf2a5bc 100644 --- a/manifests/ruby.yml +++ b/manifests/ruby.yml @@ -1775,11 +1775,11 @@ manifest: rails72: v2.20.0 rails80: v2.20.0 uds-rails: v2.20.0 - tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) - tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_hash_operations: - declaration: missing_feature (Hash length not implemented) component_version: <=2.22.0 + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: missing_feature tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin_Default_On: irrelevant (Scoped to dotnet rollout in this PR) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Dynamic_Instrumentation: missing_feature From e09ad0aa3e0b0cdc9b7cc36163451d693ab42a55 Mon Sep 17 00:00:00 2001 From: dudik <8845578+dudikeleti@users.noreply.github.com> Date: Fri, 10 Jul 2026 17:39:47 +0200 Subject: [PATCH 6/8] Validate the established `truncated` flag instead of requiring an unsupported `notCapturedReason`. --- tests/debugger/test_debugger_probe_snapshot.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/debugger/test_debugger_probe_snapshot.py b/tests/debugger/test_debugger_probe_snapshot.py index afb4a8e1020..acdf441822d 100644 --- a/tests/debugger/test_debugger_probe_snapshot.py +++ b/tests/debugger/test_debugger_probe_snapshot.py @@ -381,9 +381,7 @@ def test_default_max_length(self): assert len(string_value) <= self.DEFAULT_MAX_LENGTH, ( f"longString should have length {self.DEFAULT_MAX_LENGTH}, got: {len(string_value)}" ) - assert long_string.get("notCapturedReason") == "stringLength", ( - f"longString should have notCapturedReason='stringLength', got: {long_string.get('notCapturedReason')}" - ) + assert long_string.get("truncated") is True, "longString should be marked as truncated" def setup_log_line_snapshot_debug_track(self): self.use_debugger_endpoint = True From b234701a0d1b8132e1763873ba318eafd0b46c7e Mon Sep 17 00:00:00 2001 From: dudik <8845578+dudikeleti@users.noreply.github.com> Date: Fri, 10 Jul 2026 18:10:48 +0200 Subject: [PATCH 7/8] validate timeout reporting on the intended captured collection without relying on runner timing. --- tests/debugger/test_debugger_guardrails.py | 41 ++++++++-------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/tests/debugger/test_debugger_guardrails.py b/tests/debugger/test_debugger_guardrails.py index 8b720fd29f8..4c42fb671be 100644 --- a/tests/debugger/test_debugger_guardrails.py +++ b/tests/debugger/test_debugger_guardrails.py @@ -114,8 +114,8 @@ def _assert_evaluation_timeout_snapshot(self) -> None: class Test_Debugger_Snapshot_Guardrails(debugger.BaseDebuggerTest): """RFC guardrails for completed snapshot size and capture-time budget.""" - SNAPSHOT_SIZE_COLLECTION_ITEMS = 300000 - CAPTURE_TIMEOUT_COLLECTION_ITEMS = 1000000 + SNAPSHOT_SIZE_COLLECTION_ITEMS = 300_000 + CAPTURE_TIMEOUT_COLLECTION_ITEMS = 1_000_000 def _setup_snapshot_guardrail(self, probes_name: str, collection_size: int) -> None: self.initialize_weblog_remote_config() @@ -158,16 +158,21 @@ def test_snapshot_size_cap(self) -> None: f"got: {large_collection!r}" ) - def setup_capture_timeout_reason(self) -> None: + def setup_capture_timeout_reports_reason(self) -> None: self._setup_snapshot_guardrail("probe_capture_timeout_reason", self.CAPTURE_TIMEOUT_COLLECTION_ITEMS) - def test_capture_timeout_reason(self) -> None: + def test_capture_timeout_reports_reason(self) -> None: + """A capture exceeding the tracer's time budget identifies the value it could not finish. + + The probe allows twice as many collection items as the fixture contains, so the + collection-size limit cannot cause the truncation. Tracers use budgets between 50 and + 250 ms, so we assert the reported reason instead of elapsed time. + """ _, snapshot = self._get_single_snapshot() - reasons = set(self._iter_not_captured_reasons(snapshot)) - assert "timeout" in reasons, f"Expected notCapturedReason='timeout', got reasons: {sorted(reasons)}" - unexpected_reasons = {"collectionSize", "stringLength", "fieldCount", "depth"} & reasons - assert not unexpected_reasons, ( - f"Capture timeout fixture should avoid structural limit reasons, but found: {sorted(unexpected_reasons)}" + large_collection = self._get_captured_local(snapshot, "largeCollection") + assert large_collection.get("notCapturedReason") == "timeout", ( + "largeCollection should have notCapturedReason='timeout' when capture exceeds the tracer's " + f"time budget, got: {large_collection!r}" ) def _get_single_snapshot(self) -> tuple[dict[str, Any], dict[str, Any]]: @@ -212,21 +217,3 @@ def _is_partially_captured(self, value: object, expected_items: int) -> bool: return True return any(self._is_partially_captured(child, expected_items) for child in value.values()) - - def _iter_not_captured_reasons(self, value: object) -> list[str]: - if isinstance(value, dict): - reasons = [] - reason = value.get("notCapturedReason") - if isinstance(reason, str): - reasons.append(reason) - for child in value.values(): - reasons.extend(self._iter_not_captured_reasons(child)) - return reasons - - if isinstance(value, list): - reasons = [] - for child in value: - reasons.extend(self._iter_not_captured_reasons(child)) - return reasons - - return [] From 2c71ee0c952fa6c24eba491fc4fc8a0ea034777d Mon Sep 17 00:00:00 2001 From: dudik <8845578+dudikeleti@users.noreply.github.com> Date: Fri, 10 Jul 2026 18:11:02 +0200 Subject: [PATCH 8/8] Enable debugger capture-timeout coverage across tracers --- manifests/dotnet.yml | 2 +- manifests/golang.yml | 2 +- manifests/java.yml | 2 +- manifests/nodejs.yml | 2 +- manifests/php.yml | 2 +- manifests/python.yml | 2 +- manifests/ruby.yml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/manifests/dotnet.yml b/manifests/dotnet.yml index 63d93bd6f69..af8212bfdb5 100644 --- a/manifests/dotnet.yml +++ b/manifests/dotnet.yml @@ -655,7 +655,7 @@ manifest: ? tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_string_operations : bug (DEBUG-2560) tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) - tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails::test_snapshot_size_cap: missing_feature (Debugger snapshot size guardrail not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: bug (DEBUG-4637) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin_Default_On: missing_feature (Temporarily disabled; enable from v3.42.0 later) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Dynamic_Instrumentation: bug (DEBUG-4637) diff --git a/manifests/golang.yml b/manifests/golang.yml index dc35c8b05d6..8b2e3c9e5cf 100644 --- a/manifests/golang.yml +++ b/manifests/golang.yml @@ -1075,7 +1075,7 @@ manifest: tests/debugger/test_debugger_exception_replay.py::Test_Debugger_Exception_Replay::test_exception_replay_stackoverflow: missing_feature (Implemented only for dotnet) tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language: missing_feature (feature not implemented) tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) - tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails::test_snapshot_size_cap: missing_feature (Debugger snapshot size guardrail not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: missing_feature tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin_Default_On: irrelevant (Scoped to dotnet rollout in this PR) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Dynamic_Instrumentation: missing_feature diff --git a/manifests/java.yml b/manifests/java.yml index ea5071c2e4b..035500b8914 100644 --- a/manifests/java.yml +++ b/manifests/java.yml @@ -3042,7 +3042,7 @@ manifest: spring-boot-wildfly: v1.38.0 uds-spring-boot: v1.33.0 tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) - tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails::test_snapshot_size_cap: missing_feature (Debugger snapshot size guardrail not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: - weblog_declaration: "*": missing_feature diff --git a/manifests/nodejs.yml b/manifests/nodejs.yml index 02f56fe8ea2..41ed7a3f766 100644 --- a/manifests/nodejs.yml +++ b/manifests/nodejs.yml @@ -1613,7 +1613,7 @@ manifest: - weblog_declaration: express4-typescript: bug (DEBUG-3715) tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) - tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails::test_snapshot_size_cap: missing_feature (Debugger snapshot size guardrail not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: - weblog_declaration: "*": *ref_5_83_0 diff --git a/manifests/php.yml b/manifests/php.yml index a72033f4c51..9d72eb43eaf 100644 --- a/manifests/php.yml +++ b/manifests/php.yml @@ -652,7 +652,7 @@ manifest: ? tests/debugger/test_debugger_expression_language.py::Test_Debugger_Expression_Language::test_expression_language_string_operations : '>=1.16.0' tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) - tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails::test_snapshot_size_cap: missing_feature (Debugger snapshot size guardrail not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: missing_feature (code origin entry spans not captured in PHP-FPM; RC toggle works but no spans to verify) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin_Default_On: irrelevant (Scoped to dotnet rollout in this PR) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Dynamic_Instrumentation: diff --git a/manifests/python.yml b/manifests/python.yml index 8319b09fa11..61f675fb8b7 100644 --- a/manifests/python.yml +++ b/manifests/python.yml @@ -1224,7 +1224,7 @@ manifest: "*": missing_feature *flask: v2.11.0 tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) - tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails::test_snapshot_size_cap: missing_feature (Debugger snapshot size guardrail not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: - weblog_declaration: "*": missing_feature diff --git a/manifests/ruby.yml b/manifests/ruby.yml index 41f5bf2a5bc..f72642f0d9b 100644 --- a/manifests/ruby.yml +++ b/manifests/ruby.yml @@ -1779,7 +1779,7 @@ manifest: - declaration: missing_feature (Hash length not implemented) component_version: <=2.22.0 tests/debugger/test_debugger_guardrails.py::Test_Debugger_Evaluation_Timeout: missing_feature (Debugger evaluation timeout guardrail not implemented yet) - tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails: missing_feature (Debugger snapshot size and capture timeout guardrails not implemented yet) + tests/debugger/test_debugger_guardrails.py::Test_Debugger_Snapshot_Guardrails::test_snapshot_size_cap: missing_feature (Debugger snapshot size guardrail not implemented yet) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin: missing_feature tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Code_Origin_Default_On: irrelevant (Scoped to dotnet rollout in this PR) tests/debugger/test_debugger_inproduct_enablement.py::Test_Debugger_InProduct_Enablement_Dynamic_Instrumentation: missing_feature