92 lines
3.6 KiB
Python
92 lines
3.6 KiB
Python
from copy import deepcopy
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from ai_platform.presentation.http.schemas import RunRequest
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("field", "operator", "value"),
|
|
[
|
|
("custom_fields.interest.region", "eq", "north"),
|
|
("lead_profile.email", "exists", None),
|
|
("lead_profile.custom_fields.course", "contains", "IELTS"),
|
|
("metrics.profile_field_count", "gte", 1),
|
|
("metrics.conversation_count", "gte", 1),
|
|
("metrics.conversation_turn_count", "gte", 3),
|
|
("metrics.contact_started_conversation_count", "eq", 1),
|
|
("metrics.contact_message_recency_hours", "gte", 1.5),
|
|
("metrics.customer_initiated_return", "eq", True),
|
|
("metrics.customer_initiated_return", "ne", False),
|
|
("metrics.customer_message_count", "exists", None),
|
|
],
|
|
)
|
|
def test_rules_accept_supported_customer_field_paths(valid_run_payload, field, operator, value):
|
|
data = deepcopy(valid_run_payload)
|
|
data["scoring_config"]["rules"][0].update(field=field, operator=operator, value=value)
|
|
|
|
request = RunRequest.model_validate(data)
|
|
|
|
assert request.scoring_config.rules[0].field == field
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("field", "operator", "value", "points"),
|
|
[
|
|
("snapshot.conversations", "exists", None, 1),
|
|
("custom_fields..region", "eq", "north", 1),
|
|
("metrics.unknown_metric", "eq", 1, 1),
|
|
("metrics.conversation_turn_count", "eq", 1.0, 1),
|
|
("metrics.contact_started_conversation_count", "contains", 1, 1),
|
|
("metrics.customer_message_count", "gt", True, 1),
|
|
("metrics.contact_message_recency_days", "contains", 1, 1),
|
|
("metrics.contact_message_recency_hours", "contains", 1, 1),
|
|
("metrics.customer_initiated_return", "eq", 1, 1),
|
|
("metrics.customer_initiated_return", "ne", 0, 1),
|
|
("metrics.customer_initiated_return", "gt", True, 1),
|
|
("metrics.customer_initiated_return", "contains", True, 1),
|
|
("metrics.customer_initiated_return", "exists", None, 1),
|
|
("custom_fields.age", "gte", "18", 1),
|
|
("custom_fields.age", "gt", True, 1),
|
|
("custom_fields.age", "eq", None, 1),
|
|
("custom_fields.age", "exists", "not-null", 1),
|
|
("custom_fields.region", "eq", {"value": "north"}, 1),
|
|
("custom_fields.tags", "contains", ["nested"], 1),
|
|
("custom_fields.age", "eq", 18, True),
|
|
("custom_fields.age", "eq", 18, 1.5),
|
|
],
|
|
)
|
|
def test_rules_reject_invalid_paths_values_and_points(
|
|
valid_run_payload, field, operator, value, points
|
|
):
|
|
data = deepcopy(valid_run_payload)
|
|
data["scoring_config"]["rules"][0].update(
|
|
field=field, operator=operator, value=value, points=points
|
|
)
|
|
|
|
with pytest.raises(ValidationError):
|
|
RunRequest.model_validate(data)
|
|
|
|
|
|
def test_rule_values_enforce_size_limits(valid_run_payload):
|
|
data = deepcopy(valid_run_payload)
|
|
data["scoring_config"]["rules"][0]["value"] = "x" * 1_001
|
|
|
|
with pytest.raises(ValidationError, match="1000 characters"):
|
|
RunRequest.model_validate(data)
|
|
|
|
data["scoring_config"]["rules"][0].update(operator="contains", value=["x"] * 101)
|
|
with pytest.raises(ValidationError, match="100 items"):
|
|
RunRequest.model_validate(data)
|
|
|
|
|
|
def test_scoring_weights_reject_boolean_and_string_types(valid_run_payload):
|
|
for rule_weight, ai_weight in ((True, 0), (0.5, "0.5")):
|
|
data = deepcopy(valid_run_payload)
|
|
data["scoring_config"]["rule_weight"] = rule_weight
|
|
data["scoring_config"]["ai_weight"] = ai_weight
|
|
|
|
with pytest.raises(ValidationError):
|
|
RunRequest.model_validate(data)
|