use assert_cmd::prelude::*;
use predicates::prelude::*;
use std::fs;
use std::process::Command;
use tempfile::TempDir;
fn tldr_cmd() -> Command {
Command::new(assert_cmd::cargo::cargo_bin!("tldr"))
}
#[test]
fn test_help_text() {
let mut cmd = tldr_cmd();
cmd.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains(
"TLDR provides code analysis commands",
))
.stdout(predicate::str::contains("tree"))
.stdout(predicate::str::contains("structure"))
.stdout(predicate::str::contains("calls"))
.stdout(predicate::str::contains("impact"))
.stdout(predicate::str::contains("dead"))
.stdout(predicate::str::contains("cfg"))
.stdout(predicate::str::contains("dfg"))
.stdout(predicate::str::contains("slice"))
.stdout(predicate::str::contains("search"))
.stdout(predicate::str::contains("context"))
.stdout(predicate::str::contains("smells"))
.stdout(predicate::str::contains("secrets"));
}
#[test]
fn test_version() {
let mut cmd = tldr_cmd();
cmd.arg("--version")
.assert()
.success()
.stdout(predicate::str::contains("tldr"));
}
#[test]
fn test_tree_json_output() {
let temp = TempDir::new().unwrap();
fs::create_dir(temp.path().join("subdir")).unwrap();
fs::write(temp.path().join("file1.py"), "# test").unwrap();
fs::write(temp.path().join("subdir/file2.py"), "# test2").unwrap();
let mut cmd = tldr_cmd();
cmd.args(["tree", temp.path().to_str().unwrap(), "-q"])
.assert()
.success()
.stdout(predicate::str::contains("\"type\""))
.stdout(predicate::str::contains("\"children\""));
}
#[test]
fn test_tree_with_extension_filter() {
let temp = TempDir::new().unwrap();
fs::write(temp.path().join("file.py"), "# python").unwrap();
fs::write(temp.path().join("file.rs"), "// rust").unwrap();
let mut cmd = tldr_cmd();
cmd.args(["tree", temp.path().to_str().unwrap(), "--ext", ".py", "-q"])
.assert()
.success()
.stdout(predicate::str::contains("file.py"))
.stdout(predicate::str::contains("file.rs").not());
}
#[test]
fn test_tree_nonexistent_path() {
let mut cmd = tldr_cmd();
cmd.args(["tree", "/nonexistent/path/that/does/not/exist", "-q"])
.assert()
.failure();
}
#[test]
fn test_structure_json_output() {
let temp = TempDir::new().unwrap();
fs::write(
temp.path().join("test.py"),
r#"
def foo():
pass
class Bar:
def method(self):
pass
"#,
)
.unwrap();
let mut cmd = tldr_cmd();
cmd.args([
"structure",
temp.path().to_str().unwrap(),
"-l",
"python",
"-q",
])
.assert()
.success()
.stdout(predicate::str::contains("\"functions\""))
.stdout(predicate::str::contains("\"classes\""));
}
#[test]
fn test_search_finds_pattern() {
let temp = TempDir::new().unwrap();
fs::write(
temp.path().join("test.py"),
r#"
def find_me():
pass
def another():
pass
"#,
)
.unwrap();
let mut cmd = tldr_cmd();
cmd.args(["search", "find_me", temp.path().to_str().unwrap(), "-q"])
.assert()
.success()
.stdout(predicate::str::contains("find_me"));
}
#[test]
fn test_search_invalid_regex() {
let temp = TempDir::new().unwrap();
fs::write(temp.path().join("test.py"), "content").unwrap();
let mut cmd = tldr_cmd();
cmd.args(["search", "[invalid", temp.path().to_str().unwrap(), "-q"])
.assert()
.failure();
}
#[test]
fn test_json_format() {
let temp = TempDir::new().unwrap();
fs::write(temp.path().join("test.py"), "# test").unwrap();
let mut cmd = tldr_cmd();
let output = cmd
.args(["tree", temp.path().to_str().unwrap(), "-f", "json", "-q"])
.output()
.unwrap();
let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert!(json.is_object());
}
#[test]
fn test_compact_format() {
let temp = TempDir::new().unwrap();
fs::write(temp.path().join("test.py"), "# test").unwrap();
let mut cmd = tldr_cmd();
let output = cmd
.args(["tree", temp.path().to_str().unwrap(), "-f", "compact", "-q"])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
let lines: Vec<&str> = stdout.lines().collect();
assert_eq!(lines.len(), 1, "Compact JSON should be single line");
}
#[test]
fn test_text_format() {
let temp = TempDir::new().unwrap();
fs::write(temp.path().join("test.py"), "# test").unwrap();
let mut cmd = tldr_cmd();
cmd.args(["tree", temp.path().to_str().unwrap(), "-f", "text", "-q"])
.assert()
.success();
}
#[test]
fn test_tree_alias() {
let mut cmd = tldr_cmd();
cmd.args(["t", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("file tree"));
}
#[test]
fn test_structure_alias() {
let mut cmd = tldr_cmd();
cmd.args(["s", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("code structure"));
}
#[test]
fn test_calls_alias() {
let mut cmd = tldr_cmd();
cmd.args(["c", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("call graph"));
}
#[test]
fn test_cfg_command_help() {
let mut cmd = tldr_cmd();
cmd.args(["cfg", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("control flow graph"));
}
#[test]
fn test_dfg_command_help() {
let mut cmd = tldr_cmd();
cmd.args(["dfg", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("data flow graph"));
}
#[test]
fn test_slice_command_help() {
let mut cmd = tldr_cmd();
cmd.args(["slice", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("program slice"));
}
#[test]
fn test_smells_command() {
let temp = TempDir::new().unwrap();
fs::write(temp.path().join("test.py"), "def foo(): pass").unwrap();
let mut cmd = tldr_cmd();
cmd.args(["smells", temp.path().to_str().unwrap(), "-q"])
.assert()
.success()
.stdout(predicate::str::contains("smells"));
}
#[test]
fn test_secrets_command() {
let temp = TempDir::new().unwrap();
fs::write(temp.path().join("test.py"), "# no secrets here").unwrap();
let mut cmd = tldr_cmd();
cmd.args(["secrets", temp.path().to_str().unwrap(), "-q"])
.assert()
.success()
.stdout(predicate::str::contains("findings"));
}
#[test]
fn test_cold_start_performance() {
use std::time::Instant;
let temp = TempDir::new().unwrap();
fs::write(temp.path().join("test.py"), "# test").unwrap();
let start = Instant::now();
let mut cmd = tldr_cmd();
cmd.args(["tree", temp.path().to_str().unwrap(), "-q"])
.assert()
.success();
let elapsed = start.elapsed();
assert!(
elapsed.as_millis() < 1000,
"Cold start took {}ms, expected <1000ms",
elapsed.as_millis()
);
}