App-karr
view release on metacpan or search on metacpan
t/263-error-shows-working-command.t view on Meta::CPAN
_hint_is_last( $empty->{stderr}, ' karr needs --board NAME=PATH',
'with no name typed, both halves stay placeholders' );
};
subtest 'needs for an id that is not on the board' => sub {
my $repo = _board_repo('a card');
my $rv = _run_karr( $repo, 'needs', 99 );
is( $rv->{exit}, 1, 'a missing card is a runtime failure (1), unchanged' )
or diag $rv->{stdout} . $rv->{stderr};
like( $rv->{stderr}, qr/\QTask 99 not found on this board\E/, 'the id is named' );
_hint_is_last( $rv->{stderr}, ' karr list --compact',
'and the way to find the ids that do exist is the last line' );
};
#### import
subtest 'import spells out --yes instead of asking for it' => sub {
my $repo = _board_repo('a card');
my $rv = _run_karr( $repo, 'import' );
is( $rv->{exit}, 1, 'a refused destructive operation is still 1' )
or diag $rv->{stdout} . $rv->{stderr};
_hint_is_last( $rv->{stderr}, ' karr import --yes',
'the whole command, not "re-run with --yes"' );
};
subtest 'import with no file view offers the command that writes one' => sub {
my $repo = _board_repo('a card');
my $rv = _run_karr( $repo, 'import', '--yes' );
is( $rv->{exit}, 1, 'exit 1' ) or diag $rv->{stdout} . $rv->{stderr};
like( $rv->{stderr}, qr/\QNo materialized task view found\E/, 'says what is missing' );
_hint_is_last( $rv->{stderr}, ' karr materialize', 'suggestion last' );
};
subtest 'an empty file view offers both intents, in order' => sub {
my $repo = _board_repo('a card');
path($repo)->child('tasks')->mkpath;
my $rv = _run_karr( $repo, 'import', '--yes' );
is( $rv->{exit}, 1, 'exit 1' ) or diag $rv->{stdout} . $rv->{stderr};
like( $rv->{stderr}, qr/\Qthe file view is empty\E/, 'says what it found' );
my @lines = _lines( $rv->{stderr} );
is_deeply( [ @lines[ -2, -1 ] ], [ ' karr materialize', ' karr delete ID' ],
'refresh the view first, delete deliberately second -- both last' )
or diag $rv->{stderr};
};
#### materialize and repair
# Five commands raise this, four off App::karr::BoardStore/has_board_refs and
# one through App::karr::Role::BoardDiscovery/require_board. They are asserted
# together and character for character: the first pass of k263 converted only
# materialize and repair, which left the same sentence spelled two ways
# depending on which command a caller happened to type -- worse than either
# wording on its own, and invisible to a test that checks one command at a time.
subtest 'every board-less repository gets the same message, word for word' => sub {
my @argv = (
[ 'backup' ],
[ 'destroy', '--yes' ],
[ 'materialize' ],
[ 'repair' ],
[ 'create', 'a card' ], # via require_board, not has_board_refs
);
for my $argv (@argv) {
my $repo = _bare_repo();
my $name = join ' ', 'karr', @$argv;
my $rv = _run_karr( $repo, @$argv );
is( $rv->{exit}, 1, "$name: a missing board is a runtime failure (1), unchanged" )
or diag $rv->{stdout} . $rv->{stderr};
is_deeply( [ _lines( $rv->{stderr} ) ],
[ 'No karr board found:', ' karr init' ],
"$name: the sentence, then the command, and nothing else" )
or diag $rv->{stderr};
}
};
subtest 'repair dry run ends on the command that applies it' => sub {
my $repo = _board_repo('a card');
# A bare-date `started` that precedes the card's own `created` is what the
# clamp pass reports; without it the dry run has nothing to offer. `created`
# is set at construction, so the card is rebuilt rather than edited.
App::karr::Git->new( dir => $repo )->save_task_ref(
do {
my $t = App::karr::Task->new(
id => 1,
title => 'a card',
status => 'done',
created => '2026-07-02T10:00:00Z',
updated => '2026-07-09T18:00:00Z',
);
$t->started('2026-07-02');
$t;
} );
my $rv = _run_karr( $repo, 'repair' );
is( $rv->{exit}, 0, 'a dry run still exits 0' ) or diag $rv->{stderr};
_hint_is_last( $rv->{stdout}, ' karr repair --yes',
'the offer is a command, and it is the last thing printed' );
};
#### Part B: where the answer stands
# The ticket's second fault, and the one that is only about placement. The
# answer was always in the output -- it just sat above a fifteen-line usage
# block, and the agent that hit it was reading through `tail -3`:
#
# $ karr move 1 in-progress --claim
# Option claim requires an argument <- the answer
# USAGE: karr move ID[,ID,...] STATUS [--claim NAME] [--next|--prev]
# --claim=String Claim task for an agent <- the second answer
# ... thirteen more lines ...
# --man show the manual <- all `tail -3` saw
#
# App::karr::Role::ExitCodes buffers STDERR for the length of new_with_options
# and puts the diagnostic back AFTER the block, with the invocation that would
( run in 0.243 second using v1.01-cache-2.11-cpan-4ef0a570458 )