App-karr

 view release on metacpan or  search on metacpan

t/236-delete-dependent-warning.t  view on Meta::CPAN

use strict;
use warnings;
use Test::More;
use lib 't/lib';
use TestGit qw( require_git_c );
require_git_c();
use TestKarr qw( run_karr run_karr_stdin );
use File::Temp qw( tempdir );
use JSON::MaybeXS qw( decode_json );

use App::karr::Git;
use App::karr::BoardStore;
use App::karr::Task;

# Ticket #236: `karr delete` said nothing about the cards left pointing at the
# id it removed. `karr edit 6 --add-depends-on 2` then `karr delete 2 --yes`
# printed "Deleted task 2" and nothing else, and card 6 was left carrying a
# dependency on an id the board no longer has -- which `karr show 6` then
# renders as "2 (unknown)". The information existed, it simply arrived after
# the only moment it could have stopped anybody.
#
# kanban-md collects board.FindDependents before the delete and prints each hit
# as a "Warning:" on STDERR (cmd/delete.go, internal/board/board.go:53-72). It
# warns and deletes anyway, and karr follows: refusing would make a hard delete
# depend on cards the operator may not care about, and karr already answers the
# whole depends_on family by warning rather than blocking (#123).
#
# It weighs more here than in the reference, because karr's `delete` really
# removes the ref while kanban-md's archives the card -- and karr has `archive`
# as the soft way out, which is exactly where this warning sends a caller.
#
# The channel is the one App::karr::Role::DependencyCheck already argued for:
# the human copy on STDERR so STDOUT stays parseable, --json carrying the same
# sentence in the result object because a JSON consumer never reads STDERR, and
# --quiet silencing the STDERR copy only.

# In-process runner (t/lib/TestKarr.pm): same ($cwd, @argv) signature and
# { exit, stdout, stderr } return as the open3 helper this file used to carry,
# dispatched through the shared App::karr::Dispatch path. This file's own
# convention -- a leading SCALAR ref in @argv standing for the answer typed at
# `karr delete`'s confirmation prompt -- still works, now routed through
# run_karr_stdin. KARR_TEST_SUBPROC=1 restores the old open3 path.
sub _run_karr {
    my ( $cwd, @argv ) = @_;
    my $stdin_text = ref $argv[0] eq 'SCALAR' ? ${ shift @argv } : undef;
    return defined $stdin_text
        ? run_karr_stdin( $cwd, $stdin_text, @argv )
        : run_karr( $cwd, @argv );
}

# A fresh isolated temp repo per subtest, never the developer's real board.
# Seeded through BoardStore rather than `karr create`, because `parent` has no
# CLI route at all (App::karr::Task) and depends_on only has one on cards that
# already exist.
sub _board {
    my (@specs) = @_;
    my $repo = tempdir( CLEANUP => 1 );
    system( 'git', 'init', '-q', $repo ) == 0 or die 'git init';
    system( 'git', '-C', $repo, 'config', 'user.email', 'test@example.com' ) == 0
        or die 'git config';
    system( 'git', '-C', $repo, 'config', 'user.name', 'Test User' ) == 0
        or die 'git config';

    my $init = _run_karr( $repo, 'init', '--name', 'Dependent Board' );
    die "karr init failed: $init->{stderr}" if $init->{exit};

    my $git   = App::karr::Git->new( dir => $repo );
    my $store = App::karr::BoardStore->new( git => $git );
    for my $spec (@specs) {
        $store->save_task(
            App::karr::Task->new(
                id     => $spec->{id},
                title  => $spec->{title} // "Task $spec->{id}",
                status => $spec->{status} // 'todo',
                ( $spec->{depends_on} ? ( depends_on => $spec->{depends_on} ) : () ),
                ( defined $spec->{parent} ? ( parent => $spec->{parent} ) : () ),
            )
        );
    }
    $git->write_ref( 'refs/karr/meta/next-id', "50\n" );

    return $repo;
}

sub _task {
    my ( $repo, $id ) = @_;
    return App::karr::Git->new( dir => $repo )->load_task_ref($id);
}

subtest 'deleting a card another card depends on warns and names it' => sub {
    my $repo = _board(
        { id => 2, title => 'K2' },
        { id => 6, title => 'K6', depends_on => [2] },
    );

    my $r = _run_karr( $repo, 'delete', '2', '--yes' );
    is( $r->{exit}, 0, 'the delete is not refused -- warn, do not block' )
        or diag $r->{stderr};
    like( $r->{stderr}, qr/task 6 \(K6\) depends on task 2/,
        'the warning names the dependent card by id and title' );
    like( $r->{stderr}, qr/karr archive 2/,



( run in 0.357 second using v1.01-cache-2.11-cpan-aadc1410aed )