fix(deck): preserve done/archived and validate target board on move
Addresses the round-1 review on PR #885: - Preserve `done` across a cross-board move. The internal card-update route (the only one that works cross-board — the board/stack-scoped route 404s for a card not already on that board) does not accept a done value, so a "done" card is re-marked done after the move. Deck stamps the current time there, so the original timestamp isn't preserved — documented as a route limitation. (`archived` is already preserved: CardService only mutates it when sent.) - Validate that target_stack_id is on target_board_id before moving, so the parameter is load-bearing and a mismatch fails loudly instead of misreporting. - Skip the same-board guard's get_stacks round-trip on a same-stack reorder. - Add unit coverage (done-restore call, destination validation, same-stack skip) and integration coverage (done preservation, target-board mismatch). 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
437eaa0872
commit
798a00d89d
@@ -75,6 +75,7 @@ async def test_move_card_to_board_preserves_identity(
|
||||
source_board_id=source_board_id,
|
||||
source_stack_id=source_stack_id,
|
||||
card_id=card.id,
|
||||
target_board_id=target_board_id,
|
||||
target_stack_id=target_stack_id,
|
||||
)
|
||||
|
||||
@@ -105,7 +106,8 @@ async def test_move_card_to_board_remaps_labels(
|
||||
two_boards_with_stacks
|
||||
)
|
||||
|
||||
# Pick a default label that exists on both boards by title
|
||||
# "Finished" is one of the four labels Deck auto-creates on every new
|
||||
# board, so it is reliably present on both the source and target boards.
|
||||
source_board = await nc_client.deck.get_board(source_board_id)
|
||||
target_board = await nc_client.deck.get_board(target_board_id)
|
||||
source_label = next(
|
||||
@@ -131,6 +133,7 @@ async def test_move_card_to_board_remaps_labels(
|
||||
source_board_id=source_board_id,
|
||||
source_stack_id=source_stack_id,
|
||||
card_id=card.id,
|
||||
target_board_id=target_board_id,
|
||||
target_stack_id=target_stack_id,
|
||||
)
|
||||
|
||||
@@ -148,6 +151,69 @@ async def test_move_card_to_board_remaps_labels(
|
||||
assert all(label.id != source_label.id for label in finished)
|
||||
|
||||
|
||||
async def test_move_card_to_board_preserves_done_status(
|
||||
nc_client: NextcloudClient, two_boards_with_stacks: tuple
|
||||
):
|
||||
"""A card marked done keeps its done timestamp across a cross-board move."""
|
||||
source_board_id, source_stack_id, target_board_id, target_stack_id = (
|
||||
two_boards_with_stacks
|
||||
)
|
||||
|
||||
suffix = uuid.uuid4().hex[:8]
|
||||
card = await nc_client.deck.create_card(
|
||||
source_board_id, source_stack_id, f"Done card {suffix}"
|
||||
)
|
||||
done_ts = "2030-01-02T03:04:05+00:00"
|
||||
await nc_client.deck.update_card(
|
||||
board_id=source_board_id,
|
||||
stack_id=source_stack_id,
|
||||
card_id=card.id,
|
||||
done=done_ts,
|
||||
)
|
||||
before = await nc_client.deck.get_card(source_board_id, source_stack_id, card.id)
|
||||
assert before.done is not None, "Card should be done before the move"
|
||||
|
||||
await nc_client.deck.move_card_to_board(
|
||||
source_board_id=source_board_id,
|
||||
source_stack_id=source_stack_id,
|
||||
card_id=card.id,
|
||||
target_board_id=target_board_id,
|
||||
target_stack_id=target_stack_id,
|
||||
)
|
||||
|
||||
after = await nc_client.deck.get_card(target_board_id, target_stack_id, card.id)
|
||||
assert after.done is not None, "done status was cleared during the move"
|
||||
|
||||
|
||||
async def test_move_card_to_board_rejects_stack_not_on_target_board(
|
||||
nc_client: NextcloudClient, two_boards_with_stacks: tuple
|
||||
):
|
||||
"""The move is rejected when target_stack_id is not on target_board_id."""
|
||||
source_board_id, source_stack_id, target_board_id, _target_stack_id = (
|
||||
two_boards_with_stacks
|
||||
)
|
||||
|
||||
suffix = uuid.uuid4().hex[:8]
|
||||
card = await nc_client.deck.create_card(
|
||||
source_board_id, source_stack_id, f"Mismatch {suffix}"
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="not a stack on target board"):
|
||||
await nc_client.deck.move_card_to_board(
|
||||
source_board_id=source_board_id,
|
||||
source_stack_id=source_stack_id,
|
||||
card_id=card.id,
|
||||
target_board_id=target_board_id,
|
||||
target_stack_id=source_stack_id, # on the source board, not target
|
||||
)
|
||||
|
||||
# The card stayed put
|
||||
still_there = await nc_client.deck.get_card(
|
||||
source_board_id, source_stack_id, card.id
|
||||
)
|
||||
assert still_there.stackId == source_stack_id
|
||||
|
||||
|
||||
async def test_reorder_card_rejects_cross_board_target(
|
||||
nc_client: NextcloudClient, two_boards_with_stacks: tuple
|
||||
):
|
||||
|
||||
Reference in New Issue
Block a user