Files
tiddl/tests/cli/commands/auth/test_auth.py
T
2026-05-04 22:02:00 +02:00

246 lines
8.0 KiB
Python

import pytest
from unittest.mock import patch, MagicMock
from time import time
from typer.testing import CliRunner
from tiddl.core.auth import AuthClientError
from tiddl.cli.commands.auth import auth_command
from tiddl.cli.utils.auth import AuthData
runner = CliRunner()
def test_login_already_logged(monkeypatch: pytest.MonkeyPatch):
"""Should exit early if user is logged in."""
monkeypatch.setattr(
"tiddl.cli.commands.auth.load_auth_data", lambda: AuthData(token="token")
)
result = runner.invoke(auth_command, ["login"])
assert "Already logged in." in result.stdout
assert result.exit_code == 0
def test_login_success(monkeypatch: pytest.MonkeyPatch):
"""Should save user token."""
monkeypatch.setattr(
"tiddl.cli.commands.auth.load_auth_data", lambda: AuthData(token=None)
)
device_auth_mock = MagicMock()
device_auth_mock.verificationUriComplete = "verify.uri"
device_auth_mock.deviceCode = "device123"
device_auth_mock.expiresIn = 60
device_auth_mock.interval = 1
auth_mock = MagicMock()
auth_mock.access_token = "newtoken"
auth_mock.refresh_token = "refreshtoken"
auth_mock.expires_in = 3600
auth_mock.user_id = 123
auth_mock.user.countryCode = "US"
with (
patch("tiddl.cli.commands.auth.AuthAPI") as MockAuthAPI,
patch("tiddl.cli.commands.auth.typer.launch") as mock_launch,
patch("tiddl.cli.commands.auth.save_auth_data") as mock_save,
patch("tiddl.cli.commands.auth.time", side_effect=lambda: 1000),
patch("tiddl.cli.commands.auth.sleep"),
):
auth_api = MockAuthAPI.return_value
auth_api.get_device_auth.return_value = device_auth_mock
auth_api.get_auth.side_effect = [
AuthClientError(error="authorization_pending"),
auth_mock,
]
result = runner.invoke(auth_command, ["login"])
auth_api.get_device_auth.assert_called_once()
auth_api.get_auth.assert_called()
mock_launch.assert_called_once_with("https://verify.uri")
mock_save.assert_called_once()
saved_data = mock_save.call_args[0][0]
assert saved_data.token == "newtoken"
assert "Logged in!" in result.stdout
assert result.exit_code == 0
def test_login_expired(monkeypatch: pytest.MonkeyPatch):
"""Should not save token and exit."""
monkeypatch.setattr(
"tiddl.cli.commands.auth.load_auth_data", lambda: AuthData(token=None)
)
device_auth_mock = MagicMock()
device_auth_mock.verificationUriComplete = "verify.uri"
device_auth_mock.deviceCode = "device123"
device_auth_mock.expiresIn = 60
device_auth_mock.interval = 1
with (
patch("tiddl.cli.commands.auth.AuthAPI") as MockAuthAPI,
patch("tiddl.cli.commands.auth.typer.launch") as mock_launch,
patch("tiddl.cli.commands.auth.save_auth_data") as mock_save,
patch("tiddl.cli.commands.auth.time", side_effect=lambda: 1000),
patch("tiddl.cli.commands.auth.sleep"),
):
auth_api = MockAuthAPI.return_value
auth_api.get_device_auth.return_value = device_auth_mock
auth_api.get_auth.side_effect = [
AuthClientError(error="expired_token"),
]
result = runner.invoke(auth_command, ["login"])
auth_api.get_device_auth.assert_called_once()
auth_api.get_auth.assert_called()
mock_launch.assert_called_once_with("https://verify.uri")
mock_save.assert_not_called()
assert "Time for authentication has expired." in result.stdout
assert result.exit_code == 0
def test_logout_with_token(monkeypatch: pytest.MonkeyPatch):
"""Should clear auth data and logout token in API."""
monkeypatch.setattr(
"tiddl.cli.commands.auth.load_auth_data", lambda: AuthData(token="token")
)
with (
patch("tiddl.cli.commands.auth.AuthAPI") as MockAuthAPI,
patch("tiddl.cli.commands.auth.save_auth_data") as mock_save,
):
mock_api_instance = MockAuthAPI.return_value
result = runner.invoke(auth_command, ["logout"])
mock_api_instance.logout_token.assert_called_once_with("token")
mock_save.assert_called_once_with(AuthData())
assert "Logged out successfully!\n" in result.stdout
assert result.exit_code == 0
def test_logout_no_token(monkeypatch: pytest.MonkeyPatch):
"""Should do nothing."""
monkeypatch.setattr(
"tiddl.cli.commands.auth.load_auth_data", lambda: AuthData(token=None)
)
with (patch("tiddl.cli.commands.auth.AuthAPI") as MockAuthAPI,):
result = runner.invoke(auth_command, ["logout"])
MockAuthAPI.assert_not_called()
assert "No active session found." in result.stdout
assert result.exit_code == 0
def test_logout_force(monkeypatch: pytest.MonkeyPatch):
"""Should remove local token even when the API request raises an error."""
# 1. Mock existing session
monkeypatch.setattr(
"tiddl.cli.commands.auth.load_auth_data", lambda: AuthData(token="fake-token")
)
with (
patch("tiddl.cli.commands.auth.AuthAPI") as MockAuthAPI,
patch("tiddl.cli.commands.auth.save_auth_data") as mock_save,
):
# 2. Configure the mock to RAISE an exception
mock_api_instance = MockAuthAPI.return_value
mock_api_instance.logout_token.side_effect = Exception("Server Down")
# 3. Invoke with --force
result = runner.invoke(auth_command, ["logout", "--force"])
# 4. Assertions
# API was still called
mock_api_instance.logout_token.assert_called_once_with("fake-token")
# Local data was still wiped (this is the core of --force)
mock_save.assert_called_once_with(AuthData())
# Check for your specific "force" success message
assert "Token removed!" in result.stdout
assert result.exit_code == 0
def test_logout_fails_without_force(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr(
"tiddl.cli.commands.auth.load_auth_data", lambda: AuthData(token="token")
)
with (
patch("tiddl.cli.commands.auth.AuthAPI") as MockAuthAPI,
patch("tiddl.cli.commands.auth.save_auth_data") as mock_save,
):
MockAuthAPI.return_value.logout_token.side_effect = Exception("Error")
result = runner.invoke(auth_command, ["logout"])
assert "Local session retained" in result.stdout
mock_save.assert_not_called() # Ensure data wasn't wiped
def test_refresh_not_logged_in(monkeypatch: pytest.MonkeyPatch):
"""Should exit early if refresh_token is missing."""
monkeypatch.setattr(
"tiddl.cli.commands.auth.load_auth_data", lambda: AuthData(refresh_token=None)
)
result = runner.invoke(auth_command, ["refresh"])
assert "Not logged in." in result.stdout
assert result.exit_code == 0
def test_refresh_not_expired(monkeypatch: pytest.MonkeyPatch):
"""Should exit early if token still valid."""
monkeypatch.setattr(
"tiddl.cli.commands.auth.load_auth_data",
lambda: AuthData(
token="abc", refresh_token="ref", expires_at=int(time()) + 3600
),
)
result = runner.invoke(auth_command, ["refresh"])
assert "Auth token expires in" in result.stdout
assert result.exit_code == 0
def test_refresh_success(monkeypatch: pytest.MonkeyPatch):
"""Should refresh token if expired."""
expired_data = AuthData(
token="oldtoken", refresh_token="refreshtoken", expires_at=0
)
monkeypatch.setattr("tiddl.cli.commands.auth.load_auth_data", lambda: expired_data)
mock_auth_response = MagicMock()
mock_auth_response.access_token = "newtoken"
with (
patch("tiddl.cli.commands.auth.AuthAPI") as MockAuthAPI,
patch("tiddl.cli.commands.auth.save_auth_data") as mock_save,
):
MockAuthAPI.return_value.refresh_token.return_value = mock_auth_response
result = runner.invoke(auth_command, ["refresh"])
mock_save.assert_called_once_with(expired_data)
assert "Auth token has been refreshed!" in result.stdout
assert result.exit_code == 0