App-FuguBench
view release on metacpan or search on metacpan
t/fugubench/worktree-verb.t view on Meta::CPAN
'the clone carries the origin URL of the source (WT-CLONE-2)'
);
ok( -f "$dest/Projects/one/deep/.env",
'the .env file at depth arrives (WT-CLONE-3)' );
is(
( stat "$dest/Projects/one/deep/.env" )[2] & 07777,
0600,
'the copy keeps the mode of the source (WT-CLONE-3)'
);
ok( !-e "$dest/Projects/two/.env",
'clone skips a .env symbolic link (WT-CLONE-3)' );
};
subtest 'clone keeps what exists and refuses a bad path' => sub {
my ( $dir, $real ) = _repo();
my $dest = tempdir( CLEANUP => 1 );
_source( "$real/Wiki", 'https://example.com/wiki.git' );
_write( "$real/.env", "KEY=value\n" );
# A destination that exists stays as it is, so a second run
# repairs a bootstrap that stopped early (WT-CLONE-4).
make_path("$dest/Wiki");
_write( "$dest/Wiki/local.txt", "mine\n" );
# A project can leave a symbolic link at the destination of a
# file copy. The copy replaces the link, and it writes no byte
# through it (WT-CLONE-4).
my $away = tempdir( CLEANUP => 1 );
_write( "$away/target.txt", "outside\n" );
symlink "$away/target.txt", "$dest/.env" or die "symlink: $!";
my $result = _clone( $dir, $dest, 'Wiki', '.env', 'absent' );
is( $result->{exit_code}, 0, 'clone exits 0' )
or diag $result->{stderr};
ok( !-e "$dest/Wiki/.git",
'clone keeps the destination that exists (WT-CLONE-4)' );
is( Fugu::File->read("$dest/Wiki/local.txt"),
"mine\n", 'the local change stays (WT-CLONE-4)' );
like(
$result->{stderr},
qr{already exists, skipped: Wiki},
'the message names the destination that clone skips'
);
ok( !-l "$dest/.env", 'clone replaces the destination link' );
is( Fugu::File->read("$dest/.env"),
"KEY=value\n", 'the copy holds the source (WT-CLONE-4)' );
is( Fugu::File->read("$away/target.txt"),
"outside\n", 'the file outside the tree keeps its content' );
like(
$result->{stderr},
qr{missing in main checkout, skipped: absent},
'clone skips an absent path with a message (WT-CLONE-5)'
);
# A path with a parent segment leaves the current directory,
# so the shape check stops it before any work (WT-CLONE-5).
$result = _clone( $dir, $dest, '../escape' );
is( $result->{exit_code}, 1,
'clone refuses a path with a parent segment (WT-CLONE-5)' );
like( $result->{stderr}, qr/invalid path/,
'the message names the cause' );
# In the main checkout itself, clone changes nothing
# (WT-CLONE-6).
$result = _clone( $dir, $dir, 'Wiki' );
is( $result->{exit_code}, 0, 'clone exits 0 in the main checkout' )
or diag $result->{stderr};
like(
$result->{stderr},
qr/already the main checkout, nothing to do/,
'the message names the cause (WT-CLONE-6)'
);
ok( -d "$real/Wiki/.git",
'the main checkout keeps its repository (WT-CLONE-6)' );
};
subtest 'the base of the worktrees resolves against the root' => sub {
# A clone under Projects/ inherits worktree.base from the
# workspace above it, and its worktrees belong to the clone.
# So the value resolves against the root, and not against the
# home of the key (CLI-CONFIG-2).
my $home = tempdir( CLEANUP => 1 );
_write( "$home/.toolingrc", "worktree.base trees\n" );
my ( $dir, $real ) = _repo( undef, "$home/Projects" );
my $result = _run( $dir, 'create', 'one' );
is( $result->{exit_code}, 0, 'create exits 0 with a configured base' )
or diag $result->{stderr};
is( $result->{stdout}, "$real/trees/one\n",
'the worktree sits under the configured base (CLI-CONFIG-2)' );
$result = _run( $dir, 'list' );
like(
$result->{stdout},
qr/^one\s+\d+ d\s+clean$/m,
'list reads the configured base'
);
};
subtest 'the nested worktree directory follows the configured base' => sub {
# The skip of WT-REMOVE-3 and WT-CLONE-3 names the
# worktree.base directory, and a checkout can configure
# another one.
my $home = tempdir( CLEANUP => 1 );
_write( "$home/.toolingrc", "worktree.base trees\n" );
my ( $dir, $real ) = _repo( undef, "$home/Projects" );
my $result = _run( $dir, 'create', 'outer' );
is( $result->{exit_code}, 0, 'create makes the worktree' )
or diag $result->{stderr};
# A worktree of the worktree sits under the configured base.
# The risk walk must skip it, so no state of it stops the
# removal of the outer worktree.
my $inner = "$real/trees/outer/trees/inner";
t/fugubench/worktree-verb.t view on Meta::CPAN
my $dest = tempdir( CLEANUP => 1 );
_source( "$real/Projects/one", 'https://example.com/one.git' );
make_path("$real/Projects/one/trees/inner");
make_path("$real/Projects/one/deep");
_write( "$real/Projects/one/trees/inner/.env", "KEY=nested\n" );
_write( "$real/Projects/one/deep/.env", "KEY=value\n" );
$result = _clone( $dir, $dest, 'Projects' );
is( $result->{exit_code}, 0, 'clone exits 0' )
or diag $result->{stderr};
ok( -f "$dest/Projects/one/deep/.env",
'the .env file outside the base arrives (WT-CLONE-3)' );
ok( !-e "$dest/Projects/one/trees/inner/.env",
'the .env walk skips the configured base (WT-CLONE-3)' );
};
subtest 'a base of the root stops each subcommand' => sub {
# A worktree.base of . resolves to the root. The base then
# holds every path of the checkout, and the containment guard
# of remove admits each one. So the verb stops with the
# configuration error (CLI-CONFIG-3).
my ($dir) = _repo();
make_path("$dir/lib");
_write( "$dir/lib/Real.pm", "1;\n" );
_write( "$dir/.toolingrc", "worktree.base .\n" );
_git( '-C', $dir, 'add', '-A' );
_git( '-C', $dir, 'commit', '--quiet', '-m', 'Add a tracked tree' );
for my $case ( [ 'create', 'one' ], [ 'remove', 'one' ], ['list'] ) {
my $result = _run( $dir, @{$case} );
is( $result->{exit_code}, 3,
"$case->[0] stops with the configuration error" );
like( $result->{stderr}, qr/worktree[.]base/,
'the message names the key' );
}
my $dest = tempdir( CLEANUP => 1 );
my $result = _clone( $dir, $dest, 'lib' );
is( $result->{exit_code}, 3,
'clone stops with the configuration error' );
ok( !-e "$dest/lib", 'clone copies nothing' );
# The remove of a tracked directory of the checkout. No other
# guard holds here: the risk walk finds no repository in a
# plain directory, and _take then deletes the tree
# (WT-SAFETY-1).
$result = _run( $dir, 'remove', 'lib' );
is( $result->{exit_code}, 3, 'remove refuses a tracked directory' );
ok( -f "$dir/lib/Real.pm", 'the tracked tree stays' );
is( _git( '-C', $dir, 'status', '--porcelain' ),
q{}, 'the checkout holds no change' );
};
subtest 'a base of the wrong shape stops each subcommand' => sub {
# The value leaves the tree, so the shape check of
# CLI-CONFIG-3 refuses it. Every subcommand reads the base,
# so each one stops with the configuration error.
my ($dir) = _repo();
_write( "$dir/.toolingrc", "worktree.base ../escape\n" );
for my $case ( [ 'create', 'one' ], [ 'remove', 'one' ], ['list'] ) {
my $result = _run( $dir, @{$case} );
is( $result->{exit_code}, 3,
"$case->[0] stops with the configuration error" );
like( $result->{stderr}, qr/worktree[.]base: .*leaves the tree/,
'the message names the key and the cause' );
}
my $dest = tempdir( CLEANUP => 1 );
my $result = _clone( $dir, $dest, 'Projects' );
is( $result->{exit_code}, 3,
'clone stops with the configuration error' );
like( $result->{stderr}, qr/worktree[.]base: .*leaves the tree/,
'the message names the key and the cause' );
};
subtest 'a linked worktree is no main checkout' => sub {
my ( $dir, $real ) = _repo();
my $result = _run( $dir, 'create', 'inner' );
is( $result->{exit_code}, 0, 'create makes the worktree' )
or diag $result->{stderr};
# The worktree holds the .toolingrc of the checkout, so the
# walk stops in it. Its .git is a file, not a directory.
$result = _run( "$real/.claude/worktrees/inner", 'list' );
is( $result->{exit_code}, 1, '-C on a linked worktree exits 1' );
is( $result->{stdout}, q{}, 'the refusal writes no line' );
};
done_testing();
( run in 1.146 second using v1.01-cache-2.11-cpan-54e63673c56 )