721 lines
21 KiB
Python
721 lines
21 KiB
Python
|
|
"""
|
||
|
|
Unit Tests für logic_evaluator.py (Phase 3)
|
||
|
|
|
||
|
|
Run with: PYTHONPATH=./backend pytest tests/backend/test_phase3_logic_evaluator.py -v
|
||
|
|
"""
|
||
|
|
import pytest
|
||
|
|
from logic_evaluator import (
|
||
|
|
evaluate_logic_expression,
|
||
|
|
resolve_signal_reference,
|
||
|
|
compare_values
|
||
|
|
)
|
||
|
|
from workflow_models import (
|
||
|
|
LogicExpression,
|
||
|
|
LogicOperator,
|
||
|
|
NormalizedSignal,
|
||
|
|
SignalStatus,
|
||
|
|
NodeExecutionState,
|
||
|
|
NodeStatus
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
# ── Comparison Operator Tests ──────────────────────────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_eq_true():
|
||
|
|
"""Test: EQ operator - match"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="body.relevanz",
|
||
|
|
value="decrease"
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="relevanz",
|
||
|
|
raw_value="decrease",
|
||
|
|
normalized_value="decrease",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is True
|
||
|
|
assert error is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_eq_false():
|
||
|
|
"""Test: EQ operator - no match"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="body.relevanz",
|
||
|
|
value="increase"
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="relevanz",
|
||
|
|
raw_value="decrease",
|
||
|
|
normalized_value="decrease",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is False
|
||
|
|
assert error is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_neq():
|
||
|
|
"""Test: NEQ operator"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.NEQ,
|
||
|
|
ref="body.relevanz",
|
||
|
|
value="stable"
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="relevanz",
|
||
|
|
raw_value="decrease",
|
||
|
|
normalized_value="decrease",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is True
|
||
|
|
assert error is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_in_true():
|
||
|
|
"""Test: IN operator - value in list"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.IN,
|
||
|
|
ref="body.prioritaet",
|
||
|
|
value=["hoch", "mittel"]
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="prioritaet",
|
||
|
|
raw_value="hoch",
|
||
|
|
normalized_value="hoch",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is True
|
||
|
|
assert error is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_in_false():
|
||
|
|
"""Test: IN operator - value not in list"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.IN,
|
||
|
|
ref="body.prioritaet",
|
||
|
|
value=["hoch", "mittel"]
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="prioritaet",
|
||
|
|
raw_value="niedrig",
|
||
|
|
normalized_value="niedrig",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is False
|
||
|
|
assert error is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_gt():
|
||
|
|
"""Test: GT operator - greater than"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.GT,
|
||
|
|
ref="body.score",
|
||
|
|
value=50
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="score",
|
||
|
|
raw_value="75",
|
||
|
|
normalized_value="75",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is True
|
||
|
|
assert error is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_lt():
|
||
|
|
"""Test: LT operator - less than"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.LT,
|
||
|
|
ref="body.score",
|
||
|
|
value=50
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="score",
|
||
|
|
raw_value="25",
|
||
|
|
normalized_value="25",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is True
|
||
|
|
assert error is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_contains_string():
|
||
|
|
"""Test: CONTAINS operator - string contains substring"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.CONTAINS,
|
||
|
|
ref="body.kategorie",
|
||
|
|
value="Gewicht"
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="kategorie",
|
||
|
|
raw_value="Gewichtsverlust positiv",
|
||
|
|
normalized_value="Gewichtsverlust positiv",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is True
|
||
|
|
assert error is None
|
||
|
|
|
||
|
|
|
||
|
|
# ── Logical Operator Tests ──────────────────────────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_and_both_true():
|
||
|
|
"""Test: AND operator - both operands true"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.AND,
|
||
|
|
operands=[
|
||
|
|
LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="body.relevanz",
|
||
|
|
value="decrease"
|
||
|
|
),
|
||
|
|
LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="activity.intensitaet",
|
||
|
|
value="hoch"
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="relevanz",
|
||
|
|
raw_value="decrease",
|
||
|
|
normalized_value="decrease",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
),
|
||
|
|
"activity": NodeExecutionState(
|
||
|
|
node_id="activity",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="intensitaet",
|
||
|
|
raw_value="hoch",
|
||
|
|
normalized_value="hoch",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is True
|
||
|
|
assert error is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_and_one_false():
|
||
|
|
"""Test: AND operator - one operand false"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.AND,
|
||
|
|
operands=[
|
||
|
|
LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="body.relevanz",
|
||
|
|
value="decrease"
|
||
|
|
),
|
||
|
|
LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="activity.intensitaet",
|
||
|
|
value="niedrig"
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="relevanz",
|
||
|
|
raw_value="decrease",
|
||
|
|
normalized_value="decrease",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
),
|
||
|
|
"activity": NodeExecutionState(
|
||
|
|
node_id="activity",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="intensitaet",
|
||
|
|
raw_value="hoch",
|
||
|
|
normalized_value="hoch",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is False
|
||
|
|
assert error is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_or_one_true():
|
||
|
|
"""Test: OR operator - one operand true"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.OR,
|
||
|
|
operands=[
|
||
|
|
LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="body.relevanz",
|
||
|
|
value="decrease"
|
||
|
|
),
|
||
|
|
LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="activity.intensitaet",
|
||
|
|
value="niedrig"
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="relevanz",
|
||
|
|
raw_value="decrease",
|
||
|
|
normalized_value="decrease",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
),
|
||
|
|
"activity": NodeExecutionState(
|
||
|
|
node_id="activity",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="intensitaet",
|
||
|
|
raw_value="hoch",
|
||
|
|
normalized_value="hoch",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is True
|
||
|
|
assert error is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_or_both_false():
|
||
|
|
"""Test: OR operator - both operands false"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.OR,
|
||
|
|
operands=[
|
||
|
|
LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="body.relevanz",
|
||
|
|
value="increase"
|
||
|
|
),
|
||
|
|
LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="activity.intensitaet",
|
||
|
|
value="niedrig"
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="relevanz",
|
||
|
|
raw_value="decrease",
|
||
|
|
normalized_value="decrease",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
),
|
||
|
|
"activity": NodeExecutionState(
|
||
|
|
node_id="activity",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="intensitaet",
|
||
|
|
raw_value="hoch",
|
||
|
|
normalized_value="hoch",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is False
|
||
|
|
assert error is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_not():
|
||
|
|
"""Test: NOT operator - negation"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.NOT,
|
||
|
|
operands=[
|
||
|
|
LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="body.relevanz",
|
||
|
|
value="increase"
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="relevanz",
|
||
|
|
raw_value="decrease",
|
||
|
|
normalized_value="decrease",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is True # NOT (decrease == increase) = NOT False = True
|
||
|
|
assert error is None
|
||
|
|
|
||
|
|
|
||
|
|
# ── Nested Expressions Tests ──────────────────────────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_nested_and_or():
|
||
|
|
"""Test: Nested expression - (A AND B) OR C"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.OR,
|
||
|
|
operands=[
|
||
|
|
LogicExpression(
|
||
|
|
operator=LogicOperator.AND,
|
||
|
|
operands=[
|
||
|
|
LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="body.relevanz",
|
||
|
|
value="decrease"
|
||
|
|
),
|
||
|
|
LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="activity.intensitaet",
|
||
|
|
value="niedrig"
|
||
|
|
)
|
||
|
|
]
|
||
|
|
),
|
||
|
|
LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="nutrition.defizit",
|
||
|
|
value="hoch"
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="relevanz",
|
||
|
|
raw_value="decrease",
|
||
|
|
normalized_value="decrease",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
),
|
||
|
|
"activity": NodeExecutionState(
|
||
|
|
node_id="activity",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="intensitaet",
|
||
|
|
raw_value="hoch", # FALSE für AND-Teil
|
||
|
|
normalized_value="hoch",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
),
|
||
|
|
"nutrition": NodeExecutionState(
|
||
|
|
node_id="nutrition",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="defizit",
|
||
|
|
raw_value="hoch", # TRUE für OR-Teil
|
||
|
|
normalized_value="hoch",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
# (decrease AND niedrig) OR hoch = (True AND False) OR True = False OR True = True
|
||
|
|
assert result is True
|
||
|
|
assert error is None
|
||
|
|
|
||
|
|
|
||
|
|
# ── Error Handling Tests ──────────────────────────────────────────────────
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_missing_node():
|
||
|
|
"""Test: Error handling - node not found"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="missing_node.relevanz",
|
||
|
|
value="decrease"
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is False
|
||
|
|
assert error is not None
|
||
|
|
assert "not found" in error.lower()
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_missing_signal():
|
||
|
|
"""Test: Error handling - signal not found in node"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="body.missing_signal",
|
||
|
|
value="decrease"
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="relevanz",
|
||
|
|
raw_value="decrease",
|
||
|
|
normalized_value="decrease",
|
||
|
|
status=SignalStatus.VALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is False
|
||
|
|
assert error is not None
|
||
|
|
assert "not found" in error.lower()
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_unclear_signal():
|
||
|
|
"""Test: Error handling - signal has UNCLEAR status"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="body.relevanz",
|
||
|
|
value="decrease"
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="relevanz",
|
||
|
|
raw_value="maybe",
|
||
|
|
normalized_value="unklar",
|
||
|
|
status=SignalStatus.UNCLEAR
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is False
|
||
|
|
assert error is not None
|
||
|
|
assert "unclear" in error.lower() or "status" in error.lower()
|
||
|
|
|
||
|
|
|
||
|
|
def test_evaluate_invalid_signal():
|
||
|
|
"""Test: Error handling - signal has INVALID status"""
|
||
|
|
expression = LogicExpression(
|
||
|
|
operator=LogicOperator.EQ,
|
||
|
|
ref="body.relevanz",
|
||
|
|
value="decrease"
|
||
|
|
)
|
||
|
|
|
||
|
|
context = {
|
||
|
|
"node_results": {
|
||
|
|
"body": NodeExecutionState(
|
||
|
|
node_id="body",
|
||
|
|
status=NodeStatus.EXECUTED,
|
||
|
|
normalized_signals=[
|
||
|
|
NormalizedSignal(
|
||
|
|
question_type="relevanz",
|
||
|
|
raw_value="invalid_value",
|
||
|
|
normalized_value=None,
|
||
|
|
status=SignalStatus.INVALID
|
||
|
|
)
|
||
|
|
]
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result, error = evaluate_logic_expression(expression, context)
|
||
|
|
assert result is False
|
||
|
|
assert error is not None
|
||
|
|
assert "invalid" in error.lower() or "status" in error.lower()
|
||
|
|
|
||
|
|
|
||
|
|
def test_compare_values_gt_non_numeric():
|
||
|
|
"""Test: Error handling - GT with non-numeric values"""
|
||
|
|
result, error = compare_values(LogicOperator.GT, "text", 50)
|
||
|
|
assert result is False
|
||
|
|
assert error is not None
|
||
|
|
assert "cannot compare" in error.lower()
|
||
|
|
|
||
|
|
|
||
|
|
def test_compare_values_in_non_list():
|
||
|
|
"""Test: Error handling - IN with non-list right value"""
|
||
|
|
result, error = compare_values(LogicOperator.IN, "value", "not_a_list")
|
||
|
|
assert result is False
|
||
|
|
assert error is not None
|
||
|
|
assert "requires list" in error.lower()
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
pytest.main([__file__, "-v"])
|