require_relative "test_helper" class BoardResolverTest < Minitest::Test include BacklogTestHelpers def setup @client = FakePlankaClient.new ensurer = Backlog::BoardEnsurer.new(client: @client, human_user_id: "human-1") @board = ensurer.ensure("llf-schema", project_name: "Dev").board @writes = [] end def resolver_with(config) @config = config Backlog::BoardResolver.new( client: @client, read_config: -> { @config }, write_config: ->(contents) { @writes << contents } ) end def config_with_cache(id = @board.id) "# repo config\ntracker=planka:llf-schema\nboard_id=#{id}\nversion=3\n" end def test_cache_hit_uses_boards_get_and_skips_name_listing @client.projects.define_singleton_method(:list) { raise "projects.list must not be called on a cache hit" } resolver = resolver_with(config_with_cache) board = resolver.board_for("llf-schema") assert_equal @board.id, board.id assert_empty @writes end def test_cache_miss_resolves_by_name_and_writes_board_id_preserving_comments resolver = resolver_with("# repo config\ntracker=planka:llf-schema\nversion=3\n") board = resolver.board_for("llf-schema") assert_equal @board.id, board.id assert_equal 1, @writes.size assert_equal "# repo config\ntracker=planka:llf-schema\nversion=3\nboard_id=#{@board.id}\n", @writes.last end def test_stale_id_404_falls_back_to_name_lookup_and_rewrites_cache resolver = resolver_with(config_with_cache("no-such-id")) board = resolver.board_for("llf-schema") assert_equal @board.id, board.id assert_includes @writes.last, "board_id=#{@board.id}\n" assert_includes @writes.last, "# repo config" end def test_renamed_board_name_mismatch_falls_back_and_rewrites_cache stale = @board @client.boards.update(stale.id, name: "something-else") current = @client.boards.create(stale.project_id, name: "llf-schema", position: 2) resolver = resolver_with(config_with_cache(stale.id)) board = resolver.board_for("llf-schema") assert_equal current.id, board.id assert_includes @writes.last, "board_id=#{current.id}\n" end def test_non_planka_tracker_ignores_board_id_and_never_writes resolver = resolver_with("tracker=forgejo:jared/cc-os\nboard_id=bogus-id\n") board = resolver.board_for("llf-schema") assert_equal @board.id, board.id assert_empty @writes end def test_board_id_for_different_planka_board_name_is_ignored resolver = resolver_with("tracker=planka:other-board\nboard_id=bogus-id\n") board = resolver.board_for("llf-schema") assert_equal @board.id, board.id assert_empty @writes # tracker doesn't match this board name; no cache write either end def test_unreadable_config_degrades_to_name_lookup resolver = Backlog::BoardResolver.new( client: @client, read_config: -> { raise Errno::EACCES, "config" }, write_config: ->(contents) { @writes << contents } ) board = resolver.board_for("llf-schema") assert_equal @board.id, board.id assert_empty @writes end def test_unwritable_config_never_fails_the_command resolver = Backlog::BoardResolver.new( client: @client, read_config: -> { "tracker=planka:llf-schema\n" }, write_config: ->(_) { raise Errno::EACCES, "config" } ) board = resolver.board_for("llf-schema") assert_equal @board.id, board.id end def test_matching_cached_id_is_not_rewritten resolver = resolver_with(config_with_cache) resolver.board_for("llf-schema") assert_empty @writes end def test_unknown_board_still_raises resolver = resolver_with(nil) error = assert_raises(RuntimeError) { resolver.board_for("nope") } assert_match(/no board named "nope"/, error.message) end def test_cards_add_through_resolver_writes_cache_once resolver = resolver_with("tracker=planka:llf-schema\n") cards = Backlog::Cards.new(client: @client, board_resolver: resolver) card = cards.add(board_name: "llf-schema", title: "Cache me") assert_equal "Cache me", card.name assert_equal 1, @writes.size assert_includes @writes.last, "board_id=#{@board.id}\n" end end