Files
rtk-ai__rtk/tests/native_test_expression_test.rs
Nicolas Le Cam 80e45683be test(test): cover Commands::Test routing end to end
The unit tests exercise `is_native_test_expression` directly, so inverting or
dropping the branch that calls it left the suite green while `rtk test -d dir`
went back to `sh: 0: Illegal option -d`.

Run the binary instead: native expressions carry the system `test` exit code,
`!` is neither answered backwards nor allowed to swallow a command, argument
boundaries survive, and a command still reaches the test runner.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 03:23:44 +02:00

92 lines
3.3 KiB
Rust

#![cfg(unix)]
//! End-to-end coverage for `Commands::Test` routing.
//!
//! `rtk test` doubles as a wrapper around a test runner and as the POSIX `test`
//! utility. The unit tests in `main.rs` cover `is_native_test_expression` on its
//! own; these run the binary so an inverted or removed branch in the `Commands::Test`
//! arm fails here instead of shipping.
use std::path::Path;
use std::process::Command;
use tempfile::TempDir;
fn exit_code(cwd: &Path, args: &[&str]) -> i32 {
Command::new(env!("CARGO_BIN_EXE_rtk"))
.env("LC_ALL", "C")
.env("HOME", cwd.join("home"))
.args(args)
.current_dir(cwd)
.output()
.expect("spawn rtk")
.status
.code()
.expect("rtk exited via signal")
}
fn fixture() -> TempDir {
let dir = TempDir::new().expect("create tempdir");
std::fs::create_dir(dir.path().join("home")).expect("create home");
std::fs::create_dir(dir.path().join("adir")).expect("create adir");
std::fs::write(dir.path().join("afile"), b"").expect("create afile");
dir
}
/// A native expression must reach the system `test`, whose exit code is the answer.
/// Routing it through the shell wrapper instead yields `sh: 0: Illegal option -d`
/// and exit 2 for every one of these.
#[test]
fn native_expressions_carry_the_test_exit_code() {
let dir = fixture();
let cwd = dir.path();
assert_eq!(exit_code(cwd, &["test", "-d", "adir"]), 0);
assert_eq!(exit_code(cwd, &["test", "-d", "nodir"]), 1);
assert_eq!(exit_code(cwd, &["test", "-f", "afile"]), 0);
assert_eq!(exit_code(cwd, &["test", "-f", "nofile"]), 1);
assert_eq!(exit_code(cwd, &["test", "-e", "adir"]), 0);
}
/// `!` negates, so these are the cases the shell wrapper answered backwards
/// rather than loudly: it reported "not a directory" as true for a directory
/// that exists, because `-d` was a missing command and `!` inverted its 127.
#[test]
fn negated_expressions_are_not_answered_backwards() {
let dir = fixture();
let cwd = dir.path();
assert_eq!(exit_code(cwd, &["test", "!", "-d", "adir"]), 1);
assert_eq!(exit_code(cwd, &["test", "!", "-d", "nodir"]), 0);
}
/// `!` and `(` are shell syntax as well as `test` syntax. They mark a native
/// expression only when what they apply to is one, so a command behind `!` still
/// runs under the shell and keeps the exit code negation it had before.
#[test]
fn bang_before_a_command_still_runs_the_command() {
let dir = fixture();
let cwd = dir.path();
assert_eq!(exit_code(cwd, &["test", "!", "false"]), 0);
assert_eq!(exit_code(cwd, &["test", "!", "true"]), 1);
}
/// Argument boundaries survive: the passthrough passes argv through, where the
/// shell wrapper re-split a joined string and saw three arguments here.
#[test]
fn native_expressions_keep_argument_boundaries() {
let dir = fixture();
std::fs::write(dir.path().join("a file"), b"").expect("create spaced file");
assert_eq!(exit_code(dir.path(), &["test", "-f", "a file"]), 0);
}
/// The other direction: a command to run under the test filter still goes to the
/// filter, not to the system `test`, which would reject it as too many arguments.
#[test]
fn commands_still_reach_the_test_runner() {
let dir = fixture();
assert_eq!(exit_code(dir.path(), &["test", "echo", "hello"]), 0);
}