224 lines
7.5 KiB
Ruby
224 lines
7.5 KiB
Ruby
require_relative "test_helper"
|
|
|
|
class CardsTest < Minitest::Test
|
|
include BacklogTestHelpers
|
|
|
|
def setup
|
|
@client = FakePlankaClient.new
|
|
@ensurer = Backlog::BoardEnsurer.new(client: @client, human_user_id: "human-1")
|
|
@ensurer.ensure("llf-schema", project_name: "Dev")
|
|
@cards = Backlog::Cards.new(client: @client)
|
|
end
|
|
|
|
def test_add_creates_card_at_backlog_list
|
|
card = @cards.add(board_name: "llf-schema", title: "Fix the flaky import")
|
|
|
|
backlog = @client.lists_store.find { |l| l.name == "Backlog" }
|
|
assert_equal backlog.id, card.list_id
|
|
assert_equal "Fix the flaky import", card.name
|
|
assert_equal "project", card.type
|
|
end
|
|
|
|
def test_add_positions_new_card_after_existing_backlog_cards
|
|
first = @cards.add(board_name: "llf-schema", title: "one")
|
|
second = @cards.add(board_name: "llf-schema", title: "two")
|
|
|
|
assert_operator second.position, :>, first.position
|
|
end
|
|
|
|
def test_add_with_description
|
|
card = @cards.add(board_name: "llf-schema", title: "t", description: "details here")
|
|
|
|
assert_equal "details here", card.description
|
|
end
|
|
|
|
def test_add_raises_for_missing_board
|
|
error = assert_raises(RuntimeError) { @cards.add(board_name: "nope", title: "t") }
|
|
assert_match(/no board named/, error.message)
|
|
end
|
|
|
|
def test_snapshot_shape_and_list_order
|
|
@cards.add(board_name: "llf-schema", title: "captured task")
|
|
|
|
snapshot = @cards.snapshot(board_name: "llf-schema")
|
|
|
|
assert_equal "llf-schema", snapshot["board"]
|
|
assert_equal Backlog::BoardSpec::LISTS, snapshot["lists"].map { |l| l["name"] }
|
|
backlog = snapshot["lists"].find { |l| l["name"] == "Backlog" }
|
|
assert_equal ["captured task"], backlog["cards"].map { |c| c["name"] }
|
|
assert_equal Backlog::BoardSpec::LABEL_NAMES.sort, snapshot["labels"].sort
|
|
end
|
|
|
|
def test_snapshot_card_emits_complete_key_set
|
|
@cards.add(board_name: "llf-schema", title: "captured task")
|
|
|
|
snapshot = @cards.snapshot(board_name: "llf-schema")
|
|
backlog = snapshot["lists"].find { |l| l["name"] == "Backlog" }
|
|
card = backlog["cards"].first
|
|
|
|
expected_keys = %w[
|
|
id createdAt updatedAt type position name description dueDate
|
|
isDueCompleted stopwatch commentsTotal isClosed listChangedAt
|
|
boardId listId creatorUserId prevListId coverAttachmentId
|
|
isSubscribed labels
|
|
]
|
|
assert_equal expected_keys.sort, card.keys.sort
|
|
assert card.key?("listChangedAt"), "snapshot card must carry listChangedAt for board-audit"
|
|
end
|
|
|
|
def test_snapshot_includes_card_label_names
|
|
card = @cards.add(board_name: "llf-schema", title: "labeled task")
|
|
p1 = @client.labels_store.find { |l| l.name == "P1" }
|
|
@client.attach_label(card.id, p1.id)
|
|
|
|
snapshot = @cards.snapshot(board_name: "llf-schema")
|
|
|
|
backlog = snapshot["lists"].find { |l| l["name"] == "Backlog" }
|
|
assert_equal ["P1"], backlog["cards"].first["labels"]
|
|
end
|
|
|
|
def test_snapshot_raises_for_missing_board
|
|
assert_raises(RuntimeError) { @cards.snapshot(board_name: "nope") }
|
|
end
|
|
|
|
def test_attach_label_by_name
|
|
card = @cards.add(board_name: "llf-schema", title: "raw card")
|
|
|
|
@cards.attach_label(board_name: "llf-schema", card_id: card.id, label: "afk-ready")
|
|
|
|
snapshot = @cards.snapshot(board_name: "llf-schema")
|
|
backlog = snapshot["lists"].find { |l| l["name"] == "Backlog" }
|
|
assert_equal ["afk-ready"], backlog["cards"].first["labels"]
|
|
end
|
|
|
|
def test_attach_label_raises_for_unknown_label
|
|
card = @cards.add(board_name: "llf-schema", title: "raw card")
|
|
|
|
error = assert_raises(RuntimeError) do
|
|
@cards.attach_label(board_name: "llf-schema", card_id: card.id, label: "nonexistent")
|
|
end
|
|
assert_match(/no label/, error.message)
|
|
end
|
|
|
|
def test_comment_adds_text_to_card
|
|
card = @cards.add(board_name: "llf-schema", title: "stale card")
|
|
|
|
comment = @cards.comment(card_id: card.id, text: "audit: in Doing 9 days")
|
|
|
|
assert_equal card.id, comment.card_id
|
|
assert_equal "audit: in Doing 9 days", comment.text
|
|
assert_equal 1, @client.comments.store.size
|
|
end
|
|
|
|
# -- move -----------------------------------------------------------
|
|
|
|
def list_named(name)
|
|
@client.lists_store.find { |l| l.name == name }
|
|
end
|
|
|
|
def move_card_to(list_name, card = nil)
|
|
card ||= @cards.add(board_name: "llf-schema", title: "moving card")
|
|
@client.cards.move(card.id, list_id: list_named(list_name).id, position: 65_536) unless list_name == "Backlog"
|
|
card
|
|
end
|
|
|
|
def label!(card, name)
|
|
label = @client.labels_store.find { |l| l.name == name }
|
|
@client.attach_label(card.id, label.id)
|
|
end
|
|
|
|
def test_move_backlog_to_doing
|
|
card = @cards.add(board_name: "llf-schema", title: "task")
|
|
|
|
moved = @cards.move(board_name: "llf-schema", card_id: card.id, to: "Doing")
|
|
|
|
assert_equal list_named("Doing").id, moved.list_id
|
|
end
|
|
|
|
def test_move_doing_to_review
|
|
card = move_card_to("Doing")
|
|
|
|
moved = @cards.move(board_name: "llf-schema", card_id: card.id, to: "Review")
|
|
|
|
assert_equal list_named("Review").id, moved.list_id
|
|
end
|
|
|
|
def test_move_afk_ready_doing_to_done
|
|
card = move_card_to("Doing")
|
|
label!(card, "afk-ready")
|
|
|
|
moved = @cards.move(board_name: "llf-schema", card_id: card.id, to: "Done")
|
|
|
|
assert_equal list_named("Done").id, moved.list_id
|
|
end
|
|
|
|
def test_move_positions_card_at_end_of_destination_list
|
|
first = move_card_to("Doing")
|
|
second = @cards.add(board_name: "llf-schema", title: "second")
|
|
|
|
@cards.move(board_name: "llf-schema", card_id: first.id, to: "Waiting")
|
|
moved_second = @cards.move(board_name: "llf-schema", card_id: second.id, to: "Waiting")
|
|
|
|
waiting_first = @client.cards.all_for_board(list_named("Waiting").board_id)
|
|
.find { |c| c.id == first.id }
|
|
assert_operator moved_second.position, :>, waiting_first.position
|
|
end
|
|
|
|
def test_move_into_next_refused
|
|
card = @cards.add(board_name: "llf-schema", title: "task")
|
|
|
|
error = assert_raises(RuntimeError) do
|
|
@cards.move(board_name: "llf-schema", card_id: card.id, to: "Next")
|
|
end
|
|
assert_match(/Next is human-curated/, error.message)
|
|
end
|
|
|
|
def test_move_out_of_next_refused
|
|
card = move_card_to("Next")
|
|
|
|
error = assert_raises(RuntimeError) do
|
|
@cards.move(board_name: "llf-schema", card_id: card.id, to: "Doing")
|
|
end
|
|
assert_match(/Next is human-curated/, error.message)
|
|
end
|
|
|
|
def test_move_hitl_card_refused
|
|
card = @cards.add(board_name: "llf-schema", title: "task")
|
|
label!(card, "hitl")
|
|
|
|
error = assert_raises(RuntimeError) do
|
|
@cards.move(board_name: "llf-schema", card_id: card.id, to: "Doing")
|
|
end
|
|
assert_match(/hitl cards are human-owned/, error.message)
|
|
end
|
|
|
|
def test_move_to_done_without_afk_ready_refused
|
|
card = move_card_to("Doing")
|
|
|
|
error = assert_raises(RuntimeError) do
|
|
@cards.move(board_name: "llf-schema", card_id: card.id, to: "Done")
|
|
end
|
|
assert_match(/afk-ready/, error.message)
|
|
end
|
|
|
|
def test_move_out_of_done_refused
|
|
card = move_card_to("Doing")
|
|
label!(card, "afk-ready")
|
|
@cards.move(board_name: "llf-schema", card_id: card.id, to: "Done")
|
|
|
|
error = assert_raises(RuntimeError) do
|
|
@cards.move(board_name: "llf-schema", card_id: card.id, to: "Doing")
|
|
end
|
|
assert_match(/cannot move a card out of Done/, error.message)
|
|
end
|
|
|
|
def test_move_unknown_column_refused
|
|
card = @cards.add(board_name: "llf-schema", title: "task")
|
|
|
|
error = assert_raises(RuntimeError) do
|
|
@cards.move(board_name: "llf-schema", card_id: card.id, to: "Someplace")
|
|
end
|
|
assert_match(/unknown list/, error.message)
|
|
end
|
|
end
|