App-karr

 view release on metacpan or  search on metacpan

t/151-foundation-shell-expansion.t  view on Meta::CPAN

  my $tick = $repo->child('RAN-BACKTICK');
  my $dollar = $repo->child('RAN-DOLLAR-PAREN');

  my $prompt = "Run `touch $tick` and \$(touch $dollar), keep it under \$500";

  my ( $code, $out ) = $f->_run_command(
    $repo,
    { prompt => $prompt, max_runtime => 60 },
    'printf "%s" "$PROMPT"',
  );

  is $code, 0, 'command ran';
  ok !$tick->exists,   'the backtick span was not executed';
  ok !$dollar->exists, 'the $(...) was not executed';
  is $out, $prompt,
    'the agent receives the prompt verbatim, $500 and all';
};

subtest q{single quotes protect $2: awk '{print $2}' reaches awk} => sub {
  my $repo = tempdir( CLEANUP => 1 );

  # POSIX awk; if this ever fails to *run*, check that awk exists before
  # reading it as a regression.
  my ( $code, $out ) = $f->_run_command(
    $repo,
    { max_runtime => 60 },
    q{echo "alpha beta" | awk '{print $2}'},
  );

  is $code, 0, 'command ran';
  is $out, "beta\n",
    'the single-quoted awk program is passed through untouched';
};

subtest 'the variables a template may reference all still expand' => sub {
  my $repo = tempdir( CLEANUP => 1 );
  local $ENV{KARR_TEST_INHERITED} = 'from-parent';

  my ( $code, $out ) = $f->_run_command(
    $repo,
    { prompt => 'PROMPT-VALUE', max_runtime => 60 },
    'printf "%s|%s|%s|%s|[%s]"'
      . ' "$PROMPT" "${KARR_REPO}" "$KARR_ROLE"'
      . ' "$KARR_TEST_INHERITED" "$KARR_TEST_NEVER_SET"',
  );

  is $code, 0, 'command ran';
  is $out, "PROMPT-VALUE|$repo|agent|from-parent|[]",
    'PROMPT, ${VAR} braced form, KARR_ROLE, an inherited var; unset is empty';
};

subtest 'the synthesized claude command hands the prompt over as one argument'
  => sub {
  my $bin_dir = tempdir( CLEANUP => 1 );
  my $bin     = $bin_dir->child('fake-claude');
  $bin->spew_utf8( <<'SH' );
#!/bin/sh
printf 'argc=%s\n' "$#"
for a in "$@"; do printf 'arg=[%s]\n' "$a"; done
SH
  chmod 0755, "$bin" or die "chmod $bin: $!";

  my $repo = tempdir( CLEANUP => 1 );
  $repo->child('globbed.txt')->spew_utf8('x');   # something for * to catch
  my $ran = $repo->child('RAN');

  # Everything a Markdown prompt throws at a shell: a glob, a backtick span,
  # word-splitting whitespace, a metacharacter and an apostrophe.
  my $prompt = "Pick * next; run `touch $ran`, mind the spaces, don't quote";

  my $karr = {
    claude      => 1,
    claude_bin  => "$bin",
    prompt      => $prompt,
    max_runtime => 60,
  };

  my $cmd = $f->_agent_command( $repo, $karr );
  like $cmd, qr/-p "\$PROMPT"/,
    'the synthesized template quotes $PROMPT for the shell';

  my ( $code, $out ) = $f->_run_command( $repo, $karr, $cmd );

  is $code, 0, 'command ran';
  like $out, qr/^argc=6$/m,
    'six arguments: the prompt was neither word-split nor globbed';
  like $out, qr/\Qarg=[$prompt]\E/,
    'the prompt arrives as exactly one argument, verbatim';
  ok !$ran->exists, 'and its backtick span did not run';
};

subtest 'the START log line records the template handed to /bin/sh' => sub {
  my $repo = tempdir( CLEANUP => 1 );
  my $cmd  = 'printf "%s" "$PROMPT" >/dev/null';

  $f->_run_command( $repo, { prompt => 'PROMPT-VALUE', max_runtime => 60 }, $cmd );

  my ($start) = grep { /START command=/ }
    split /\n/, $repo->child('.karr.log')->slurp_utf8;

  like $start, qr/\QSTART command=$cmd\E/,
    'the template is logged verbatim';
  unlike $start, qr/PROMPT-VALUE/,
    'not the substituted result: env values stay out of .karr.log';
};

done_testing;



( run in 1.082 second using v1.01-cache-2.11-cpan-6736b670a1e )