From 13abaf3db7362ab2719e0205e5b91cb7272397e5 Mon Sep 17 00:00:00 2001 From: Chris Coutinho Date: Wed, 29 Apr 2026 15:35:10 +0200 Subject: [PATCH] test(deck): add integration tests for card comment tools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cover full CRUD lifecycle (create → list → update → delete → verify gone) and the reply path where parent_id populates replyTo on the new comment. Tests run against the live mcp container via the existing nc_mcp_client fixture and reuse the temporary_board_with_card fixture for setup/cleanup. Co-Authored-By: Claude Opus 4.7 (1M context) --- tests/server/test_deck_mcp.py | 114 ++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/tests/server/test_deck_mcp.py b/tests/server/test_deck_mcp.py index ab3734dd..acca2746 100644 --- a/tests/server/test_deck_mcp.py +++ b/tests/server/test_deck_mcp.py @@ -221,3 +221,117 @@ async def test_deck_workflow_integration_mcp( # 3. Verify board data matches via resource (already done in step 1) logger.info(f"Board data verification completed for board: {board_id}") logger.info("Board structure and data verified successfully") + + +# Card Comment Tests + + +async def test_deck_card_comment_crud_workflow_mcp( + nc_mcp_client: ClientSession, + nc_client: NextcloudClient, + temporary_board_with_card: tuple, +): + """Full CRUD lifecycle for card comments via MCP tools.""" + _, _, card_data = temporary_board_with_card + card_id = card_data["id"] + + # 1. Create a top-level comment via MCP + create_result = await nc_mcp_client.call_tool( + "deck_create_card_comment", + {"card_id": card_id, "message": "Initial comment"}, + ) + assert create_result.isError is False, ( + f"Comment creation failed: {create_result.content}" + ) + comment = json.loads(create_result.content[0].text) + comment_id = comment["id"] + assert comment["objectId"] == card_id + assert comment["message"] == "Initial comment" + assert comment["replyTo"] is None + logger.info(f"Created comment ID {comment_id} on card {card_id}") + + # 2. List comments via MCP — verify the new comment is present + list_result = await nc_mcp_client.call_tool( + "deck_get_card_comments", {"card_id": card_id} + ) + assert list_result.isError is False, f"List comments failed: {list_result.content}" + listed = json.loads(list_result.content[0].text) + assert listed["total"] >= 1 + listed_ids = [c["id"] for c in listed["results"]] + assert comment_id in listed_ids, "Created comment not in list" + + # 3. Cross-check via direct client + direct_comments = await nc_client.deck.get_comments(card_id) + direct_ids = [c.id for c in direct_comments] + assert comment_id in direct_ids, "Created comment not visible via direct client" + + # 4. Update the comment via MCP + update_result = await nc_mcp_client.call_tool( + "deck_update_card_comment", + { + "card_id": card_id, + "comment_id": comment_id, + "message": "Edited comment", + }, + ) + assert update_result.isError is False, ( + f"Comment update failed: {update_result.content}" + ) + updated = json.loads(update_result.content[0].text) + assert updated["id"] == comment_id + assert updated["message"] == "Edited comment" + + # 5. Delete the comment via MCP + delete_result = await nc_mcp_client.call_tool( + "deck_delete_card_comment", + {"card_id": card_id, "comment_id": comment_id}, + ) + assert delete_result.isError is False, ( + f"Comment delete failed: {delete_result.content}" + ) + delete_response = json.loads(delete_result.content[0].text) + assert delete_response["success"] is True + assert delete_response["card_id"] == card_id + assert delete_response["comment_id"] == comment_id + + # 6. Verify the comment is gone + final_list_result = await nc_mcp_client.call_tool( + "deck_get_card_comments", {"card_id": card_id} + ) + final_listed = json.loads(final_list_result.content[0].text) + final_ids = [c["id"] for c in final_listed["results"]] + assert comment_id not in final_ids, "Comment still present after delete" + + +async def test_deck_card_comment_reply_mcp( + nc_mcp_client: ClientSession, temporary_board_with_card: tuple +): + """Replying with parent_id populates replyTo on the new comment.""" + _, _, card_data = temporary_board_with_card + card_id = card_data["id"] + + # Create the parent comment + parent_result = await nc_mcp_client.call_tool( + "deck_create_card_comment", + {"card_id": card_id, "message": "Parent message"}, + ) + assert parent_result.isError is False + parent = json.loads(parent_result.content[0].text) + parent_id = parent["id"] + + # Create a reply + reply_result = await nc_mcp_client.call_tool( + "deck_create_card_comment", + { + "card_id": card_id, + "message": "Reply message", + "parent_id": parent_id, + }, + ) + assert reply_result.isError is False, f"Reply failed: {reply_result.content}" + reply = json.loads(reply_result.content[0].text) + + assert reply["message"] == "Reply message" + assert reply["replyTo"] is not None, "replyTo should be populated for replies" + assert reply["replyTo"]["id"] == parent_id + assert reply["replyTo"]["message"] == "Parent message"