fix(deck): include archived cards in list tools for status=all/archived
The active Deck listing endpoints (StackService::findAll / CardMapper::findAllForStacks and StackService::find / CardMapper::findAll) filter out archived cards at the SQL level — only the /stacks/archived endpoint returns them. The client-side status="all"/"archived" filters in deck_get_cards, deck_get_stacks, deck_get_stack and deck_get_board_overview therefore operated on a list the server had already stripped of archived cards, so they could never surface one. deck_get_card (by ID) bypasses the filter, which is why it appeared to work. Fixes #842. When status is "all" or "archived", also fetch /stacks/archived (client.deck.get_archived_stacks) and merge those cards back in per stack — concurrently with the active fetch where applicable. status="open"/"done" are unchanged and cost no extra call. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
792c536802
commit
961449be30
@@ -421,3 +421,123 @@ async def test_deck_get_board_overview_mcp(
|
||||
assert stack["card_count"] == len(stack["cards"])
|
||||
card_ids = [c["id"] for c in stack["cards"]]
|
||||
assert card_data["id"] in card_ids
|
||||
|
||||
|
||||
# Archived-card visibility in the active list tools (issue #842)
|
||||
|
||||
|
||||
async def _create_and_archive_card(
|
||||
nc_mcp_client: ClientSession, board_id: int, stack_id: int
|
||||
) -> int:
|
||||
"""Create a card in the given stack, archive it, and return its id.
|
||||
|
||||
The card is cleaned up when the temporary stack is deleted by the
|
||||
fixture teardown (deleting a stack removes its cards)."""
|
||||
create_result = await nc_mcp_client.call_tool(
|
||||
"deck_create_card",
|
||||
{
|
||||
"board_id": board_id,
|
||||
"stack_id": stack_id,
|
||||
"title": f"Archived Card {uuid.uuid4().hex[:8]}",
|
||||
},
|
||||
)
|
||||
assert create_result.isError is False, f"create failed: {create_result.content}"
|
||||
archived_id = json.loads(create_result.content[0].text)["id"]
|
||||
|
||||
archive_result = await nc_mcp_client.call_tool(
|
||||
"deck_archive_card",
|
||||
{"board_id": board_id, "stack_id": stack_id, "card_id": archived_id},
|
||||
)
|
||||
assert archive_result.isError is False, f"archive failed: {archive_result.content}"
|
||||
return archived_id
|
||||
|
||||
|
||||
async def test_deck_get_cards_status_includes_archived_mcp(
|
||||
nc_mcp_client: ClientSession, temporary_board_with_card: tuple
|
||||
):
|
||||
"""deck_get_cards: archived cards appear under status="all"/"archived" and
|
||||
are excluded under the default "open" — the regression behind issue #842."""
|
||||
board_data, stack_data, card_data = temporary_board_with_card
|
||||
board_id = board_data["id"]
|
||||
stack_id = stack_data["id"]
|
||||
open_id = card_data["id"]
|
||||
archived_id = await _create_and_archive_card(nc_mcp_client, board_id, stack_id)
|
||||
|
||||
async def card_ids(status: str) -> list[int]:
|
||||
result = await nc_mcp_client.call_tool(
|
||||
"deck_get_cards",
|
||||
{"board_id": board_id, "stack_id": stack_id, "status": status},
|
||||
)
|
||||
assert result.isError is False, f"deck_get_cards({status}) failed"
|
||||
return [c["id"] for c in json.loads(result.content[0].text)["cards"]]
|
||||
|
||||
open_ids = await card_ids("open")
|
||||
assert open_id in open_ids
|
||||
assert archived_id not in open_ids, "archived card must not show under 'open'"
|
||||
|
||||
all_ids = await card_ids("all")
|
||||
assert open_id in all_ids and archived_id in all_ids, (
|
||||
"status='all' must include both open and archived cards"
|
||||
)
|
||||
|
||||
archived_only = await card_ids("archived")
|
||||
assert archived_only == [archived_id], (
|
||||
f"status='archived' should return only the archived card, got {archived_only}"
|
||||
)
|
||||
|
||||
|
||||
async def test_deck_get_stack_status_includes_archived_mcp(
|
||||
nc_mcp_client: ClientSession, temporary_board_with_card: tuple
|
||||
):
|
||||
"""deck_get_stack (single stack) honours archived cards for status
|
||||
"all"/"archived" too, mirroring deck_get_cards (issue #842)."""
|
||||
board_data, stack_data, card_data = temporary_board_with_card
|
||||
board_id = board_data["id"]
|
||||
stack_id = stack_data["id"]
|
||||
archived_id = await _create_and_archive_card(nc_mcp_client, board_id, stack_id)
|
||||
|
||||
async def stack_card_ids(status: str) -> list[int]:
|
||||
result = await nc_mcp_client.call_tool(
|
||||
"deck_get_stack",
|
||||
{"board_id": board_id, "stack_id": stack_id, "status": status},
|
||||
)
|
||||
assert result.isError is False, f"deck_get_stack({status}) failed"
|
||||
payload = json.loads(result.content[0].text)
|
||||
return [c["id"] for c in (payload.get("cards") or [])]
|
||||
|
||||
assert archived_id not in await stack_card_ids("open")
|
||||
assert archived_id in await stack_card_ids("all")
|
||||
assert await stack_card_ids("archived") == [archived_id]
|
||||
|
||||
|
||||
async def test_deck_get_stacks_and_overview_include_archived_mcp(
|
||||
nc_mcp_client: ClientSession, temporary_board_with_card: tuple
|
||||
):
|
||||
"""deck_get_stacks and deck_get_board_overview surface archived cards when
|
||||
status="all" (issue #842), keyed onto the correct stack."""
|
||||
board_data, stack_data, card_data = temporary_board_with_card
|
||||
board_id = board_data["id"]
|
||||
stack_id = stack_data["id"]
|
||||
archived_id = await _create_and_archive_card(nc_mcp_client, board_id, stack_id)
|
||||
|
||||
# deck_get_stacks(status="all")
|
||||
stacks_result = await nc_mcp_client.call_tool(
|
||||
"deck_get_stacks", {"board_id": board_id, "status": "all"}
|
||||
)
|
||||
assert stacks_result.isError is False, "deck_get_stacks(all) failed"
|
||||
stacks_payload = json.loads(stacks_result.content[0].text)
|
||||
stack = next(s for s in stacks_payload["stacks"] if s["id"] == stack_id)
|
||||
stack_card_ids = [c["id"] for c in (stack.get("cards") or [])]
|
||||
assert card_data["id"] in stack_card_ids
|
||||
assert archived_id in stack_card_ids, "archived card missing from deck_get_stacks"
|
||||
|
||||
# deck_get_board_overview(status="all")
|
||||
overview_result = await nc_mcp_client.call_tool(
|
||||
"deck_get_board_overview", {"board_id": board_id, "status": "all"}
|
||||
)
|
||||
assert overview_result.isError is False, "board overview failed"
|
||||
overview_payload = json.loads(overview_result.content[0].text)
|
||||
ov_stack = next(s for s in overview_payload["stacks"] if s["id"] == stack_id)
|
||||
ov_card_ids = [c["id"] for c in ov_stack["cards"]]
|
||||
assert archived_id in ov_card_ids, "archived card missing from board overview"
|
||||
assert ov_stack["card_count"] == len(ov_stack["cards"])
|
||||
|
||||
Reference in New Issue
Block a user