App-FuguBench

 view release on metacpan or  search on metacpan

t/fugubench/hook.t  view on Meta::CPAN

};

subtest 'a session in a worktree reads the worktree' => sub {
	my ( $tree, $origin ) = _tree();
	my $co = _checkout( $tree, 'Workspace',
		"wiki.origin\tfile://$origin\n" . "wiki.project\tWorkspace\n" );

	# A worktree holds its own .toolingrc and its own library
	# clone, so it is a checkout of its own. The walk must not cut
	# the cwd at the marker (CLI-CHECKOUT-4, HOOK-EVENTS-5).
	my $wt = "$co/.claude/worktrees/wt-1";
	_write( "$wt/.toolingrc",
		"wiki.origin\tfile://$origin\n" . "wiki.project\tTree\n" );
	_git( $tree, 'clone', '--quiet', "$tree/origin.git", "$wt/Wiki" );

	my $page = "Session-Tree-$today-1.md";
	my $r    = _hook( $tree, 'SessionStart',
		_json( session_id => 's1', cwd => $wt ) );
	is( $r->{stdout}, "$page\n", 'the page takes the project of the worktree' )
	    or diag $r->{stderr};
	ok( -e "$wt/Wiki/$page", 'the page lands in the library of the worktree' );
	is( scalar( glob "$co/Wiki/Session-*" ),
		undef, 'the library above the worktree holds no page' );

	# SessionEnd reads the same checkout, and it closes that page.
	$r = _hook( $tree, 'SessionEnd',
		_json( session_id => 's1', cwd => $wt ) );
	is( $r->{exit_code}, 0, 'SessionEnd exits zero' ) or diag $r->{stderr};
	like( Fugu::File->read("$wt/Wiki/$page"),
		qr/^Closed: /m, 'the page of the worktree holds the closed line' );
};

subtest 'WorktreeCreate writes the path, and a second run repeats it' => sub {
	my ($tree) = _tree();
	my ( $repo, $real ) = _repo( $tree, 'repo', q{} );
	my $wt = "$real/.claude/worktrees/wt-1";

	my $r = _hook( $tree, 'WorktreeCreate',
		_json( cwd => $repo, name => 'wt-1' ) );
	is( $r->{exit_code}, 0, 'the event exits zero' ) or diag $r->{stderr};
	is( $r->{stdout}, "$wt\n", 'the path is the only line of the output' );
	ok( -e "$wt/.git", 'the worktree exists' );

	# Claude Code runs the create hook again when a session
	# reconnects (HOOK-WORKTREE-3, WT-CREATE-7).
	$r = _hook( $tree, 'WorktreeCreate',
		_json( cwd => $repo, name => 'wt-1' ) );
	is( $r->{exit_code}, 0, 'a second create of one name exits zero' )
	    or diag $r->{stderr};
	is( $r->{stdout}, "$wt\n", 'the second create writes the path again' );

	# The harness needs the path, so this event reports a failure
	# (HOOK-EVENTS-3).
	$r = _hook( $tree, 'WorktreeCreate', _json( cwd => $repo ) );
	is( $r->{exit_code}, 1,   'a payload with no name exits 1' );
	is( $r->{stdout},    q{}, 'a payload with no name writes no path' );

	# The event returns the code of the subcommand, and the
	# subcommand refuses a name that leaves the base (WT-CREATE-2).
	$r = _hook( $tree, 'WorktreeCreate',
		_json( cwd => $repo, name => '../escape' ) );
	is( $r->{exit_code}, 1, 'a name that the subcommand refuses exits 1' );
	like( $r->{stderr}, qr/invalid worktree name/,
		'the subcommand names the reason' );

	is(
		_hook( $tree, 'WorktreeCreate', _json( name => 'wt-2' ) )
		    ->{exit_code},
		1,
		'a payload with no cwd exits 1'
	);

	my $outside = "$tree/outside";
	make_path($outside);
	is(
		_hook(
			$tree, 'WorktreeCreate',
			_json( cwd => $outside, name => 'wt-3' ) )->{exit_code},
		1,
		'a cwd with no checkout exits 1'
	);

	# A sub-agent payload changes nothing (HOOK-EVENTS-4).
	$r = _hook( $tree, 'WorktreeCreate',
		_json( cwd => $repo, name => 'wt-4', agent_id => 'a1' ) );
	is( $r->{exit_code}, 0, 'a sub-agent payload exits zero' );
	ok( !-e "$real/.claude/worktrees/wt-4",
		'a sub-agent makes no worktree' );
};

subtest 'WorktreeRemove keeps the worktree and prints the command' => sub {
	my ($tree) = _tree();
	my ($repo) = _repo( $tree, 'repo', q{} );
	my $wt = "$repo/.claude/worktrees/team/wt-1";
	make_path($wt);

	my $r = _hook( $tree, 'WorktreeRemove',
		_json( cwd => $repo, worktree_path => $wt ) );
	is( $r->{exit_code}, 0,   'the event exits zero' );
	is( $r->{stdout},    q{}, 'the event writes no result line' );
	ok( -d $wt, 'the event removes nothing' );
	like( $r->{stderr}, qr/\Qworktree kept: $wt\E/,
		'the hint names the path' );
	like(
		$r->{stderr},
		qr{\Qto remove it: make -C $repo worktree-remove NAME=team/wt-1\E},
		'the hint names the root and the name'
	);

	# The split takes the worktree.base value of the checkout.
	my ($other) = _repo( $tree, 'other', "worktree.base\ttrees\n" );
	$r = _hook( $tree, 'WorktreeRemove',
		_json( cwd => $other, worktree_path => "$other/trees/wt-2" ) );
	like( $r->{stderr}, qr{\Qmake -C $other worktree-remove NAME=wt-2\E},
		'the split takes the configured base' );

	# Without a checkout the verb prints the path alone.
	$r = _hook( $tree, 'WorktreeRemove', _json( worktree_path => $wt ) );
	is( $r->{exit_code}, 0, 'a payload with no cwd exits zero' );
	like( $r->{stderr}, qr/\Qworktree kept: $wt\E/,
		'the hint names the path' );



( run in 3.605 seconds using v1.01-cache-2.11-cpan-54e63673c56 )