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

162 lines
5.8 KiB
Python
Raw Normal View History

from __future__ import annotations
from unittest.mock import AsyncMock
import pytest
from slack_sdk.errors import SlackApiError
from yuxi.channels.adapters.slack.scopes import (
ScopesInfo,
fetch_scopes,
validate_required_scopes,
)
class TestScopesInfo:
def test_defaults(self):
info = ScopesInfo()
assert info.bot_scopes == []
assert info.user_scopes == []
assert info.error == ""
def test_has_chat_write(self):
info = ScopesInfo(bot_scopes=["chat:write", "files:read"])
assert info.has_chat_write is True
assert info.has_chat_write_customize is False
assert info.has_files_write is False
def test_has_chat_write_customize(self):
info = ScopesInfo(bot_scopes=["chat:write.customize"])
assert info.has_chat_write_customize is True
assert info.has_chat_write is False
def test_has_files_write(self):
info = ScopesInfo(bot_scopes=["files:write"])
assert info.has_files_write is True
def test_has_channels_manage(self):
info = ScopesInfo(bot_scopes=["channels:manage"])
assert info.has_channels_manage is True
def test_empty_scopes(self):
info = ScopesInfo(bot_scopes=[])
assert info.has_chat_write is False
assert info.has_chat_write_customize is False
assert info.has_files_write is False
assert info.has_channels_manage is False
def test_to_dict(self):
info = ScopesInfo(
bot_scopes=["chat:write", "files:write"],
user_scopes=["identity.basic"],
error="",
)
d = info.to_dict()
assert d["bot_scopes"] == ["chat:write", "files:write"]
assert d["has_chat_write"] is True
assert d["has_files_write"] is True
assert d["has_channels_manage"] is False
class TestFetchScopes:
@pytest.mark.asyncio
async def test_fetch_scopes_success(self):
mock_client = AsyncMock()
mock_client.auth_test = AsyncMock(return_value={"ok": True})
mock_client.api_call = AsyncMock(
return_value={"headers": {"x-oauth-scopes": "chat:write, files:read, channels:read"}}
)
result = await fetch_scopes(mock_client)
assert result.error == ""
assert "chat:write" in result.bot_scopes
assert "files:read" in result.bot_scopes
assert "channels:read" in result.bot_scopes
@pytest.mark.asyncio
async def test_fetch_scopes_auth_test_fails(self):
mock_client = AsyncMock()
mock_client.auth_test = AsyncMock(return_value={"ok": False, "error": "invalid_auth"})
result = await fetch_scopes(mock_client)
assert "invalid_auth" in result.error
assert result.bot_scopes == []
@pytest.mark.asyncio
async def test_fetch_scopes_slack_api_error(self):
mock_client = AsyncMock()
mock_client.auth_test = AsyncMock(side_effect=SlackApiError("error", {"ok": False, "error": "token_revoked"}))
result = await fetch_scopes(mock_client)
assert result.error != ""
assert result.bot_scopes == []
@pytest.mark.asyncio
async def test_fetch_scopes_generic_exception(self):
mock_client = AsyncMock()
mock_client.auth_test = AsyncMock(side_effect=Exception("network error"))
result = await fetch_scopes(mock_client)
assert "network error" in result.error
@pytest.mark.asyncio
async def test_fetch_scopes_scopes_as_list(self):
mock_client = AsyncMock()
mock_client.auth_test = AsyncMock(return_value={"ok": True})
mock_client.api_call = AsyncMock(return_value={"headers": {"x-oauth-scopes": ["chat:write", "files:read"]}})
result = await fetch_scopes(mock_client)
assert result.bot_scopes == ["chat:write", "files:read"]
@pytest.mark.asyncio
async def test_fetch_scopes_empty_scopes(self):
mock_client = AsyncMock()
mock_client.auth_test = AsyncMock(return_value={"ok": True})
mock_client.api_call = AsyncMock(return_value={"headers": {"x-oauth-scopes": ""}})
result = await fetch_scopes(mock_client)
assert result.bot_scopes == []
class TestValidateRequiredScopes:
@pytest.mark.asyncio
async def test_all_present(self):
mock_client = AsyncMock()
mock_client.auth_test = AsyncMock(return_value={"ok": True})
mock_client.api_call = AsyncMock(
return_value={"headers": {"x-oauth-scopes": "chat:write, files:write, channels:manage"}}
)
ok, missing = await validate_required_scopes(mock_client, ["chat:write", "files:write"])
assert ok is True
assert missing == []
@pytest.mark.asyncio
async def test_some_missing(self):
mock_client = AsyncMock()
mock_client.auth_test = AsyncMock(return_value={"ok": True})
mock_client.api_call = AsyncMock(return_value={"headers": {"x-oauth-scopes": "chat:write"}})
ok, missing = await validate_required_scopes(mock_client, ["chat:write", "files:write"])
assert ok is False
assert "files:write" in missing
@pytest.mark.asyncio
async def test_all_missing(self):
mock_client = AsyncMock()
mock_client.auth_test = AsyncMock(return_value={"ok": True})
mock_client.api_call = AsyncMock(return_value={"headers": {"x-oauth-scopes": "chat:write"}})
ok, missing = await validate_required_scopes(mock_client, ["admin", "audit:read"])
assert ok is False
assert set(missing) == {"admin", "audit:read"}
@pytest.mark.asyncio
async def test_empty_required(self):
mock_client = AsyncMock()
mock_client.auth_test = AsyncMock(return_value={"ok": True})
mock_client.api_call = AsyncMock(return_value={"headers": {"x-oauth-scopes": "chat:write"}})
ok, missing = await validate_required_scopes(mock_client, [])
assert ok is True
assert missing == []