mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-07 07:55:16 +02:00
105 lines
3.5 KiB
Python
105 lines
3.5 KiB
Python
"""Tests for CallTransferManager Redis-backed transfer-context lookup.
|
|
|
|
These tests verify (regression for issue #328):
|
|
1. Lookup by original_call_sid resolves via a secondary index, never an
|
|
O(N) `KEYS transfer:context:*` keyspace scan.
|
|
2. A lookup for an unknown call sid returns None without scanning.
|
|
3. Removing a transfer context also clears its call-sid index entry.
|
|
"""
|
|
|
|
from typing import Dict, List
|
|
|
|
import pytest
|
|
|
|
|
|
class _FakeRedis:
|
|
"""Minimal in-memory async Redis double.
|
|
|
|
Counts calls to ``keys()`` so tests can assert the lookup path no longer
|
|
performs an O(N) keyspace scan (the regression behind issue #328).
|
|
"""
|
|
|
|
def __init__(self) -> None:
|
|
self._store: Dict[str, str] = {}
|
|
self.keys_call_count = 0
|
|
|
|
async def setex(self, key: str, ttl: int, value: str) -> None:
|
|
self._store[key] = value
|
|
|
|
async def get(self, key: str):
|
|
return self._store.get(key)
|
|
|
|
async def delete(self, *keys: str) -> None:
|
|
for key in keys:
|
|
self._store.pop(key, None)
|
|
|
|
async def keys(self, pattern: str) -> List[str]:
|
|
self.keys_call_count += 1
|
|
if pattern.endswith("*"):
|
|
prefix = pattern[:-1]
|
|
return [k for k in self._store if k.startswith(prefix)]
|
|
return [k for k in self._store if k == pattern]
|
|
|
|
|
|
def _build_context(transfer_id: str, original_call_sid: str):
|
|
from api.services.telephony.transfer_event_protocol import TransferContext
|
|
|
|
return TransferContext(
|
|
transfer_id=transfer_id,
|
|
call_sid="dest-call-sid",
|
|
target_number="+15551230000",
|
|
tool_uuid="tool-uuid",
|
|
original_call_sid=original_call_sid,
|
|
conference_name="conference-name",
|
|
initiated_at=0.0,
|
|
)
|
|
|
|
|
|
class TestFindTransferContextByCallSid:
|
|
"""Lookup must use the call-sid index, not a KEYS scan (issue #328)."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_lookup_uses_index_and_not_keys_scan(self):
|
|
from api.services.telephony.call_transfer_manager import CallTransferManager
|
|
|
|
fake = _FakeRedis()
|
|
manager = CallTransferManager(redis_client=fake)
|
|
|
|
await manager.store_transfer_context(_build_context("tx-1", "caller-abc"))
|
|
|
|
found = await manager.find_transfer_context_for_call("caller-abc")
|
|
|
|
assert found is not None
|
|
assert found.transfer_id == "tx-1"
|
|
# Regression (issue #328): the lookup must resolve via the secondary
|
|
# index, never an O(N) `KEYS transfer:context:*` keyspace scan.
|
|
assert fake.keys_call_count == 0
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_lookup_returns_none_for_unknown_call_sid(self):
|
|
from api.services.telephony.call_transfer_manager import CallTransferManager
|
|
|
|
fake = _FakeRedis()
|
|
manager = CallTransferManager(redis_client=fake)
|
|
|
|
await manager.store_transfer_context(_build_context("tx-1", "caller-abc"))
|
|
|
|
found = await manager.find_transfer_context_for_call("not-a-caller")
|
|
|
|
assert found is None
|
|
assert fake.keys_call_count == 0
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remove_clears_call_sid_index(self):
|
|
from api.services.telephony.call_transfer_manager import CallTransferManager
|
|
|
|
fake = _FakeRedis()
|
|
manager = CallTransferManager(redis_client=fake)
|
|
|
|
await manager.store_transfer_context(_build_context("tx-1", "caller-abc"))
|
|
await manager.remove_transfer_context("tx-1")
|
|
|
|
found = await manager.find_transfer_context_for_call("caller-abc")
|
|
|
|
assert found is None
|
|
assert fake.keys_call_count == 0
|