App-karr

 view release on metacpan or  search on metacpan

t/26-skill-share-dir.t  view on Meta::CPAN

};

subtest 'the same fallback when the share dir is there but the file is not' => sub {
    my $empty    = share_dir_holding(undef);
    my $checkout = checkout_holding($DEV_SKILL);

    require File::ShareDir;
    no warnings 'redefine';
    # The other way the first lookup comes up empty: dist_dir answers, but
    # nothing is in it. It must fall through rather than return nothing.
    local *File::ShareDir::dist_dir = sub { return "$empty" };
    local $INC{$ANCHOR} = $checkout->child( 'lib', $ANCHOR )->stringify;

    is( App::karr::Cmd::Init->new->_skill_content, $DEV_SKILL,
        'init falls through to the checkout' );
    is( App::karr::Cmd::Skill->new->_skill_content, $DEV_SKILL,
        'and so does the skill command' );
};

subtest 'neither: it says so instead of returning nothing' => sub {
    my $bare = checkout_holding(undef);

    require File::ShareDir;
    no warnings 'redefine';
    local *File::ShareDir::dist_dir = sub { die "Failed to find share dir for dist 'App-karr'\n" };
    local $INC{$ANCHOR} = $bare->child( 'lib', $ANCHOR )->stringify;

    for my $class (qw( App::karr::Cmd::Init App::karr::Cmd::Skill )) {
        my $content = eval { $class->new->_skill_content };
        is( $content, undef, "$class returns nothing usable" );
        like( $@, qr/Could not find claude-skill\.md/,
            "$class names the file it could not find" );
        unlike( $@, qr/ at \S+ line \d+/, "$class adds no source location" );
    }
};

# In-process, the fallback above runs against a %INC entry this file set. That
# proves the arithmetic; it does not prove the CLI ever reaches it. Below, both
# commands are run as `karr` really runs, against a File::ShareDir that cannot
# answer -- the situation of anyone working from a checkout without the dist
# installed -- and have to come back with this checkout's share file.
my $SHARE = path($ROOT)->child('share/claude-skill.md');

# A File::ShareDir that fails the way an uninstalled dist makes it fail, ahead
# of the real one in the child's @INC. Cheaper and far more reliable than
# arranging for App-karr not to be installed on the machine running the tests:
# here it usually is, which is exactly why this path needs forcing to be seen
# at all.
sub stub_sharedir_lib {
    my $lib = path( tempdir( CLEANUP => 1 ) );
    $lib->child('File')->mkpath;
    $lib->child('File/ShareDir.pm')->spew_utf8( <<'PERL' );
package File::ShareDir;
sub dist_dir { die "Failed to find share dir for dist 'App-karr'\n" }
1;
PERL
    return $lib;
}

# Raw bytes on both sides: the child writes UTF-8 to its stdout, the file holds
# UTF-8, and comparing them undecoded keeps this check about which file was
# found rather than about encoding (t/65 owns that).
sub run_karr {
    my ( $cwd, $lib, @args ) = @_;
    my $old_cwd = getcwd();
    chdir $cwd or die "chdir $cwd: $!";
    my $err_fh = gensym;
    my $pid = open3( my $in, my $out_fh, $err_fh,
        $^X, "-I$lib", "-I$ROOT/lib", $BIN, @args );
    close $in;
    binmode $out_fh;
    binmode $err_fh;
    my $out = do { local $/; <$out_fh> };
    my $err = do { local $/; <$err_fh> };
    waitpid( $pid, 0 );
    my $exit = $? >> 8;
    chdir $old_cwd or die "chdir $old_cwd: $!";
    return { stdout => $out, stderr => $err, exit => $exit };
}

subtest 'karr skill show through the real CLI, with nothing installed' => sub {
    plan skip_all => "$SHARE not found - not a source checkout"
        unless $SHARE->exists;

    # Run from somewhere else entirely: the fallback has to find the tree the
    # code was loaded from, not the directory the user happens to stand in.
    my $elsewhere = tempdir( CLEANUP => 1 );
    my $r = run_karr( $elsewhere, stub_sharedir_lib(), 'skill', 'show' );

    is( $r->{exit}, 0, 'it exits 0' ) or diag "stderr: $r->{stderr}";
    is( $r->{stdout}, $SHARE->slurp_raw,
        'and prints this checkout\'s share/claude-skill.md, byte for byte' );
};

subtest 'karr init --claude-skill through the real CLI, with nothing installed' => sub {
    plan skip_all => "$SHARE not found - not a source checkout"
        unless $SHARE->exists;

    my $repo = tempdir( CLEANUP => 1 );
    system( 'git', 'init', '-q', $repo ) == 0
        or plan skip_all => 'git init failed';
    system( 'git', '-C', $repo, 'config', 'user.email', 'test@example.com' );
    system( 'git', '-C', $repo, 'config', 'user.name',  'Test User' );

    my $r = run_karr( $repo, stub_sharedir_lib(), 'init', '--claude-skill' );

    is( $r->{exit}, 0, 'it exits 0' ) or diag "stderr: $r->{stderr}";
    like( $r->{stdout}, qr/Installed Claude Code skill/, 'and reports the install' );

    my $installed = path($repo)->child('.claude/skills/kanban-issues-karr-cli/SKILL.md');
    ok( $installed->exists, 'the skill is there' ) or return;
    is( $installed->slurp_raw, $SHARE->slurp_raw,
        'and it is this checkout\'s share/claude-skill.md, byte for byte' );
};

done_testing;



( run in 0.772 second using v1.01-cache-2.11-cpan-364913b4093 )