from __future__ import annotations from unittest.mock import AsyncMock, patch import pytest from yuxi.channels.adapters.twitch.auth_provider import ( AuthProvider, RefreshingAuthProvider, StaticAuthProvider, create_auth_provider, ) class TestStaticAuthProvider: def test_returns_access_token(self): provider = StaticAuthProvider(client_id="cid", access_token="tok") result = provider._access_token assert result == "tok" @pytest.mark.asyncio async def test_get_access_token(self): provider = StaticAuthProvider(client_id="cid", access_token="tok") token = await provider.get_access_token() assert token == "tok" @pytest.mark.asyncio async def test_get_client_id(self): provider = StaticAuthProvider(client_id="cid", access_token="tok") cid = await provider.get_client_id() assert cid == "cid" @pytest.mark.asyncio async def test_validate_with_valid_token(self): provider = StaticAuthProvider(client_id="cid", access_token="tok") with patch("yuxi.channels.adapters.twitch.auth_provider.validate_token") as mock_validate: mock_validate.return_value = {"id": "123"} result = await provider.validate() assert result is True @pytest.mark.asyncio async def test_validate_with_invalid_token(self): provider = StaticAuthProvider(client_id="cid", access_token="tok") with patch("yuxi.channels.adapters.twitch.auth_provider.validate_token") as mock_validate: mock_validate.return_value = None result = await provider.validate() assert result is False @pytest.mark.asyncio async def test_validate_missing_credentials(self): provider = StaticAuthProvider(client_id="", access_token="") result = await provider.validate() assert result is False @pytest.mark.asyncio async def test_get_access_token_returns_none_when_empty(self): provider = StaticAuthProvider(client_id="cid", access_token="") token = await provider.get_access_token() assert token is None class TestRefreshingAuthProvider: def test_initial_state(self): provider = RefreshingAuthProvider( client_id="cid", client_secret="secret", access_token="tok", refresh_token="rtok", ) assert provider._access_token == "tok" assert provider._refresh_token == "rtok" assert provider._client_id == "cid" assert provider._client_secret == "secret" assert provider._token_expires_in == 0 assert provider._token_expires_at is None @pytest.mark.asyncio async def test_get_access_token_not_expired(self): provider = RefreshingAuthProvider( client_id="cid", client_secret="secret", access_token="tok", refresh_token="rtok", ) provider._token_expires_at = None token = await provider.get_access_token() assert token == "tok" @pytest.mark.asyncio async def test_get_client_id(self): provider = RefreshingAuthProvider( client_id="cid", client_secret="secret", access_token="tok", refresh_token="rtok", ) cid = await provider.get_client_id() assert cid == "cid" @pytest.mark.asyncio async def test_validate_with_valid_token(self): provider = RefreshingAuthProvider( client_id="cid", client_secret="secret", access_token="tok", refresh_token="rtok", ) with patch("yuxi.channels.adapters.twitch.auth_provider.validate_token") as mock_validate: mock_validate.return_value = {"id": "123"} result = await provider.validate() assert result is True @pytest.mark.asyncio async def test_validate_with_empty_credentials(self): provider = RefreshingAuthProvider( client_id="", client_secret="secret", access_token="", refresh_token="rtok", ) result = await provider.validate() assert result is False @pytest.mark.asyncio async def test_refresh_successful(self): provider = RefreshingAuthProvider( client_id="cid", client_secret="secret", access_token="old_tok", refresh_token="rtok", ) with patch("yuxi.channels.adapters.twitch.auth_provider.refresh_access_token") as mock_refresh: mock_refresh.return_value = { "access_token": "new_tok", "refresh_token": "new_rtok", "expires_in": 14400, } result = await provider._refresh() assert result is True assert provider._access_token == "new_tok" assert provider._refresh_token == "new_rtok" assert provider._token_expires_in == 14400 assert provider._token_expires_at is not None @pytest.mark.asyncio async def test_refresh_no_new_access_token(self): provider = RefreshingAuthProvider( client_id="cid", client_secret="secret", access_token="old_tok", refresh_token="rtok", ) with patch("yuxi.channels.adapters.twitch.auth_provider.refresh_access_token") as mock_refresh: mock_refresh.return_value = { "refresh_token": "new_rtok", "expires_in": 14400, } result = await provider._refresh() assert result is True @pytest.mark.asyncio async def test_refresh_failure_single_attempt(self): provider = RefreshingAuthProvider( client_id="cid", client_secret="secret", access_token="old_tok", refresh_token="rtok", ) with patch("yuxi.channels.adapters.twitch.auth_provider.refresh_access_token") as mock_refresh: mock_refresh.return_value = None result = await provider._refresh() assert result is False @pytest.mark.asyncio async def test_refresh_missing_client_secret(self): provider = RefreshingAuthProvider( client_id="cid", client_secret="", access_token="old_tok", refresh_token="rtok", ) result = await provider._refresh() assert result is False @pytest.mark.asyncio async def test_refresh_missing_refresh_token(self): provider = RefreshingAuthProvider( client_id="cid", client_secret="secret", access_token="old_tok", refresh_token="", ) result = await provider._refresh() assert result is False @pytest.mark.asyncio async def test_is_expired_when_none(self): provider = RefreshingAuthProvider( client_id="cid", client_secret="secret", access_token="tok", refresh_token="rtok", ) provider._token_expires_at = None assert provider._is_expired() is False @pytest.mark.asyncio async def test_is_expired_when_future(self): import time provider = RefreshingAuthProvider( client_id="cid", client_secret="secret", access_token="tok", refresh_token="rtok", ) provider._token_expires_at = time.time() + 3600 assert provider._is_expired() is False @pytest.mark.asyncio async def test_is_expired_when_past(self): import time provider = RefreshingAuthProvider( client_id="cid", client_secret="secret", access_token="tok", refresh_token="rtok", ) provider._token_expires_at = time.time() - 1 assert provider._is_expired() is True @pytest.mark.asyncio async def test_get_access_token_expired_and_refreshes(self): import time provider = RefreshingAuthProvider( client_id="cid", client_secret="secret", access_token="old_tok", refresh_token="rtok", ) provider._token_expires_at = time.time() - 1 with patch("yuxi.channels.adapters.twitch.auth_provider.refresh_access_token") as mock_refresh: mock_refresh.return_value = { "access_token": "new_tok", "refresh_token": "new_rtok", "expires_in": 14400, } token = await provider.get_access_token() assert token == "new_tok" @pytest.mark.asyncio async def test_get_access_token_expired_refresh_fails(self): import time provider = RefreshingAuthProvider( client_id="cid", client_secret="secret", access_token="old_tok", refresh_token="rtok", ) provider._token_expires_at = time.time() - 1 with patch("yuxi.channels.adapters.twitch.auth_provider.refresh_access_token") as mock_refresh: mock_refresh.return_value = None token = await provider.get_access_token() assert token is None @pytest.mark.asyncio async def test_expires_at_property(self): provider = RefreshingAuthProvider( client_id="cid", client_secret="secret", access_token="tok", refresh_token="rtok", ) provider._token_expires_at = 12345.0 assert provider.expires_at == 12345.0 @pytest.mark.asyncio async def test_expires_in_property(self): provider = RefreshingAuthProvider( client_id="cid", client_secret="secret", access_token="tok", refresh_token="rtok", ) provider._token_expires_in = 3600 assert provider.expires_in == 3600 class TestCreateAuthProvider: def test_creates_refreshing_when_secret_and_refresh_present(self): config = { "client_id": "cid", "client_secret": "secret", "access_token": "tok", "refresh_token": "rtok", } provider = create_auth_provider(config) assert isinstance(provider, RefreshingAuthProvider) def test_creates_static_when_no_secret(self): config = { "client_id": "cid", "access_token": "tok", } provider = create_auth_provider(config) assert isinstance(provider, StaticAuthProvider) def test_creates_static_when_no_refresh_token(self): config = { "client_id": "cid", "client_secret": "secret", "access_token": "tok", } provider = create_auth_provider(config) assert isinstance(provider, StaticAuthProvider) def test_creates_static_when_empty_config(self): config = {} provider = create_auth_provider(config) assert isinstance(provider, StaticAuthProvider) def test_auth_provider_is_abstract(self): assert hasattr(AuthProvider, "__abstractmethods__")