test: Define expected partial update behavior for DeckClient.update_card
Refactor tests to assert what SHOULD happen (partial updates preserve unchanged fields) rather than documenting current buggy behavior. Tests will fail until fix is implemented in client or upstream. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
30d3d9f0cf
commit
71ace47197
@@ -1,22 +1,16 @@
|
|||||||
"""
|
"""
|
||||||
Integration tests for DeckClient.update_card API behavior.
|
Integration tests for DeckClient.update_card API behavior.
|
||||||
|
|
||||||
This test suite documents the behavior of our DeckClient.update_card method
|
These tests define the EXPECTED behavior for partial card updates:
|
||||||
and identifies bugs in how it handles partial updates.
|
- Only fields explicitly passed should be modified
|
||||||
|
- All other fields should be preserved unchanged
|
||||||
FINDINGS:
|
|
||||||
The Deck API PUT endpoint is a FULL REPLACEMENT, not a partial update.
|
|
||||||
Fields not included in the request body are either:
|
|
||||||
- Required and cause 400 error (title, type, owner)
|
|
||||||
- Optional but get CLEARED if not sent (description)
|
|
||||||
|
|
||||||
Related issues:
|
Related issues:
|
||||||
- nextcloud-mcp-server #452: DeckClient.update_card always sets owner/type
|
- nextcloud-mcp-server #452: DeckClient.update_card partial update bugs
|
||||||
- deck #3127: REST API Docs: missing parameter in "update cards"
|
- deck #3127: REST API Docs: missing parameter in "update cards"
|
||||||
- deck #4106: Provide a working example of API usage to update a cards details
|
- deck #4106: Provide a working example of API usage to update a cards details
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import httpx
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
pytestmark = [pytest.mark.integration]
|
pytestmark = [pytest.mark.integration]
|
||||||
@@ -48,22 +42,15 @@ async def deck_test_card(nc_client):
|
|||||||
|
|
||||||
class TestDeckClientUpdateCard:
|
class TestDeckClientUpdateCard:
|
||||||
"""
|
"""
|
||||||
Test DeckClient.update_card() method behavior with various parameter combinations.
|
Test DeckClient.update_card() partial update behavior.
|
||||||
|
|
||||||
These tests document the current buggy behavior where:
|
Expected: Only explicitly provided fields are updated, all others preserved.
|
||||||
1. Updating without title fails (400) - title is required but conditionally sent
|
|
||||||
2. Updating with title clears description - description should be preserved
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async def test_update_title_only_clears_description(
|
async def test_update_title_only_preserves_description(
|
||||||
self, nc_client, deck_test_card
|
self, nc_client, deck_test_card
|
||||||
):
|
):
|
||||||
"""
|
"""Updating only the title should preserve the description."""
|
||||||
BUG: Updating only the title clears the description.
|
|
||||||
|
|
||||||
The Deck PUT API is a full replacement. Our client doesn't send
|
|
||||||
description when not explicitly provided, so it gets cleared.
|
|
||||||
"""
|
|
||||||
await nc_client.deck.update_card(
|
await nc_client.deck.update_card(
|
||||||
board_id=deck_test_card["board_id"],
|
board_id=deck_test_card["board_id"],
|
||||||
stack_id=deck_test_card["stack_id"],
|
stack_id=deck_test_card["stack_id"],
|
||||||
@@ -77,28 +64,27 @@ class TestDeckClientUpdateCard:
|
|||||||
deck_test_card["card_id"],
|
deck_test_card["card_id"],
|
||||||
)
|
)
|
||||||
assert updated.title == "New Title"
|
assert updated.title == "New Title"
|
||||||
# BUG: Description was cleared instead of preserved
|
assert updated.description == "Original description"
|
||||||
assert updated.description == "" # Should be "Original description"
|
|
||||||
|
|
||||||
async def test_update_description_only_fails(self, nc_client, deck_test_card):
|
async def test_update_description_only(self, nc_client, deck_test_card):
|
||||||
"""
|
"""Updating only the description should work and preserve other fields."""
|
||||||
BUG: Updating only the description fails with 400.
|
await nc_client.deck.update_card(
|
||||||
|
board_id=deck_test_card["board_id"],
|
||||||
|
stack_id=deck_test_card["stack_id"],
|
||||||
|
card_id=deck_test_card["card_id"],
|
||||||
|
description="New description only",
|
||||||
|
)
|
||||||
|
|
||||||
The Deck PUT API requires title, type, and owner.
|
updated = await nc_client.deck.get_card(
|
||||||
Our client doesn't send title when not explicitly provided.
|
deck_test_card["board_id"],
|
||||||
"""
|
deck_test_card["stack_id"],
|
||||||
with pytest.raises(httpx.HTTPStatusError) as exc_info:
|
deck_test_card["card_id"],
|
||||||
await nc_client.deck.update_card(
|
)
|
||||||
board_id=deck_test_card["board_id"],
|
assert updated.title == "Original Title"
|
||||||
stack_id=deck_test_card["stack_id"],
|
assert updated.description == "New description only"
|
||||||
card_id=deck_test_card["card_id"],
|
|
||||||
description="New description only",
|
|
||||||
)
|
|
||||||
|
|
||||||
assert exc_info.value.response.status_code == 400
|
|
||||||
|
|
||||||
async def test_update_title_and_description(self, nc_client, deck_test_card):
|
async def test_update_title_and_description(self, nc_client, deck_test_card):
|
||||||
"""Updating title and description together works correctly."""
|
"""Updating title and description together should work."""
|
||||||
await nc_client.deck.update_card(
|
await nc_client.deck.update_card(
|
||||||
board_id=deck_test_card["board_id"],
|
board_id=deck_test_card["board_id"],
|
||||||
stack_id=deck_test_card["stack_id"],
|
stack_id=deck_test_card["stack_id"],
|
||||||
@@ -115,40 +101,62 @@ class TestDeckClientUpdateCard:
|
|||||||
assert updated.title == "New Title"
|
assert updated.title == "New Title"
|
||||||
assert updated.description == "New description"
|
assert updated.description == "New description"
|
||||||
|
|
||||||
async def test_update_duedate_only_fails(self, nc_client, deck_test_card):
|
async def test_update_duedate_only(self, nc_client, deck_test_card):
|
||||||
"""
|
"""Updating only the duedate should work and preserve other fields."""
|
||||||
BUG: Updating only the duedate fails with 400.
|
await nc_client.deck.update_card(
|
||||||
|
board_id=deck_test_card["board_id"],
|
||||||
|
stack_id=deck_test_card["stack_id"],
|
||||||
|
card_id=deck_test_card["card_id"],
|
||||||
|
duedate="2025-12-31T23:59:59+00:00",
|
||||||
|
)
|
||||||
|
|
||||||
title is required but not sent when not explicitly provided.
|
updated = await nc_client.deck.get_card(
|
||||||
"""
|
deck_test_card["board_id"],
|
||||||
with pytest.raises(httpx.HTTPStatusError) as exc_info:
|
deck_test_card["stack_id"],
|
||||||
await nc_client.deck.update_card(
|
deck_test_card["card_id"],
|
||||||
board_id=deck_test_card["board_id"],
|
)
|
||||||
stack_id=deck_test_card["stack_id"],
|
assert updated.title == "Original Title"
|
||||||
card_id=deck_test_card["card_id"],
|
assert updated.description == "Original description"
|
||||||
duedate="2025-12-31T23:59:59+00:00",
|
assert updated.duedate is not None
|
||||||
)
|
|
||||||
|
|
||||||
assert exc_info.value.response.status_code == 400
|
async def test_update_archived_only(self, nc_client, deck_test_card):
|
||||||
|
"""Updating only the archived status should work and preserve other fields."""
|
||||||
|
await nc_client.deck.update_card(
|
||||||
|
board_id=deck_test_card["board_id"],
|
||||||
|
stack_id=deck_test_card["stack_id"],
|
||||||
|
card_id=deck_test_card["card_id"],
|
||||||
|
archived=True,
|
||||||
|
)
|
||||||
|
|
||||||
async def test_update_archived_only_fails(self, nc_client, deck_test_card):
|
updated = await nc_client.deck.get_card(
|
||||||
"""
|
deck_test_card["board_id"],
|
||||||
BUG: Updating only the archived status fails with 400.
|
deck_test_card["stack_id"],
|
||||||
|
deck_test_card["card_id"],
|
||||||
|
)
|
||||||
|
assert updated.title == "Original Title"
|
||||||
|
assert updated.description == "Original description"
|
||||||
|
assert updated.archived is True
|
||||||
|
|
||||||
title is required but not sent when not explicitly provided.
|
async def test_update_order_only(self, nc_client, deck_test_card):
|
||||||
"""
|
"""Updating only the order should work and preserve other fields."""
|
||||||
with pytest.raises(httpx.HTTPStatusError) as exc_info:
|
await nc_client.deck.update_card(
|
||||||
await nc_client.deck.update_card(
|
board_id=deck_test_card["board_id"],
|
||||||
board_id=deck_test_card["board_id"],
|
stack_id=deck_test_card["stack_id"],
|
||||||
stack_id=deck_test_card["stack_id"],
|
card_id=deck_test_card["card_id"],
|
||||||
card_id=deck_test_card["card_id"],
|
order=99,
|
||||||
archived=True,
|
)
|
||||||
)
|
|
||||||
|
|
||||||
assert exc_info.value.response.status_code == 400
|
updated = await nc_client.deck.get_card(
|
||||||
|
deck_test_card["board_id"],
|
||||||
|
deck_test_card["stack_id"],
|
||||||
|
deck_test_card["card_id"],
|
||||||
|
)
|
||||||
|
assert updated.title == "Original Title"
|
||||||
|
assert updated.description == "Original description"
|
||||||
|
assert updated.order == 99
|
||||||
|
|
||||||
async def test_update_preserves_type(self, nc_client, deck_test_card):
|
async def test_update_preserves_type(self, nc_client, deck_test_card):
|
||||||
"""Type is correctly preserved (already always sent in current implementation)."""
|
"""Type should be preserved when not explicitly changed."""
|
||||||
original = deck_test_card["card"]
|
original = deck_test_card["card"]
|
||||||
|
|
||||||
await nc_client.deck.update_card(
|
await nc_client.deck.update_card(
|
||||||
@@ -164,9 +172,10 @@ class TestDeckClientUpdateCard:
|
|||||||
deck_test_card["card_id"],
|
deck_test_card["card_id"],
|
||||||
)
|
)
|
||||||
assert updated.type == original.type
|
assert updated.type == original.type
|
||||||
|
assert updated.description == "Original description"
|
||||||
|
|
||||||
async def test_update_preserves_owner(self, nc_client, deck_test_card):
|
async def test_update_preserves_owner(self, nc_client, deck_test_card):
|
||||||
"""Owner is correctly preserved (already always sent in current implementation)."""
|
"""Owner should be preserved when not explicitly changed."""
|
||||||
original = deck_test_card["card"]
|
original = deck_test_card["card"]
|
||||||
|
|
||||||
await nc_client.deck.update_card(
|
await nc_client.deck.update_card(
|
||||||
@@ -182,19 +191,4 @@ class TestDeckClientUpdateCard:
|
|||||||
deck_test_card["card_id"],
|
deck_test_card["card_id"],
|
||||||
)
|
)
|
||||||
assert updated.owner == original.owner
|
assert updated.owner == original.owner
|
||||||
|
assert updated.description == "Original description"
|
||||||
async def test_update_order_only_fails(self, nc_client, deck_test_card):
|
|
||||||
"""
|
|
||||||
BUG: Updating only the order fails with 400.
|
|
||||||
|
|
||||||
title is required but not sent when not explicitly provided.
|
|
||||||
"""
|
|
||||||
with pytest.raises(httpx.HTTPStatusError) as exc_info:
|
|
||||||
await nc_client.deck.update_card(
|
|
||||||
board_id=deck_test_card["board_id"],
|
|
||||||
stack_id=deck_test_card["stack_id"],
|
|
||||||
card_id=deck_test_card["card_id"],
|
|
||||||
order=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert exc_info.value.response.status_code == 400
|
|
||||||
|
|||||||
Reference in New Issue
Block a user