dotb_ai-platform/tests/test_rule_scoring.py
2026-09-14 09:57:12 +07:00

146 lines
4.9 KiB
Python

from lead_scoring.schemas.snapshot import CanonicalSnapshot
from lead_scoring.scoring import (
BehavioralMetric,
RuleMetric,
ScoringConfig,
ScoringRule,
compute_behavioral_metrics,
evaluate_rule_score,
extract_normalized_features,
)
def make_snapshot() -> CanonicalSnapshot:
return CanonicalSnapshot.model_validate(
{
"captured_at": "2026-08-27T12:00:00Z",
"lead_profile": {
"ems_lead_id": "lead-1",
"name": "Lead One",
"phone": "+84900000000",
},
"conversations": [
{
"conversation_id": "conversation-1",
"messages": [
{
"message_id": "message-1",
"sender_type": "contact",
"content": "Tôi cần tư vấn",
"sent_at": "2026-08-27T11:00:00Z",
}
],
}
],
}
)
def test_rule_score_is_deterministic_and_bounded_by_immutable_config() -> None:
snapshot = make_snapshot()
config = ScoringConfig(
version="fixture-1",
base_score=95,
confidence=0.75,
confidence_threshold=0.8,
hitl_policy_version="fixture-policy-1",
rules=(
ScoringRule(metric=RuleMetric.PROFILE_FIELD_COUNT, minimum=2, points=20),
ScoringRule(metric=RuleMetric.MESSAGE_COUNT, minimum=2, points=-10),
),
)
assert evaluate_rule_score(snapshot, config) == 100
assert evaluate_rule_score(snapshot, config) == 100
assert config.model_config.get("frozen") is True
def test_rule_score_clamps_negative_total_to_zero() -> None:
config = ScoringConfig(
version="fixture-2",
confidence=0.5,
confidence_threshold=0.8,
hitl_policy_version="fixture-policy-2",
rules=(ScoringRule(metric=RuleMetric.CONVERSATION_COUNT, minimum=1, points=-100),),
)
assert evaluate_rule_score(make_snapshot(), config) == 0
def test_compute_behavioral_metrics_deterministic_and_selective() -> None:
snapshot = CanonicalSnapshot.model_validate(
{
"captured_at": "2026-08-27T12:00:00Z",
"lead_profile": {
"ems_lead_id": "lead-behavioral-1",
"name": "Test Lead",
},
"conversations": [
{
"conversation_id": "conv-1",
"messages": [
{
"message_id": "m1",
"sender_type": "agent",
"content": "Chào anh",
"sent_at": "2026-08-27T08:00:00Z",
},
{
"message_id": "m2",
"sender_type": "contact",
"content": "Tôi muốn đăng ký",
"sent_at": "2026-08-27T08:05:00Z",
},
{
"message_id": "m3",
"sender_type": "agent",
"content": "Vâng ạ",
"sent_at": "2026-08-27T08:06:00Z",
},
],
},
{
"conversation_id": "conv-2",
"messages": [
{
"message_id": "m4",
"sender_type": "contact",
"content": "Hôm nay tôi muốn hỏi thêm học phí",
"sent_at": "2026-08-27T10:00:00Z",
},
],
},
],
}
)
all_metrics = compute_behavioral_metrics(snapshot)
assert all_metrics["message_count"] == 4
assert all_metrics["conversation_count"] == 2
assert (
all_metrics["conversation_turn_count"] == 4
) # conv1: 3 turns (agent->contact->agent) + conv2: 1 turn (contact) = 4
assert all_metrics["customer_message_count"] == 2
assert all_metrics["customer_initiated_return"] is True
selected_metrics = compute_behavioral_metrics(
snapshot,
[
BehavioralMetric.CONVERSATION_TURN_COUNT,
BehavioralMetric.CUSTOMER_INITIATED_RETURN,
],
)
assert set(selected_metrics.keys()) == {
"conversation_turn_count",
"customer_initiated_return",
}
assert selected_metrics["conversation_turn_count"] == 4
assert selected_metrics["customer_initiated_return"] is True
features = extract_normalized_features(snapshot)
assert features.message_count == 4
assert features.conversation_count == 2
assert features.conversation_turn_count == 4
assert features.customer_message_count == 2
assert features.customer_initiated_return is True