ForcePilot/backend/test/unit/channels/test_channels_slack_pairing.py

208 lines
7.0 KiB
Python
Raw Normal View History

from __future__ import annotations
import asyncio
import time
import pytest
from yuxi.channels.adapters.slack.pairing import (
PairingManager,
PendingPairing,
_normalize_code,
)
class TestNormalizeCode:
def test_strips_slack_prefix(self):
assert _normalize_code("slack:ABC123") == "ABC123"
def test_strips_Slack_prefix(self):
assert _normalize_code("Slack:ABC123") == "ABC123"
def test_strips_SLACK_prefix(self):
assert _normalize_code("SLACK:ABC123") == "ABC123"
def test_no_prefix_passthrough(self):
assert _normalize_code("ABC123") == "ABC123"
def test_empty_string(self):
assert _normalize_code("") == ""
def test_other_prefix_passthrough(self):
assert _normalize_code("other:ABC123") == "other:ABC123"
class TestPendingPairing:
def test_creation(self):
pairing = PendingPairing(user_id="U001", pairing_code="ABC123")
assert pairing.user_id == "U001"
assert pairing.pairing_code == "ABC123"
assert pairing.created_at > 0
def test_not_expired(self):
pairing = PendingPairing(user_id="U001", pairing_code="ABC123")
assert pairing.is_expired(ttl_seconds=300) is False
def test_expired(self):
pairing = PendingPairing(
user_id="U001",
pairing_code="ABC123",
created_at=time.monotonic() - 301,
)
assert pairing.is_expired(ttl_seconds=300) is True
def test_expired_with_custom_ttl(self):
pairing = PendingPairing(
user_id="U001",
pairing_code="ABC123",
created_at=time.monotonic() - 11,
)
assert pairing.is_expired(ttl_seconds=10) is True
class TestPairingManager:
@pytest.fixture
def manager(self):
return PairingManager(ttl_seconds=300)
@pytest.mark.asyncio
async def test_generate_pairing_new_user(self, manager):
decision = await manager.generate_pairing("U001")
assert decision.allowed is False
assert decision.requires_pairing is True
assert decision.pairing_code is not None
assert len(decision.pairing_code) == 6
assert decision.reason == "pairing_required"
@pytest.mark.asyncio
async def test_generate_pairing_already_approved(self, manager):
manager._approved_users.add("U001")
decision = await manager.generate_pairing("U001")
assert decision.allowed is True
assert decision.reason == "user_approved"
@pytest.mark.asyncio
async def test_generate_pairing_duplicate_returns_existing_code(self, manager):
decision1 = await manager.generate_pairing("U001")
decision2 = await manager.generate_pairing("U001")
assert decision2.pairing_code == decision1.pairing_code
assert decision2.reason == "pending_pairing_exists"
@pytest.mark.asyncio
async def test_approve_with_correct_code(self, manager):
decision = await manager.generate_pairing("U001")
user_id = await manager.approve(decision.pairing_code)
assert user_id == "U001"
assert manager.is_approved("U001") is True
@pytest.mark.asyncio
async def test_approve_with_slack_prefix(self, manager):
decision = await manager.generate_pairing("U001")
user_id = await manager.approve(f"slack:{decision.pairing_code}")
assert user_id == "U001"
@pytest.mark.asyncio
async def test_approve_with_wrong_code(self, manager):
await manager.generate_pairing("U001")
user_id = await manager.approve("WRONG")
assert user_id is None
@pytest.mark.asyncio
async def test_approve_expired_pairing(self, manager):
async with manager._lock:
pairing = PendingPairing(
user_id="U001",
pairing_code="EXP123",
created_at=time.monotonic() - 301,
)
manager._pending["U001"] = pairing
user_id = await manager.approve("EXP123")
assert user_id is None
assert "U001" not in manager._pending
@pytest.mark.asyncio
async def test_is_approved(self, manager):
assert manager.is_approved("U001") is False
manager._approved_users.add("U001")
assert manager.is_approved("U001") is True
@pytest.mark.asyncio
async def test_clear_expired(self, manager):
async with manager._lock:
expired = PendingPairing(
user_id="U001",
pairing_code="OLD1",
created_at=time.monotonic() - 301,
)
fresh = PendingPairing(
user_id="U002",
pairing_code="NEW1",
created_at=time.monotonic(),
)
manager._pending["U001"] = expired
manager._pending["U002"] = fresh
await manager.clear_expired()
async with manager._lock:
assert "U001" not in manager._pending
assert "U002" in manager._pending
@pytest.mark.asyncio
async def test_clear_all(self, manager):
async with manager._lock:
manager._pending["U001"] = PendingPairing(user_id="U001", pairing_code="C1")
manager._pending["U002"] = PendingPairing(user_id="U002", pairing_code="C2")
await manager.clear_all()
async with manager._lock:
assert len(manager._pending) == 0
@pytest.mark.asyncio
async def test_pending_count(self, manager):
assert await manager.pending_count() == 0
await manager.generate_pairing("U001")
assert await manager.pending_count() == 1
await manager.generate_pairing("U002")
assert await manager.pending_count() == 2
@pytest.mark.asyncio
async def test_pending_count_clears_expired(self, manager):
async with manager._lock:
expired = PendingPairing(
user_id="U001",
pairing_code="OLD1",
created_at=time.monotonic() - 301,
)
manager._pending["U001"] = expired
assert await manager.pending_count() == 0
@pytest.mark.asyncio
async def test_multiple_users_concurrent(self, manager):
d1 = await manager.generate_pairing("U001")
d2 = await manager.generate_pairing("U002")
assert d1.pairing_code != d2.pairing_code
user = await manager.approve(d1.pairing_code)
assert user == "U001"
assert manager.is_approved("U001") is True
assert manager.is_approved("U002") is False
@pytest.mark.asyncio
async def test_generate_pairing_code_format(self, manager):
decision = await manager.generate_pairing("U001")
code = decision.pairing_code
assert len(code) == 6
assert code.isalnum()
@pytest.mark.asyncio
async def test_reapprove_after_clear(self, manager):
d1 = await manager.generate_pairing("U001")
await manager.approve(d1.pairing_code)
assert manager.is_approved("U001") is True
manager._approved_users.discard("U001")
d2 = await manager.generate_pairing("U001")
assert d2.allowed is False
assert d2.requires_pairing is True