openwebui-cli/tests/test_auth.py
2025-12-01 04:24:51 +01:00

203 lines
6.8 KiB
Python

"""Unit tests for auth module functions."""
from unittest.mock import Mock, patch
import keyring
import pytest
from openwebui_cli.errors import AuthError
from openwebui_cli.http import delete_token, get_token, set_token
@pytest.fixture
def mock_keyring_funcs(monkeypatch):
"""Mock keyring functions."""
get_password_mock = Mock(return_value=None)
set_password_mock = Mock()
delete_password_mock = Mock()
monkeypatch.setattr("openwebui_cli.http.keyring.get_password", get_password_mock)
monkeypatch.setattr("openwebui_cli.http.keyring.set_password", set_password_mock)
monkeypatch.setattr("openwebui_cli.http.keyring.delete_password", delete_password_mock)
return {
"get_password": get_password_mock,
"set_password": set_password_mock,
"delete_password": delete_password_mock,
}
def test_get_token_success(mock_keyring_funcs):
"""get_token retrieves token from keyring."""
mock_keyring_funcs["get_password"].return_value = "stored_token_123"
token = get_token("default", "http://localhost:8080")
assert token == "stored_token_123"
assert mock_keyring_funcs["get_password"].called
def test_get_token_with_special_profile(mock_keyring_funcs):
"""get_token properly formats profile:uri key."""
mock_keyring_funcs["get_password"].return_value = "test_token"
get_token("custom_profile", "http://example.com:8080")
# Verify the key format is correct
mock_keyring_funcs["get_password"].assert_called_once_with(
"openwebui-cli", "custom_profile:http://example.com:8080"
)
def test_get_token_keyring_error(monkeypatch):
"""get_token returns None when keyring is unavailable."""
monkeypatch.setattr(
"openwebui_cli.http.keyring.get_password",
Mock(side_effect=keyring.errors.KeyringError("No backend")),
)
token = get_token("default", "http://localhost:8080")
assert token is None
def test_get_token_returns_none_when_not_stored(mock_keyring_funcs):
"""get_token returns None when token is not in keyring."""
mock_keyring_funcs["get_password"].return_value = None
token = get_token("default", "http://localhost:8080")
assert token is None
def test_set_token_stores_in_keyring(mock_keyring_funcs):
"""set_token stores token in keyring."""
set_token("default", "http://localhost:8080", "new_token_456")
mock_keyring_funcs["set_password"].assert_called_once_with(
"openwebui-cli", "default:http://localhost:8080", "new_token_456"
)
def test_set_token_with_custom_profile(mock_keyring_funcs):
"""set_token properly formats profile:uri key."""
set_token("production", "http://prod.example.com", "prod_token")
mock_keyring_funcs["set_password"].assert_called_once_with(
"openwebui-cli", "production:http://prod.example.com", "prod_token"
)
def test_delete_token_removes_from_keyring(mock_keyring_funcs):
"""delete_token removes token from keyring."""
delete_token("default", "http://localhost:8080")
mock_keyring_funcs["delete_password"].assert_called_once_with(
"openwebui-cli", "default:http://localhost:8080"
)
def test_delete_token_handles_missing_token(mock_keyring_funcs):
"""delete_token gracefully handles missing token."""
mock_keyring_funcs["delete_password"].side_effect = keyring.errors.PasswordDeleteError("Token not found")
# Should not raise an exception
delete_token("default", "http://localhost:8080")
def test_delete_token_with_multiple_profiles(mock_keyring_funcs):
"""delete_token can manage multiple profiles independently."""
delete_token("profile1", "http://server1:8080")
delete_token("profile2", "http://server2:8080")
assert mock_keyring_funcs["delete_password"].call_count == 2
calls = mock_keyring_funcs["delete_password"].call_args_list
assert calls[0][0] == ("openwebui-cli", "profile1:http://server1:8080")
assert calls[1][0] == ("openwebui-cli", "profile2:http://server2:8080")
def test_get_token_unicode_profile(mock_keyring_funcs):
"""get_token handles unicode characters in profile/uri."""
mock_keyring_funcs["get_password"].return_value = "unicode_token"
get_token("profil_ée", "http://example.com:8080")
mock_keyring_funcs["get_password"].assert_called_once()
call_args = mock_keyring_funcs["get_password"].call_args[0]
assert "profil_ée" in call_args[1]
def test_set_token_empty_token(mock_keyring_funcs):
"""set_token handles empty token strings."""
set_token("default", "http://localhost:8080", "")
mock_keyring_funcs["set_password"].assert_called_once_with(
"openwebui-cli", "default:http://localhost:8080", ""
)
def test_token_key_format_consistency(mock_keyring_funcs):
"""Verify consistent key format for profile:uri combinations."""
profile = "test"
uri = "http://test.local:9000"
expected_key = f"{profile}:{uri}"
set_token(profile, uri, "token1")
get_token(profile, uri)
delete_token(profile, uri)
# Verify all operations use the same key format
set_call = mock_keyring_funcs["set_password"].call_args[0][1]
get_call = mock_keyring_funcs["get_password"].call_args[0][1]
delete_call = mock_keyring_funcs["delete_password"].call_args[0][1]
assert set_call == expected_key
assert get_call == expected_key
assert delete_call == expected_key
def test_get_token_long_token(mock_keyring_funcs):
"""get_token handles very long tokens."""
long_token = "x" * 10000 # 10KB token
mock_keyring_funcs["get_password"].return_value = long_token
token = get_token("default", "http://localhost:8080")
assert token == long_token
assert len(token) == 10000
def test_set_token_long_token(mock_keyring_funcs):
"""set_token handles very long tokens."""
long_token = "y" * 10000
set_token("default", "http://localhost:8080", long_token)
call_args = mock_keyring_funcs["set_password"].call_args[0]
assert call_args[2] == long_token
assert len(call_args[2]) == 10000
def test_get_token_special_characters_in_uri(mock_keyring_funcs):
"""get_token handles special characters in URIs."""
mock_keyring_funcs["get_password"].return_value = "special_token"
uri = "http://user:pass@example.com:8080/path?query=value&other=123"
get_token("default", uri)
call_args = mock_keyring_funcs["get_password"].call_args[0]
assert uri in call_args[1]
def test_multiple_get_token_calls_with_cache(mock_keyring_funcs):
"""get_token makes fresh calls each time (no internal caching)."""
mock_keyring_funcs["get_password"].side_effect = ["token1", "token2", "token1"]
token1 = get_token("default", "http://localhost:8080")
token2 = get_token("default", "http://localhost:8080")
token3 = get_token("default", "http://localhost:8080")
assert token1 == "token1"
assert token2 == "token2"
assert token3 == "token1"
assert mock_keyring_funcs["get_password"].call_count == 3