Merge pull request 'feat: L1->L2 agent pool and routing engine for voice escalation' (#4) from feature/l1-l2-routing-engine into main
deploy / deploy (push) Successful in 32s
deploy / deploy (push) Successful in 32s
This commit was merged in pull request #4.
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
import json
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from services.routing_service import engine as routing_engine
|
||||
from services.shared.core import new_id, utc_now_iso
|
||||
from services.shared.db import get_session
|
||||
from services.shared.sql_init import init_sql_schema
|
||||
from services.shared.sql_models import AgentRow
|
||||
|
||||
|
||||
def _make_agent(session, *, level="L2", tenant_ids=None, skills=None, status="AVAILABLE", enabled=True):
|
||||
now = utc_now_iso()
|
||||
row = AgentRow(
|
||||
agent_id=new_id("agt"),
|
||||
tenant_ids_json=json.dumps(tenant_ids or []),
|
||||
extension="2001",
|
||||
endpoint=None,
|
||||
display_name="Test Agent",
|
||||
level=level,
|
||||
skills_json=json.dumps(skills or []),
|
||||
status=status,
|
||||
max_concurrent_calls=1,
|
||||
enabled=enabled,
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
)
|
||||
session.add(row)
|
||||
session.commit()
|
||||
session.refresh(row)
|
||||
return row
|
||||
|
||||
|
||||
def test_reserve_agent_picks_available_agent_of_requested_level():
|
||||
init_sql_schema()
|
||||
session = get_session()
|
||||
try:
|
||||
agent_l2 = _make_agent(session, level="L2")
|
||||
_make_agent(session, level="L3")
|
||||
|
||||
reserved = routing_engine.reserve_agent(
|
||||
session,
|
||||
call_id="call-1",
|
||||
level="L2",
|
||||
tenant_id=None,
|
||||
required_skills=[],
|
||||
)
|
||||
assert reserved is not None
|
||||
assert reserved.agent_id == agent_l2.agent_id
|
||||
assert reserved.status == "RESERVED"
|
||||
assert reserved.current_call_id == "call-1"
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
def test_reserve_agent_does_not_double_reserve():
|
||||
init_sql_schema()
|
||||
session = get_session()
|
||||
try:
|
||||
_make_agent(session, level="L2")
|
||||
|
||||
first = routing_engine.reserve_agent(session, call_id="call-a", level="L2", tenant_id=None, required_skills=[])
|
||||
second = routing_engine.reserve_agent(session, call_id="call-b", level="L2", tenant_id=None, required_skills=[])
|
||||
assert first is not None
|
||||
assert second is None
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
def test_reserve_agent_filters_by_tenant_and_skills():
|
||||
init_sql_schema()
|
||||
session = get_session()
|
||||
try:
|
||||
wrong_tenant = _make_agent(session, level="L2", tenant_ids=["other"])
|
||||
no_skill = _make_agent(session, level="L2", tenant_ids=["konturai"], skills=[])
|
||||
matching = _make_agent(session, level="L2", tenant_ids=["konturai"], skills=["billing"])
|
||||
|
||||
reserved = routing_engine.reserve_agent(
|
||||
session,
|
||||
call_id="call-skill",
|
||||
level="L2",
|
||||
tenant_id="konturai",
|
||||
required_skills=["billing"],
|
||||
)
|
||||
assert reserved is not None
|
||||
assert reserved.agent_id == matching.agent_id
|
||||
assert reserved.agent_id != wrong_tenant.agent_id
|
||||
assert reserved.agent_id != no_skill.agent_id
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
def test_release_agent_by_call_id_makes_agent_available_again():
|
||||
init_sql_schema()
|
||||
session = get_session()
|
||||
try:
|
||||
agent = _make_agent(session, level="L2")
|
||||
reserved = routing_engine.reserve_agent(session, call_id="call-z", level="L2", tenant_id=None, required_skills=[])
|
||||
assert reserved is not None
|
||||
|
||||
released = routing_engine.release_agent_by_call_id(session, call_id="call-z")
|
||||
assert released is not None
|
||||
assert released.agent_id == agent.agent_id
|
||||
assert released.status == "AVAILABLE"
|
||||
assert released.current_call_id is None
|
||||
assert released.calls_handled_count == 1
|
||||
|
||||
again = routing_engine.reserve_agent(session, call_id="call-again", level="L2", tenant_id=None, required_skills=[])
|
||||
assert again is not None
|
||||
assert again.agent_id == agent.agent_id
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
def test_reserve_agent_excludes_disabled_and_excluded_ids():
|
||||
init_sql_schema()
|
||||
session = get_session()
|
||||
try:
|
||||
disabled = _make_agent(session, level="L2", enabled=False)
|
||||
excluded = _make_agent(session, level="L2")
|
||||
available = _make_agent(session, level="L2")
|
||||
|
||||
reserved = routing_engine.reserve_agent(
|
||||
session,
|
||||
call_id="call-exc",
|
||||
level="L2",
|
||||
tenant_id=None,
|
||||
required_skills=[],
|
||||
exclude_agent_ids=[excluded.agent_id],
|
||||
)
|
||||
assert reserved is not None
|
||||
assert reserved.agent_id == available.agent_id
|
||||
assert reserved.agent_id != disabled.agent_id
|
||||
assert reserved.agent_id != excluded.agent_id
|
||||
finally:
|
||||
session.close()
|
||||
Reference in New Issue
Block a user