106 lines
3.6 KiB
Python
106 lines
3.6 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
from yuxi.channel.adapters import (
|
|
HttpErrorClassifier,
|
|
MediaUploadAdapter,
|
|
TokenAuthAdapter,
|
|
TypingIndicatorAdapter,
|
|
WebhookSignatureAdapter,
|
|
)
|
|
from yuxi.channel.exceptions import ChannelErrorClassification
|
|
|
|
|
|
class TestHttpErrorClassifier:
|
|
@pytest.fixture
|
|
def classifier(self):
|
|
return HttpErrorClassifier()
|
|
|
|
def test_429_returns_rate_limited(self, classifier):
|
|
assert classifier.classify(429) == ChannelErrorClassification.RATE_LIMITED
|
|
|
|
def test_400_returns_permanent(self, classifier):
|
|
assert classifier.classify(400) == ChannelErrorClassification.PERMANENT
|
|
|
|
def test_404_returns_permanent(self, classifier):
|
|
assert classifier.classify(404) == ChannelErrorClassification.PERMANENT
|
|
|
|
def test_500_returns_retryable(self, classifier):
|
|
assert classifier.classify(500) == ChannelErrorClassification.RETRYABLE
|
|
|
|
def test_503_returns_retryable(self, classifier):
|
|
assert classifier.classify(503) == ChannelErrorClassification.RETRYABLE
|
|
|
|
def test_none_status_returns_retryable(self, classifier):
|
|
assert classifier.classify(None) == ChannelErrorClassification.RETRYABLE
|
|
|
|
|
|
class TestMediaUploadAdapter:
|
|
@pytest.fixture
|
|
def adapter(self):
|
|
return MediaUploadAdapter(base_url="https://example.com")
|
|
|
|
async def test_upload_returns_none(self, adapter):
|
|
assert await adapter.upload({"url": "a.jpg"}) is None
|
|
|
|
async def test_download_returns_none(self, adapter):
|
|
assert await adapter.download("https://example.com/a.jpg") is None
|
|
|
|
def test_base_url_stored(self):
|
|
adapter = MediaUploadAdapter(base_url="https://cdn.example.com")
|
|
assert adapter.base_url == "https://cdn.example.com"
|
|
|
|
|
|
class TestTokenAuthAdapter:
|
|
@pytest.fixture
|
|
def adapter(self):
|
|
return TokenAuthAdapter(app_id="app", app_secret="secret", token_url="https://example.com/token")
|
|
|
|
async def test_get_token_returns_none(self, adapter):
|
|
assert await adapter.get_token() is None
|
|
|
|
async def test_refresh_token_returns_none(self, adapter):
|
|
assert await adapter.refresh_token() is None
|
|
|
|
def test_credentials_stored(self):
|
|
adapter = TokenAuthAdapter(app_id="a", app_secret="s", token_url="https://t")
|
|
assert adapter.app_id == "a"
|
|
assert adapter.app_secret == "s"
|
|
assert adapter.token_url == "https://t"
|
|
|
|
|
|
class TestTypingIndicatorAdapter:
|
|
@pytest.fixture
|
|
def adapter(self):
|
|
return TypingIndicatorAdapter(interval_seconds=3.0)
|
|
|
|
async def test_start_returns_none(self, adapter):
|
|
assert await adapter.start("session-1") is None
|
|
|
|
async def test_stop_returns_none(self, adapter):
|
|
assert await adapter.stop("session-1") is None
|
|
|
|
def test_interval_stored(self):
|
|
adapter = TypingIndicatorAdapter(interval_seconds=7.0)
|
|
assert adapter.interval_seconds == 7.0
|
|
|
|
|
|
class TestWebhookSignatureAdapter:
|
|
@pytest.fixture
|
|
def adapter(self):
|
|
return WebhookSignatureAdapter(secret="shhh", signature_header="X-Signature")
|
|
|
|
def test_compute_signature_returns_none(self, adapter):
|
|
assert adapter.compute_signature(b"body") is None
|
|
|
|
def test_validate_with_no_signature_returns_true(self, adapter):
|
|
assert adapter.validate(b"body", None) is True
|
|
|
|
def test_validate_with_signature_returns_false(self, adapter):
|
|
assert adapter.validate(b"body", "sig") is False
|
|
|
|
def test_config_stored(self):
|
|
adapter = WebhookSignatureAdapter(secret="secret", signature_header="X-Hub-Signature")
|
|
assert adapter.secret == "secret"
|
|
assert adapter.signature_header == "X-Hub-Signature"
|