|
| 1 | +from typing import Any |
| 2 | + |
| 3 | +import pytest |
| 4 | + |
| 5 | +from ngraph.failure.conditions import ( |
| 6 | + FailureCondition, |
| 7 | + evaluate_condition, |
| 8 | + evaluate_conditions, |
| 9 | +) |
| 10 | + |
| 11 | + |
| 12 | +class TestEvaluateCondition: |
| 13 | + def test_equality_and_inequality(self) -> None: |
| 14 | + attrs = {"x": 5, "y": "abc"} |
| 15 | + assert evaluate_condition(attrs, FailureCondition("x", "==", 5)) is True |
| 16 | + assert evaluate_condition(attrs, FailureCondition("x", "!=", 6)) is True |
| 17 | + assert evaluate_condition(attrs, FailureCondition("y", "==", "abc")) is True |
| 18 | + |
| 19 | + def test_ordering_with_none_guard(self) -> None: |
| 20 | + attrs = {"a": 3, "b": None} |
| 21 | + assert evaluate_condition(attrs, FailureCondition("a", ">", 2)) is True |
| 22 | + assert evaluate_condition(attrs, FailureCondition("a", ">=", 3)) is True |
| 23 | + assert evaluate_condition(attrs, FailureCondition("a", "<", 10)) is True |
| 24 | + assert evaluate_condition(attrs, FailureCondition("a", "<=", 3)) is True |
| 25 | + # None comparisons must return False rather than raising |
| 26 | + assert evaluate_condition(attrs, FailureCondition("b", ">", 0)) is False |
| 27 | + assert evaluate_condition(attrs, FailureCondition("missing", "<", 0)) is False |
| 28 | + |
| 29 | + def test_contains_and_not_contains(self) -> None: |
| 30 | + attrs = {"s": "hello", "l": [1, 2, 3], "n": None, "i": 123} |
| 31 | + assert ( |
| 32 | + evaluate_condition(attrs, FailureCondition("s", "contains", "ell")) is True |
| 33 | + ) |
| 34 | + assert evaluate_condition(attrs, FailureCondition("l", "contains", 2)) is True |
| 35 | + assert ( |
| 36 | + evaluate_condition(attrs, FailureCondition("s", "not_contains", "xyz")) |
| 37 | + is True |
| 38 | + ) |
| 39 | + # None yields False for contains and True for not_contains |
| 40 | + assert evaluate_condition(attrs, FailureCondition("n", "contains", 1)) is False |
| 41 | + assert ( |
| 42 | + evaluate_condition(attrs, FailureCondition("n", "not_contains", 1)) is True |
| 43 | + ) |
| 44 | + # Non-iterable must not raise |
| 45 | + assert evaluate_condition(attrs, FailureCondition("i", "contains", 1)) is False |
| 46 | + assert ( |
| 47 | + evaluate_condition(attrs, FailureCondition("i", "not_contains", 1)) is True |
| 48 | + ) |
| 49 | + |
| 50 | + def test_any_value_and_no_value(self) -> None: |
| 51 | + attrs: dict[str, Any] = {"p": 0, "q": None} |
| 52 | + assert evaluate_condition(attrs, FailureCondition("p", "any_value")) is True |
| 53 | + assert evaluate_condition(attrs, FailureCondition("q", "any_value")) is True |
| 54 | + assert ( |
| 55 | + evaluate_condition(attrs, FailureCondition("missing", "any_value")) is False |
| 56 | + ) |
| 57 | + assert ( |
| 58 | + evaluate_condition(attrs, FailureCondition("missing", "no_value")) is True |
| 59 | + ) |
| 60 | + assert evaluate_condition(attrs, FailureCondition("q", "no_value")) is True |
| 61 | + assert evaluate_condition(attrs, FailureCondition("p", "no_value")) is False |
| 62 | + |
| 63 | + def test_unsupported_operator_raises(self) -> None: |
| 64 | + with pytest.raises(ValueError, match="Unsupported operator"): |
| 65 | + evaluate_condition({}, FailureCondition("x", "bad")) |
| 66 | + |
| 67 | + |
| 68 | +class TestEvaluateConditions: |
| 69 | + def test_and_or_logic(self) -> None: |
| 70 | + attrs = {"x": 10, "y": "abc"} |
| 71 | + conds = [FailureCondition("x", ">", 5), FailureCondition("y", "==", "abc")] |
| 72 | + assert evaluate_conditions(attrs, conds, "and") is True |
| 73 | + assert evaluate_conditions(attrs, conds, "or") is True |
| 74 | + |
| 75 | + conds2 = [FailureCondition("x", "<", 5), FailureCondition("y", "!=", "abc")] |
| 76 | + assert evaluate_conditions(attrs, conds2, "and") is False |
| 77 | + assert evaluate_conditions(attrs, conds2, "or") is False |
| 78 | + |
| 79 | + def test_unsupported_logic(self) -> None: |
| 80 | + with pytest.raises(ValueError, match="Unsupported logic"): |
| 81 | + evaluate_conditions({}, [], "xor") |
0 commit comments