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"))
}
fn fixture() -> TempDir {
let dir = TempDir::new().expect("create tempdir");
fs::write(
dir.path().join("a.py"),
"def foo():\n return 1\n\ndef bar():\n return foo()\n",
)
.unwrap();
fs::write(
dir.path().join("b.py"),
"from a import foo\n\ndef baz():\n return foo()\n",
)
.unwrap();
dir
}
fn unsupported_sarif_cases() -> Vec<&'static [&'static str]> {
vec![
&["smells"][..],
&["dead"][..],
&["health"][..],
&["api-check"][..],
&["secure"][..],
&["debt"][..],
&["structure"][..],
&["tree"][..],
&["taint", "a.py", "foo"][..],
&["reaching-defs", "a.py", "foo"][..],
]
}
#[test]
fn sarif_errors_on_unsupported_commands() {
let dir = fixture();
for case in unsupported_sarif_cases() {
let mut cmd = tldr_cmd();
cmd.current_dir(dir.path());
cmd.args(case);
cmd.args(["--format", "sarif"]);
let needs_path = !matches!(case[0], "taint" | "reaching-defs");
if needs_path {
cmd.arg(".");
}
cmd.assert()
.failure()
.stderr(predicate::str::contains("not supported"))
.stderr(predicate::str::contains("sarif"));
}
}
#[test]
fn dot_errors_on_smells() {
let dir = fixture();
let mut cmd = tldr_cmd();
cmd.current_dir(dir.path())
.args(["smells", ".", "--format", "dot"]);
cmd.assert()
.failure()
.stderr(predicate::str::contains("not supported"));
}
#[test]
fn vuln_format_sarif_still_works() {
let dir = fixture();
let mut cmd = tldr_cmd();
cmd.current_dir(dir.path()).args([
"vuln",
".",
"--lang",
"python",
"--format",
"sarif",
"--quiet",
]);
let output = cmd.output().expect("run tldr vuln");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stderr.contains("not supported"),
"vuln --format sarif must not be rejected; got stderr: {stderr}"
);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("$schema") || stdout.contains("\"version\""),
"vuln --format sarif should emit SARIF; got stdout prefix: {}",
&stdout.chars().take(200).collect::<String>()
);
}
#[test]
fn clones_format_sarif_still_works() {
let dir = fixture();
let mut cmd = tldr_cmd();
cmd.current_dir(dir.path())
.args(["clones", ".", "--format", "sarif", "--quiet"]);
cmd.assert().success();
}
#[test]
fn deps_format_dot_still_works() {
let dir = fixture();
let mut cmd = tldr_cmd();
cmd.current_dir(dir.path())
.args(["deps", ".", "--format", "dot", "--quiet"]);
cmd.assert().success();
}
#[test]
fn json_works_universally() {
let dir = fixture();
for cmd_args in [
&["smells"][..],
&["tree"][..],
&["structure"][..],
&["calls"][..],
&["dead"][..],
] {
let mut cmd = tldr_cmd();
cmd.current_dir(dir.path());
cmd.args(cmd_args);
cmd.arg(".");
cmd.args(["--format", "json", "--quiet"]);
let output = cmd.output().expect("run tldr");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stderr.contains("not supported"),
"json must be accepted by {:?}; got stderr: {stderr}",
cmd_args
);
}
}
#[test]
fn validator_unit_universal_formats() {
use tldr_cli::output::{validate_format_for_command, OutputFormat};
for fmt in [OutputFormat::Json, OutputFormat::Text, OutputFormat::Compact] {
for cmd in ["smells", "vuln", "tree", "calls", "structure"] {
assert!(
validate_format_for_command(cmd, fmt).is_ok(),
"{cmd}/{:?} must be accepted",
fmt
);
}
}
}
#[test]
fn validator_unit_sarif_allowlist() {
use tldr_cli::output::{validate_format_for_command, OutputFormat};
assert!(validate_format_for_command("vuln", OutputFormat::Sarif).is_ok());
assert!(validate_format_for_command("clones", OutputFormat::Sarif).is_ok());
for cmd in [
"smells",
"dead",
"health",
"api-check",
"secure",
"debt",
"structure",
"tree",
"halstead",
"complexity",
"extract",
"taint",
"reaching-defs",
] {
let err = validate_format_for_command(cmd, OutputFormat::Sarif)
.expect_err(&format!("{cmd} must reject sarif"));
assert!(err.contains("not supported"));
assert!(err.contains(cmd));
}
}
#[test]
fn validator_unit_dot_allowlist() {
use tldr_cli::output::{validate_format_for_command, OutputFormat};
assert!(validate_format_for_command("clones", OutputFormat::Dot).is_ok());
assert!(validate_format_for_command("deps", OutputFormat::Dot).is_ok());
assert!(validate_format_for_command("calls", OutputFormat::Dot).is_ok());
assert!(validate_format_for_command("impact", OutputFormat::Dot).is_ok());
assert!(validate_format_for_command("hubs", OutputFormat::Dot).is_ok());
assert!(validate_format_for_command("inheritance", OutputFormat::Dot).is_ok());
for cmd in ["smells", "tree", "structure", "taint", "vuln", "secrets"] {
let err = validate_format_for_command(cmd, OutputFormat::Dot)
.expect_err(&format!("{cmd} must reject dot"));
assert!(err.contains("not supported"));
}
}