Skip to content

Commit 6542ece

Browse files
committed
test(ffe): add parse_error test for invalid regex
1 parent 5846f99 commit 6542ece

1 file changed

Lines changed: 100 additions & 1 deletion

File tree

tests/ffe/test_flag_eval_metrics.py

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,48 @@ def make_split_fixture(flag_key: str):
374374
}
375375

376376

377+
def make_invalid_regex_fixture(flag_key: str, invalid_regex: str = "[invalid"):
378+
"""Create a UFC fixture with an invalid regex pattern in a MATCHES condition.
379+
380+
This tests the PARSE_ERROR scenario where the configuration contains
381+
a syntactically invalid regex pattern that fails during evaluation.
382+
"""
383+
return {
384+
"createdAt": "2024-04-17T19:40:53.716Z",
385+
"format": "SERVER",
386+
"environment": {"name": "Test"},
387+
"flags": {
388+
flag_key: {
389+
"key": flag_key,
390+
"enabled": True,
391+
"variationType": "STRING",
392+
"variations": {
393+
"on": {"key": "on", "value": "on-value"},
394+
"off": {"key": "off", "value": "off-value"},
395+
},
396+
"allocations": [
397+
{
398+
"key": "regex-allocation",
399+
"rules": [
400+
{
401+
"conditions": [
402+
{
403+
"operator": "MATCHES",
404+
"attribute": "email",
405+
"value": invalid_regex, # Invalid regex pattern
406+
}
407+
]
408+
}
409+
],
410+
"splits": [{"variationKey": "on", "shards": []}],
411+
"doLog": True,
412+
}
413+
],
414+
}
415+
},
416+
}
417+
418+
377419
@scenarios.feature_flagging_and_experimentation
378420
@features.feature_flags_eval_metrics
379421
class Test_FFE_Eval_Reason_Targeting:
@@ -543,7 +585,7 @@ def test_ffe_eval_reason_disabled(self):
543585
# -----------------------|---------------------------------------------
544586
# FLAG_NOT_FOUND | Test_FFE_Eval_Config_Exists_Flag_Missing
545587
# TYPE_MISMATCH | Test_FFE_Eval_Metric_Type_Mismatch, Test_FFE_Eval_Metric_Numeric_To_Integer
546-
# PARSE_ERROR | (not tested - no cross-SDK consistent scenario)
588+
# PARSE_ERROR | Test_FFE_Eval_Metric_Parse_Error
547589
# GENERAL | (not tested - catch-all error code)
548590
# TARGETING_KEY_MISSING | Test_FFE_Eval_Targeting_Key_Optional (verifies it's NOT returned; JS excluded)
549591
# INVALID_CONTEXT | Test_FFE_Eval_Invalid_Context_Nested_Attribute (Python only)
@@ -712,6 +754,63 @@ def test_ffe_eval_metric_numeric_to_integer(self):
712754
)
713755

714756

757+
@scenarios.feature_flagging_and_experimentation
758+
@features.feature_flags_eval_metrics
759+
@irrelevant(
760+
context.library == "golang",
761+
reason="Go validates regex at config load time and rejects invalid patterns upfront",
762+
)
763+
class Test_FFE_Eval_Metric_Parse_Error:
764+
"""Test that an invalid regex pattern produces error.type=parse_error.
765+
766+
This configures a flag with a MATCHES condition containing an invalid regex pattern
767+
(e.g., "[invalid" which has an unclosed bracket). When the condition is evaluated,
768+
the regex compilation fails and produces a parse_error.
769+
770+
Behavioral differences across SDKs:
771+
- Python (libdatadog): Returns parse_error during evaluation
772+
- Go: Validates regex at config load time, rejects config with invalid regex
773+
"""
774+
775+
def setup_ffe_eval_metric_parse_error(self):
776+
rc.tracer_rc_state.reset().apply()
777+
778+
config_id = "ffe-eval-metric-parse-error"
779+
self.flag_key = "eval-metric-parse-error-flag"
780+
rc.tracer_rc_state.set_config(
781+
f"{RC_PATH}/{config_id}/config", make_invalid_regex_fixture(self.flag_key)
782+
).apply()
783+
784+
# Evaluate the flag with an attribute that triggers the invalid regex condition
785+
self.r = weblog.post(
786+
"/ffe",
787+
json={
788+
"flag": self.flag_key,
789+
"variationType": "STRING",
790+
"defaultValue": "default",
791+
"targetingKey": "user-1",
792+
"attributes": {"email": "test@example.com"}, # Triggers MATCHES condition
793+
},
794+
)
795+
796+
def test_ffe_eval_metric_parse_error(self):
797+
"""Test that invalid regex produces error.type:parse_error."""
798+
assert self.r.status_code == 200, f"Flag evaluation request failed: {self.r.text}"
799+
800+
metrics = find_eval_metrics(self.flag_key)
801+
assert len(metrics) > 0, f"Expected metric for flag '{self.flag_key}', found none. All: {find_eval_metrics()}"
802+
803+
point = metrics[0]
804+
tags = point.get("tags", [])
805+
806+
assert get_tag_value(tags, "feature_flag.result.reason") == "error", (
807+
f"Expected reason 'error' for parse error, got tags: {tags}"
808+
)
809+
assert get_tag_value(tags, "error.type") == "parse_error", (
810+
f"Expected error.type 'parse_error', got tags: {tags}"
811+
)
812+
813+
715814
@scenarios.feature_flagging_and_experimentation
716815
@features.feature_flags_eval_metrics
717816
class Test_FFE_Eval_No_Config_Loaded:

0 commit comments

Comments
 (0)