Files
trello-plugin/tests/test_auth.py
T
Marko (Hermes Implementer) 0131b25c8b feat: implement Trello card management
Add 12 card management tools:
- trello_create_card — create a card on a list
- trello_card_details — view card with members, checklists, comments
- trello_update_card — update title, description, due date
- trello_move_card — move card to a different list
- trello_archive_card — archive a card
- trello_assign_member / trello_remove_member — member management
- trello_add_comment / trello_delete_comment — comments
- trello_add_checklist_item / trello_toggle_checklist_item / trello_delete_checklist_item

Also adds _delete() helper on TrelloClient for DELETE verbs.

73 total tests — all passing.

Issue: #4
2026-05-26 23:04:55 +00:00

329 lines
12 KiB
Python

"""Tests for the Trello plugin — auth & connection feature."""
from __future__ import annotations
import json
import os
from collections.abc import Generator
from typing import Any
import pytest
from pytest import MonkeyPatch
import requests
# Tell pytest we plan to set env vars in tests
pytestmark = pytest.mark.usefixtures("clear_env")
# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------
@pytest.fixture(autouse=True)
def clear_env(monkeypatch: MonkeyPatch) -> Generator[None, None, None]:
"""Remove Trello env vars before each test so state is predictable."""
monkeypatch.delenv("TRELLO_API_KEY", raising=False)
monkeypatch.delenv("TRELLO_TOKEN", raising=False)
yield
def _make_session(mocker: Any) -> requests.Session:
"""Build a requests.Session with a mocked adapter."""
session = requests.Session()
adapter = mocker.get_adapter()
session.mount("https://", adapter)
return session
# ---------------------------------------------------------------------------
# TrelloClient — verify_credentials
# ---------------------------------------------------------------------------
class TestVerifyCredentials:
"""Tests for TrelloClient.verify_credentials()."""
def test_success(self, requests_mock: Any) -> None:
"""Happy path: valid creds return member info."""
from trello_plugin.client import TrelloClient
requests_mock.get(
"https://api.trello.com/1/members/me",
json={"id": "member123", "username": "testuser", "fullName": "Test User"},
)
client = TrelloClient(api_key="key", token="tok")
result = client.verify_credentials()
assert result["success"] is True
assert "testuser" in result["message"]
assert result["member"]["id"] == "member123"
assert result["member"]["username"] == "testuser"
def test_missing_credentials(self) -> None:
"""Both API key and token must be set."""
from trello_plugin.client import TrelloClient
client = TrelloClient(api_key="", token="")
result = client.verify_credentials()
assert result["success"] is False
assert "TRELLO_API_KEY" in result["message"]
def test_auth_failure_401(self, requests_mock: Any) -> None:
"""401 response surfaces as auth failure."""
from trello_plugin.client import TrelloClient
requests_mock.get(
"https://api.trello.com/1/members/me",
status_code=401,
)
client = TrelloClient(api_key="bad_key", token="bad_tok")
result = client.verify_credentials()
assert result["success"] is False
assert "401" in result["message"]
def test_forbidden_403(self, requests_mock: Any) -> None:
"""403 surfaces as access denied."""
from trello_plugin.client import TrelloClient
requests_mock.get(
"https://api.trello.com/1/members/me",
status_code=403,
)
client = TrelloClient(api_key="key", token="tok")
result = client.verify_credentials()
assert result["success"] is False
assert "403" in result["message"]
def test_rate_limit_429(self, requests_mock: Any) -> None:
"""429 surfaces as rate limit error."""
from trello_plugin.client import TrelloClient
requests_mock.get(
"https://api.trello.com/1/members/me",
status_code=429,
)
client = TrelloClient(api_key="key", token="tok")
result = client.verify_credentials()
assert result["success"] is False
assert "rate limit" in result["message"].lower()
def test_network_error(self, requests_mock: Any) -> None:
"""Connection errors surface as network failure."""
from trello_plugin.client import TrelloClient
requests_mock.get(
"https://api.trello.com/1/members/me",
exc=requests.exceptions.ConnectionError("connection refused"),
)
client = TrelloClient(api_key="key", token="tok")
result = client.verify_credentials()
assert result["success"] is False
assert "connect" in result["message"].lower()
def test_timeout(self, requests_mock: Any) -> None:
"""Timeout surfaces gracefully."""
from trello_plugin.client import TrelloClient
requests_mock.get(
"https://api.trello.com/1/members/me",
exc=requests.exceptions.Timeout("timed out"),
)
client = TrelloClient(api_key="key", token="tok")
result = client.verify_credentials()
assert result["success"] is False
assert "timed out" in result["message"].lower()
# ---------------------------------------------------------------------------
# TrelloClient — list_boards
# ---------------------------------------------------------------------------
class TestListBoards:
"""Tests for TrelloClient.list_boards()."""
def test_success(self, requests_mock: Any) -> None:
"""Happy path: returns a list of boards."""
from trello_plugin.client import TrelloClient
mock_boards = [
{"id": "b1", "name": "Board One", "url": "https://trello.com/b/b1", "closed": False, "starred": False},
{"id": "b2", "name": "Board Two", "url": "https://trello.com/b/b2", "closed": True, "starred": False},
]
requests_mock.get(
"https://api.trello.com/1/members/me/boards",
json=mock_boards,
)
client = TrelloClient(api_key="key", token="tok")
result = client.list_boards()
assert result["success"] is True
assert len(result["boards"]) == 2
assert result["boards"][0]["name"] == "Board One"
assert result["boards"][1]["closed"] is True
def test_missing_credentials(self) -> None:
"""Missing creds returns error before any network call."""
from trello_plugin.client import TrelloClient
client = TrelloClient(api_key="", token="")
result = client.list_boards()
assert result["success"] is False
assert "TRELLO_API_KEY" in result["message"]
def test_empty_boards(self, requests_mock: Any) -> None:
"""No boards returns an empty list."""
from trello_plugin.client import TrelloClient
requests_mock.get(
"https://api.trello.com/1/members/me/boards",
json=[],
)
client = TrelloClient(api_key="key", token="tok")
result = client.list_boards()
assert result["success"] is True
assert result["boards"] == []
# ---------------------------------------------------------------------------
# TrelloClient — disconnect
# ---------------------------------------------------------------------------
class TestDisconnect:
"""Tests for TrelloClient.disconnect()."""
def test_disconnect_clears_credentials(self) -> None:
"""Disconnect clears the in-memory credentials."""
from trello_plugin.client import TrelloClient
client = TrelloClient(api_key="key", token="tok")
result = client.disconnect()
assert result["success"] is True
assert "cleared" in result["message"].lower()
# Subsequent calls should fail
verify_result = client.verify_credentials()
assert verify_result["success"] is False
assert "TRELLO_API_KEY" in verify_result["message"]
# ---------------------------------------------------------------------------
# check_requirements
# ---------------------------------------------------------------------------
class TestCheckRequirements:
"""Tests for TrelloClient.check_requirements()."""
def test_returns_false_when_missing(self) -> None:
"""Returns False when env vars are not set."""
from trello_plugin.client import TrelloClient
assert TrelloClient.check_requirements() is False
def test_returns_true_when_set(self) -> None:
"""Returns True when both env vars are set."""
from trello_plugin.client import TrelloClient
os.environ["TRELLO_API_KEY"] = "test-key"
os.environ["TRELLO_TOKEN"] = "test-token"
assert TrelloClient.check_requirements() is True
# ---------------------------------------------------------------------------
# Tool functions
# ---------------------------------------------------------------------------
class TestToolFunctions:
"""Tests for the Hermes tool wrapper functions."""
def test_trello_verify_credentials_tool(self, requests_mock: Any) -> None:
"""Tool returns valid JSON string on success."""
from trello_plugin.tools import trello_verify_credentials
os.environ["TRELLO_API_KEY"] = "test-key"
os.environ["TRELLO_TOKEN"] = "test-token"
requests_mock.get(
"https://api.trello.com/1/members/me",
json={"id": "m1", "username": "bot", "fullName": "Bot User"},
)
result = json.loads(trello_verify_credentials())
assert result["success"] is True
assert result["member"]["username"] == "bot"
def test_trello_list_boards_tool(self, requests_mock: Any) -> None:
"""Tool returns valid JSON string on success."""
from trello_plugin.tools import trello_list_boards
os.environ["TRELLO_API_KEY"] = "test-key"
os.environ["TRELLO_TOKEN"] = "test-token"
requests_mock.get(
"https://api.trello.com/1/members/me/boards",
json=[{"id": "b1", "name": "Test", "url": "https://trello.com/b/b1", "closed": False, "starred": False}],
)
result = json.loads(trello_list_boards())
assert result["success"] is True
assert len(result["boards"]) == 1
def test_trello_disconnect_tool(self, requests_mock: Any) -> None:
"""Tool clears credentials and returns success."""
from trello_plugin.tools import trello_disconnect, trello_verify_credentials
os.environ["TRELLO_API_KEY"] = "test-key"
os.environ["TRELLO_TOKEN"] = "test-token"
# First verify works
requests_mock.get(
"https://api.trello.com/1/members/me",
json={"id": "m1", "username": "bot", "fullName": "Bot User"},
)
verify = json.loads(trello_verify_credentials())
assert verify["success"] is True
# Then disconnect
disc = json.loads(trello_disconnect())
assert disc["success"] is True
# With env vars still set, verify should reconnect successfully
# (disconnect clears the in-memory client; env vars persist)
verify2 = json.loads(trello_verify_credentials())
assert verify2["success"] is True
assert verify2["member"]["username"] == "bot"
def test_check_requirements_tool(self, requests_mock: Any) -> None:
"""check_requirements returns False when env not set."""
from trello_plugin.tools import check_requirements
assert check_requirements() is False
os.environ["TRELLO_API_KEY"] = "k"
os.environ["TRELLO_TOKEN"] = "t"
assert check_requirements() is True
# ---------------------------------------------------------------------------
# Plugin metadata
# ---------------------------------------------------------------------------
class TestPluginMetadata:
"""Tests for the plugin metadata constants."""
def test_plugin_constants(self) -> None:
from trello_plugin.tools import (
PLUGIN_DESCRIPTION,
PLUGIN_NAME,
PLUGIN_TOOLS,
PLUGIN_VERSION,
)
assert PLUGIN_NAME == "trello-plugin"
assert isinstance(PLUGIN_DESCRIPTION, str)
assert isinstance(PLUGIN_VERSION, str)
assert len(PLUGIN_TOOLS) == 24
assert all(callable(t) for t in PLUGIN_TOOLS)