claude_runner 1.4.1

CLI for executing Claude Code via builder pattern; YAML schema constants for command registration
Documentation
#![ cfg( feature = "enabled" ) ]
//! Integration tests for `clr refresh` subcommand.
//!
//! # Test Matrix
//!
//! | ID | Test | Requires Live Claude |
//! |----|------|----------------------|
//! | IT-2 | `--creds missing.json` → exit 1 | No |
//! | IT-4 | `--timeout 0` → unlimited (no watchdog), exit 0 | No (Unix) |
//! | IT-6 | `--creds <f> --timeout abc` → exit 1, invalid timeout | No |
//! | IT-8 | `clr refresh --help` → exit 0, help text shown | No |
//!
//! Tests containing `lim_it` (not present in this file) would require a live
//! OAuth-capable `claude` binary.  All tests here run without live credentials.
//!
//! Source: `tests/docs/cli/command/04_refresh.md`

mod cli_binary_test_helpers;
use cli_binary_test_helpers::{ exit_code, make_creds_file, stderr_str, stdout_str };

// ── helpers ──────────────────────────────────────────────────────────────────

/// Invoke `clr refresh <args>` and return raw output.
///
/// Delegates to the shared `cli_binary_test_helpers::run_cli` binary helper, prepending
/// the `"refresh"` subcommand to the caller-supplied arguments.
fn run_refresh( args : &[ &str ] ) -> std::process::Output
{
  let mut full = vec![ "refresh" ];
  full.extend_from_slice( args );
  cli_binary_test_helpers::run_cli( &full )
}

// ── offline tests (no live claude required) ───────────────────────────────────

/// IT-2: creds file that does not exist → exit 1 with file-not-found message.
///
/// Source: tests/docs/cli/command/04_refresh.md#it-2
#[ test ]
fn test_it2_creds_file_not_found()
{
  let out = run_refresh( &[ "--creds", "/tmp/clr_refresh_nonexistent_it2.json" ] );
  assert_eq!( exit_code( &out ), 1, "expected exit 1; stderr: {}", stderr_str( &out ) );
  let err = stderr_str( &out );
  assert!(
    err.contains( "not found" ) || err.contains( "No such file" ) || err.contains( "cannot read" ),
    "expected file-not-found message; got: {err}"
  );
}

/// IT-4: `--timeout 0` → unlimited (no watchdog); subprocess runs to natural exit.
///
/// Creates a fake `claude` shell script that sleeps briefly.  With `--timeout 0`,
/// no watchdog is spawned — the subprocess runs until it exits naturally and
/// `clr refresh` forwards the subprocess exit code (0).
///
/// Source: tests/docs/cli/command/04_refresh.md#it-4
#[ cfg( unix ) ]
#[ test ]
fn test_it4_timeout_zero_unlimited()
{
  use std::os::unix::fs::PermissionsExt;

  let dir = tempfile::tempdir().expect( "tmpdir" );
  let script = dir.path().join( "claude" );
  // timeout-0 = unlimited: subprocess runs to completion — use short sleep to keep test fast.
  std::fs::write( &script, "#!/bin/sh\nsleep 1\n" ).expect( "write fake claude" );
  std::fs::set_permissions( &script, std::fs::Permissions::from_mode( 0o755 ) )
    .expect( "chmod fake claude" );
  let path_val = format!(
    "{}:{}",
    dir.path().display(),
    std::env::var( "PATH" ).unwrap_or_default(),
  );

  let creds = make_creds_file( "{}" );
  let creds_path = creds.path().to_str().expect( "temp path is valid UTF-8" );

  let bin = env!( "CARGO_BIN_EXE_clr" );
  let out = std::process::Command::new( bin )
    .args( [ "refresh", "--creds", creds_path, "--timeout", "0" ] )
    .env( "PATH", &path_val )
    .output()
    .expect( "invoke clr refresh" );

  assert_eq!(
    exit_code( &out ),
    0,
    "IT-4: --timeout 0 must let subprocess run to completion (unlimited); stderr: {}",
    stderr_str( &out ),
  );
}

/// IT-6: `--timeout abc` → exit 1, invalid `--timeout` error.
///
/// Source: tests/docs/cli/command/04_refresh.md#it-6
#[ test ]
fn test_it6_invalid_timeout()
{
  let creds = make_creds_file( "{}" );
  let path  = creds.path().to_str().expect( "temp path is valid UTF-8" );
  let out   = run_refresh( &[ "--creds", path, "--timeout", "abc" ] );
  assert_eq!( exit_code( &out ), 1, "expected exit 1; stderr: {}", stderr_str( &out ) );
  assert!(
    stderr_str( &out ).contains( "invalid --timeout" ),
    "expected 'invalid --timeout' message; got: {}", stderr_str( &out )
  );
}

/// IT-8: `clr refresh --help` exits 0 and prints refresh-specific help text.
///
/// Source: tests/docs/cli/command/04_refresh.md#it-8
#[ test ]
fn test_it8_help_exits_zero()
{
  let bin = env!( "CARGO_BIN_EXE_clr" );
  let out = std::process::Command::new( bin )
    .args( [ "refresh", "--help" ] )
    .output()
    .expect( "failed to invoke clr refresh --help" );
  assert_eq!(
    exit_code( &out ),
    0,
    "clr refresh --help must exit 0; got: {:?}\nstderr: {}",
    out.status.code(),
    stderr_str( &out ),
  );
  let stdout = stdout_str( &out );
  assert!(
    stdout.contains( "--creds" ),
    "help text must mention --creds; got:\n{stdout}",
  );
  assert!(
    stdout.contains( "--timeout" ),
    "help text must mention --timeout; got:\n{stdout}",
  );
  assert!(
    stdout.contains( "--trace" ),
    "help text must mention --trace; got:\n{stdout}",
  );
  assert!(
    stdout.contains( "--help" ),
    "help text must mention --help; got:\n{stdout}",
  );
}