"""Tests for the Trello plugin — board management feature.""" from __future__ import annotations import json import os from typing import Any import pytest import requests pytestmark = pytest.mark.usefixtures("clear_env") # --------------------------------------------------------------------------- # Fixtures # --------------------------------------------------------------------------- @pytest.fixture(autouse=True) def clear_env(monkeypatch: pytest.MonkeyPatch) -> Any: """Remove Trello env vars before each test so state is predictable.""" monkeypatch.delenv("TRELLO_API_KEY", raising=False) monkeypatch.delenv("TRELLO_TOKEN", raising=False) # --------------------------------------------------------------------------- # TrelloClient — create_board # --------------------------------------------------------------------------- class TestCreateBoard: """Tests for TrelloClient.create_board().""" def test_success(self, requests_mock: Any) -> None: """Happy path: creates a board and returns details.""" from trello_plugin.client import TrelloClient requests_mock.post( "https://api.trello.com/1/boards", json={"id": "b1", "name": "New Board", "url": "https://trello.com/b/b1"}, status_code=200, ) client = TrelloClient(api_key="key", token="tok") result = client.create_board(name="New Board") assert result["success"] is True assert result["board"]["name"] == "New Board" assert result["board"]["id"] == "b1" 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.create_board(name="Board") assert result["success"] is False assert "TRELLO_API_KEY" in result["message"] # --------------------------------------------------------------------------- # TrelloClient — rename_board # --------------------------------------------------------------------------- class TestRenameBoard: """Tests for TrelloClient.rename_board().""" def test_success_by_id(self, requests_mock: Any) -> None: """Renaming a board by ID works.""" from trello_plugin.client import TrelloClient # Resolve board requests_mock.get( "https://api.trello.com/1/boards/b1", json={"id": "b1", "name": "Old Name", "url": "https://trello.com/b/b1"}, ) # Rename requests_mock.put( "https://api.trello.com/1/boards/b1", json={"id": "b1", "name": "New Name", "url": "https://trello.com/b/b1"}, ) client = TrelloClient(api_key="key", token="tok") result = client.rename_board(board_id="b1", name="New Name") assert result["success"] is True assert result["board"]["name"] == "New Name" def test_board_not_found(self, requests_mock: Any) -> None: """Non-existent board returns a clear error.""" from trello_plugin.client import TrelloClient # Board lookup fails requests_mock.get( "https://api.trello.com/1/boards/nonexistent", status_code=404, ) # List boards returns empty requests_mock.get( "https://api.trello.com/1/members/me/boards", json=[], ) client = TrelloClient(api_key="key", token="tok") result = client.rename_board(board_id="nonexistent", name="New") assert result["success"] is False assert "not found" in result["message"].lower() # --------------------------------------------------------------------------- # TrelloClient — archive_board # --------------------------------------------------------------------------- class TestArchiveBoard: """Tests for TrelloClient.archive_board().""" def test_success(self, requests_mock: Any) -> None: """Archiving a board returns success message.""" from trello_plugin.client import TrelloClient # Resolve requests_mock.get( "https://api.trello.com/1/boards/b1", json={"id": "b1", "name": "My Board", "url": "https://trello.com/b/b1"}, ) # Archive requests_mock.put( "https://api.trello.com/1/boards/b1", json={"id": "b1", "name": "My Board", "closed": True}, ) client = TrelloClient(api_key="key", token="tok") result = client.archive_board(board_id="b1") assert result["success"] is True assert "archived" in result["message"].lower() # --------------------------------------------------------------------------- # TrelloClient — open_board # --------------------------------------------------------------------------- class TestOpenBoard: """Tests for TrelloClient.open_board().""" def test_success(self, requests_mock: Any) -> None: """Opening a closed board returns success message.""" from trello_plugin.client import TrelloClient # Resolve requests_mock.get( "https://api.trello.com/1/boards/b1", json={"id": "b1", "name": "My Board", "url": "https://trello.com/b/b1"}, ) # Open requests_mock.put( "https://api.trello.com/1/boards/b1", json={"id": "b1", "name": "My Board", "closed": False}, ) client = TrelloClient(api_key="key", token="tok") result = client.open_board(board_id="b1") assert result["success"] is True assert "opened" in result["message"].lower() # --------------------------------------------------------------------------- # TrelloClient — board_details # --------------------------------------------------------------------------- class TestBoardDetails: """Tests for TrelloClient.board_details().""" def test_success(self, requests_mock: Any) -> None: """Board details return lists and members.""" from trello_plugin.client import TrelloClient # Resolve requests_mock.get( "https://api.trello.com/1/boards/b1", json={"id": "b1", "name": "My Board", "url": "https://trello.com/b/b1"}, ) # Details fetch requests_mock.get( "https://api.trello.com/1/boards/b1", json={ "id": "b1", "name": "My Board", "url": "https://trello.com/b/b1", "desc": "A test board", "closed": False, "starred": False, "lists": [ {"id": "l1", "name": "To Do"}, {"id": "l2", "name": "Done"}, ], "members": [ {"id": "m1", "username": "user1", "fullName": "User One"}, ], }, ) client = TrelloClient(api_key="key", token="tok") result = client.board_details(board_id="b1") assert result["success"] is True assert len(result["board"]["lists"]) == 2 assert len(result["board"]["members"]) == 1 assert result["board"]["lists"][0]["name"] == "To Do" def test_not_found(self, requests_mock: Any) -> None: """Non-existent board returns error.""" from trello_plugin.client import TrelloClient requests_mock.get( "https://api.trello.com/1/boards/nonexistent", status_code=404, ) requests_mock.get( "https://api.trello.com/1/members/me/boards", json=[], ) client = TrelloClient(api_key="key", token="tok") result = client.board_details(board_id="nonexistent") assert result["success"] is False assert "not found" in result["message"].lower() # --------------------------------------------------------------------------- # TrelloClient — _resolve_board_id # --------------------------------------------------------------------------- class TestResolveBoardId: """Tests for TrelloClient._resolve_board_id().""" def test_resolves_by_id(self, requests_mock: Any) -> None: """Resolves a valid ID directly.""" from trello_plugin.client import TrelloClient requests_mock.get( "https://api.trello.com/1/boards/b1", json={"id": "b1", "name": "My Board", "url": "https://trello.com/b/b1"}, ) client = TrelloClient(api_key="key", token="tok") result = client._resolve_board_id("b1") assert result["id"] == "b1" def test_resolves_by_name(self, requests_mock: Any) -> None: """Resolves a board name to its ID.""" from trello_plugin.client import TrelloClient # ID lookup fails requests_mock.get( "https://api.trello.com/1/boards/My%20Board", status_code=404, ) # List boards for name matching requests_mock.get( "https://api.trello.com/1/members/me/boards", json=[ {"id": "b1", "name": "My Board", "url": "https://trello.com/b/b1", "closed": False, "starred": False}, {"id": "b2", "name": "Other Board", "url": "https://trello.com/b/b2", "closed": False, "starred": False}, ], ) client = TrelloClient(api_key="key", token="tok") result = client._resolve_board_id("My Board") assert result["id"] == "b1" def test_duplicate_name_error(self, requests_mock: Any) -> None: """Multiple boards with same name returns error.""" from trello_plugin.client import TrelloClient requests_mock.get( "https://api.trello.com/1/boards/Duplicate", status_code=404, ) requests_mock.get( "https://api.trello.com/1/members/me/boards", json=[ {"id": "b1", "name": "Duplicate", "url": "", "closed": False, "starred": False}, {"id": "b2", "name": "Duplicate", "url": "", "closed": False, "starred": False}, ], ) client = TrelloClient(api_key="key", token="tok") result = client._resolve_board_id("Duplicate") assert "success" in result and result["success"] is False assert "multiple" in result["message"].lower() # --------------------------------------------------------------------------- # Tool functions # --------------------------------------------------------------------------- class TestBoardToolFunctions: """Tests for the Hermes board management tool wrappers.""" def test_trello_create_board_tool(self, requests_mock: Any) -> None: """Create board tool returns valid JSON.""" from trello_plugin.tools import trello_create_board os.environ["TRELLO_API_KEY"] = "key" os.environ["TRELLO_TOKEN"] = "tok" requests_mock.post( "https://api.trello.com/1/boards", json={"id": "b1", "name": "New Board", "url": "https://trello.com/b/b1"}, ) result = json.loads(trello_create_board(name="New Board")) assert result["success"] is True assert result["board"]["name"] == "New Board" def test_trello_rename_board_tool(self, requests_mock: Any) -> None: """Rename board tool returns valid JSON.""" from trello_plugin.tools import trello_rename_board os.environ["TRELLO_API_KEY"] = "key" os.environ["TRELLO_TOKEN"] = "tok" requests_mock.get( "https://api.trello.com/1/boards/b1", json={"id": "b1", "name": "Old", "url": ""}, ) requests_mock.put( "https://api.trello.com/1/boards/b1", json={"id": "b1", "name": "Renamed", "url": ""}, ) result = json.loads(trello_rename_board(board_id="b1", name="Renamed")) assert result["success"] is True assert result["board"]["name"] == "Renamed" def test_trello_archive_board_tool(self, requests_mock: Any) -> None: """Archive board tool returns valid JSON.""" from trello_plugin.tools import trello_archive_board os.environ["TRELLO_API_KEY"] = "key" os.environ["TRELLO_TOKEN"] = "tok" requests_mock.get( "https://api.trello.com/1/boards/b1", json={"id": "b1", "name": "Test Board", "url": ""}, ) requests_mock.put( "https://api.trello.com/1/boards/b1", json={"id": "b1", "name": "Test Board", "closed": True}, ) result = json.loads(trello_archive_board(board_id="b1")) assert result["success"] is True assert "archived" in result["message"].lower() def test_trello_open_board_tool(self, requests_mock: Any) -> None: """Open board tool returns valid JSON.""" from trello_plugin.tools import trello_open_board os.environ["TRELLO_API_KEY"] = "key" os.environ["TRELLO_TOKEN"] = "tok" requests_mock.get( "https://api.trello.com/1/boards/b1", json={"id": "b1", "name": "Test Board", "url": ""}, ) requests_mock.put( "https://api.trello.com/1/boards/b1", json={"id": "b1", "name": "Test Board", "closed": False}, ) result = json.loads(trello_open_board(board_id="b1")) assert result["success"] is True assert "opened" in result["message"].lower() def test_trello_board_details_tool(self, requests_mock: Any) -> None: """Board details tool returns valid JSON.""" from trello_plugin.tools import trello_board_details os.environ["TRELLO_API_KEY"] = "key" os.environ["TRELLO_TOKEN"] = "tok" requests_mock.get( "https://api.trello.com/1/boards/b1", json={"id": "b1", "name": "Test", "url": ""}, ) requests_mock.get( "https://api.trello.com/1/boards/b1", json={ "id": "b1", "name": "Test", "url": "", "desc": "", "closed": False, "starred": False, "lists": [{"id": "l1", "name": "To Do"}], "members": [], }, ) result = json.loads(trello_board_details(board_id="b1")) assert result["success"] is True assert len(result["board"]["lists"]) == 1