Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65fe867cf5 |
@@ -3,6 +3,7 @@
|
|||||||
**Feature:** US: Authenticate and Connect to Trello Account
|
**Feature:** US: Authenticate and Connect to Trello Account
|
||||||
**Issue:** #1
|
**Issue:** #1
|
||||||
**Branch:** `feature/trello-auth`
|
**Branch:** `feature/trello-auth`
|
||||||
|
**Doc:** 001
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
# Trello Plugin — Manage Trello Boards
|
|
||||||
|
|
||||||
**Feature:** US: Manage Trello Boards
|
|
||||||
**Issue:** #2
|
|
||||||
**Branch:** `feature/manage-boards`
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Extends the Trello plugin with board management capabilities: create, rename, close/archive, open, and view details of Trello boards.
|
|
||||||
|
|
||||||
## Design Decisions
|
|
||||||
|
|
||||||
- **Extends existing TrelloClient** — All board operations go through the same `TrelloClient` class from the auth feature.
|
|
||||||
- **Board selection by ID** — The Trello API identifies boards by ID. The tool accepts both ID and name (client resolves name to ID).
|
|
||||||
- **Resolves board name to ID** — When a user provides a board name instead of ID, the client fetches all boards and matches by name.
|
|
||||||
|
|
||||||
## Tools
|
|
||||||
|
|
||||||
### `trello_create_board`
|
|
||||||
Create a new Trello board.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
| Param | Type | Required | Description |
|
|
||||||
|-------|------|----------|-------------|
|
|
||||||
| `name` | string | Yes | Board name |
|
|
||||||
| `default_lists` | bool | No | Whether to create the default lists (default: true) |
|
|
||||||
|
|
||||||
**Returns:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"board": {"id": "abc123", "name": "My Board", "url": "https://trello.com/b/abc123"}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### `trello_rename_board`
|
|
||||||
|
|
||||||
Rename an existing board.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
| Param | Type | Required | Description |
|
|
||||||
|-------|------|----------|-------------|
|
|
||||||
| `board_id` | string | Yes | Board ID or name |
|
|
||||||
| `name` | string | Yes | New board name |
|
|
||||||
|
|
||||||
**Returns:**
|
|
||||||
```json
|
|
||||||
{"success": true, "board": {"id": "abc123", "name": "New Name"}}
|
|
||||||
```
|
|
||||||
|
|
||||||
### `trello_archive_board`
|
|
||||||
|
|
||||||
Close/archive a board.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
| Param | Type | Required | Description |
|
|
||||||
|-------|------|----------|-------------|
|
|
||||||
| `board_id` | string | Yes | Board ID or name |
|
|
||||||
|
|
||||||
**Returns:**
|
|
||||||
```json
|
|
||||||
{"success": true, "message": "Board 'My Board' archived."}
|
|
||||||
```
|
|
||||||
|
|
||||||
### `trello_open_board`
|
|
||||||
|
|
||||||
Re-open a closed/archived board.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
| Param | Type | Required | Description |
|
|
||||||
|-------|------|----------|-------------|
|
|
||||||
| `board_id` | string | Yes | Board ID or name |
|
|
||||||
|
|
||||||
**Returns:**
|
|
||||||
```json
|
|
||||||
{"success": true, "message": "Board 'My Board' opened."}
|
|
||||||
```
|
|
||||||
|
|
||||||
### `trello_board_details`
|
|
||||||
|
|
||||||
View details of a specific board, including its lists and members.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
| Param | Type | Required | Description |
|
|
||||||
|-------|------|----------|-------------|
|
|
||||||
| `board_id` | string | Yes | Board ID or name |
|
|
||||||
|
|
||||||
**Returns:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"board": {
|
|
||||||
"id": "abc123",
|
|
||||||
"name": "My Board",
|
|
||||||
"url": "https://trello.com/b/abc123",
|
|
||||||
"desc": "",
|
|
||||||
"closed": false,
|
|
||||||
"starred": false,
|
|
||||||
"lists": [{"id": "l1", "name": "To Do"}, {"id": "l2", "name": "In Progress"}],
|
|
||||||
"members": [{"id": "m1", "username": "user1", "full_name": "User One"}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Trello API Endpoints
|
|
||||||
|
|
||||||
| Purpose | Method | Endpoint |
|
|
||||||
|---------|--------|----------|
|
|
||||||
| Create board | POST | `/1/boards/` |
|
|
||||||
| Update board | PUT | `/1/boards/{id}` |
|
|
||||||
| Get board details | GET | `/1/boards/{id}` (with lists and members fields) |
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
- Board not found → clear message suggesting `trello_list_boards` to find the ID
|
|
||||||
- Validation errors → surfaced directly from Trello API
|
|
||||||
@@ -8,13 +8,8 @@ from trello_plugin.tools import (
|
|||||||
PLUGIN_TOOLS,
|
PLUGIN_TOOLS,
|
||||||
PLUGIN_VERSION,
|
PLUGIN_VERSION,
|
||||||
check_requirements,
|
check_requirements,
|
||||||
trello_archive_board,
|
|
||||||
trello_board_details,
|
|
||||||
trello_create_board,
|
|
||||||
trello_disconnect,
|
trello_disconnect,
|
||||||
trello_list_boards,
|
trello_list_boards,
|
||||||
trello_open_board,
|
|
||||||
trello_rename_board,
|
|
||||||
trello_verify_credentials,
|
trello_verify_credentials,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -27,9 +22,4 @@ __all__ = [
|
|||||||
"trello_verify_credentials",
|
"trello_verify_credentials",
|
||||||
"trello_list_boards",
|
"trello_list_boards",
|
||||||
"trello_disconnect",
|
"trello_disconnect",
|
||||||
"trello_create_board",
|
|
||||||
"trello_rename_board",
|
|
||||||
"trello_archive_board",
|
|
||||||
"trello_open_board",
|
|
||||||
"trello_board_details",
|
|
||||||
]
|
]
|
||||||
@@ -76,105 +76,6 @@ def trello_disconnect() -> str:
|
|||||||
return _respond({"success": True, "message": "Trello credentials cleared from memory. Next tool call will re-read TRELLO_API_KEY and TRELLO_TOKEN from environment and reconnect automatically."})
|
return _respond({"success": True, "message": "Trello credentials cleared from memory. Next tool call will re-read TRELLO_API_KEY and TRELLO_TOKEN from environment and reconnect automatically."})
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Tools — Board Management
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
def trello_create_board(name: str, default_lists: bool = True) -> str: # noqa: FBT001, FBT002
|
|
||||||
"""Create a new Trello board.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
name : str
|
|
||||||
The name for the new board.
|
|
||||||
default_lists : bool, optional
|
|
||||||
Whether to create default lists (default: True).
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
JSON with board id, name, and URL on success.
|
|
||||||
"""
|
|
||||||
client = _get_client()
|
|
||||||
result = client.create_board(name=name, default_lists=default_lists)
|
|
||||||
return _respond(result)
|
|
||||||
|
|
||||||
|
|
||||||
def trello_rename_board(board_id: str, name: str) -> str:
|
|
||||||
"""Rename an existing Trello board.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
board_id : str
|
|
||||||
Board ID or name.
|
|
||||||
name : str
|
|
||||||
The new name for the board.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
JSON with updated board details on success.
|
|
||||||
"""
|
|
||||||
client = _get_client()
|
|
||||||
result = client.rename_board(board_id=board_id, name=name)
|
|
||||||
return _respond(result)
|
|
||||||
|
|
||||||
|
|
||||||
def trello_archive_board(board_id: str) -> str:
|
|
||||||
"""Close/archive a Trello board.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
board_id : str
|
|
||||||
Board ID or name.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
JSON with success message.
|
|
||||||
"""
|
|
||||||
client = _get_client()
|
|
||||||
result = client.archive_board(board_id=board_id)
|
|
||||||
return _respond(result)
|
|
||||||
|
|
||||||
|
|
||||||
def trello_open_board(board_id: str) -> str:
|
|
||||||
"""Re-open a closed/archived Trello board.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
board_id : str
|
|
||||||
Board ID or name.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
JSON with success message.
|
|
||||||
"""
|
|
||||||
client = _get_client()
|
|
||||||
result = client.open_board(board_id=board_id)
|
|
||||||
return _respond(result)
|
|
||||||
|
|
||||||
|
|
||||||
def trello_board_details(board_id: str) -> str:
|
|
||||||
"""View details of a Trello board, including its lists and members.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
board_id : str
|
|
||||||
Board ID or name.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
JSON with board details including lists and members.
|
|
||||||
"""
|
|
||||||
client = _get_client()
|
|
||||||
result = client.board_details(board_id=board_id)
|
|
||||||
return _respond(result)
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Plugin metadata (used by Hermes plugin loader)
|
# Plugin metadata (used by Hermes plugin loader)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -186,11 +87,6 @@ PLUGIN_TOOLS = [
|
|||||||
trello_verify_credentials,
|
trello_verify_credentials,
|
||||||
trello_list_boards,
|
trello_list_boards,
|
||||||
trello_disconnect,
|
trello_disconnect,
|
||||||
trello_create_board,
|
|
||||||
trello_rename_board,
|
|
||||||
trello_archive_board,
|
|
||||||
trello_open_board,
|
|
||||||
trello_board_details,
|
|
||||||
]
|
]
|
||||||
PLUGIN_REQUIRES_ENV = ["TRELLO_API_KEY", "TRELLO_TOKEN"]
|
PLUGIN_REQUIRES_ENV = ["TRELLO_API_KEY", "TRELLO_TOKEN"]
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -325,5 +325,5 @@ class TestPluginMetadata:
|
|||||||
assert PLUGIN_NAME == "trello-plugin"
|
assert PLUGIN_NAME == "trello-plugin"
|
||||||
assert isinstance(PLUGIN_DESCRIPTION, str)
|
assert isinstance(PLUGIN_DESCRIPTION, str)
|
||||||
assert isinstance(PLUGIN_VERSION, str)
|
assert isinstance(PLUGIN_VERSION, str)
|
||||||
assert len(PLUGIN_TOOLS) == 8
|
assert len(PLUGIN_TOOLS) == 3
|
||||||
assert all(callable(t) for t in PLUGIN_TOOLS)
|
assert all(callable(t) for t in PLUGIN_TOOLS)
|
||||||
@@ -1,406 +0,0 @@
|
|||||||
"""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
|
|
||||||
Reference in New Issue
Block a user