use std::collections::BTreeMap;
use std::path::Path;
use serde::Serialize;
use speccoder_core::collection::{
CollectionRule, JsRunner, Toolchain, defines_tests, go_rule, rust_reachable, rust_rule,
rust_targets, strip_comments, toolchain_of,
};
use crate::collection::static_cfg;
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct Location {
pub kind: String,
pub path: String,
pub files: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct RunnerLayout {
pub toolchain: Toolchain,
pub root: String,
pub rule: CollectionRule,
pub locations: Vec<Location>,
pub not_run: Vec<String>,
pub naming: Option<String>,
pub story_dir: Option<String>,
pub extension: String,
pub unknown: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct TestLayout {
pub runners: Vec<RunnerLayout>,
pub digest: String,
#[serde(skip)]
pub pytest: Vec<String>,
}
fn parent(p: &str) -> &str {
p.rsplit_once('/').map_or("", |(d, _)| d)
}
fn at(root: &str, rel: &str) -> String {
if root.is_empty() {
rel.to_owned()
} else {
format!("{root}/{rel}")
}
}
#[must_use]
pub fn detect(root: &Path, check_argvs: &[Vec<String>]) -> TestLayout {
let files = static_cfg::files(root);
let read = |p: &str| std::fs::read_to_string(root.join(p)).ok();
let tests_in = |pred: &dyn Fn(&str) -> bool| -> Vec<&String> {
files
.iter()
.filter(|f| pred(f))
.filter(|f| {
toolchain_of(f).is_some_and(|t| read(f).is_some_and(|s| defines_tests(t, &s)))
})
.collect()
};
let mut runners = Vec::new();
for pkg in static_cfg::rust_packages(root, &files) {
let targets = rust_targets(&pkg, &files);
let reach = rust_reachable(&targets, &|p| read(p));
let inside = |f: &str| {
f.ends_with(".rs")
&& static_cfg::owner(std::slice::from_ref(&pkg), f).is_some()
&& !f.starts_with(&at(&pkg.root, "target/"))
};
let tests_dir = at(&pkg.root, "tests");
let inline = tests_in(&|f| {
inside(f)
&& !f.starts_with(&format!("{tests_dir}/"))
&& read(f).is_some_and(|s| strip_comments(&s).contains("#[cfg(test)]"))
});
let top = tests_in(&|f| {
inside(f)
&& f.strip_prefix(&format!("{tests_dir}/"))
.is_some_and(|r| !r.contains('/'))
});
let not_run: Vec<String> =
tests_in(&|f| inside(f) && f.starts_with(&format!("{tests_dir}/")))
.into_iter()
.filter(|f| !reach.contains_key(*f))
.cloned()
.collect();
let mut locations = vec![
Location {
kind: "inline_modules".to_owned(),
path: at(&pkg.root, "src"),
files: inline.len(),
},
Location {
kind: "tests_dir".to_owned(),
path: format!("{tests_dir}/*.rs"),
files: top.len(),
},
];
let mains = files
.iter()
.filter(|f| {
f.strip_prefix(&format!("{tests_dir}/"))
.is_some_and(|r| r.matches('/').count() == 1 && r.ends_with("/main.rs"))
})
.count();
if mains > 0 {
locations.push(Location {
kind: "tests_dir".to_owned(),
path: format!("{tests_dir}/<dir>/main.rs"),
files: mains,
});
}
let mut unknown = Vec::new();
let story_dir = if pkg.autotests {
Some(tests_dir.clone())
} else {
unknown.push(format!(
"{}: `autotests = false` — a new integration test needs a `[[test]]` entry, which speccoder does not add",
at(&pkg.root, "Cargo.toml")
));
None
};
runners.push(RunnerLayout {
toolchain: Toolchain::Rust,
root: pkg.root.clone(),
rule: rust_rule(&pkg),
locations,
not_run,
naming: Some(format!("{tests_dir}/<area>.rs")),
story_dir,
extension: "rs".to_owned(),
unknown,
});
}
if static_cfg::uses_pytest(root, &files, check_argvs) {
let rule = static_cfg::pytest_rule(root);
let py = tests_in(&|f| f.ends_with(".py"));
let mut by_dir: BTreeMap<String, usize> = BTreeMap::new();
for f in &py {
*by_dir.entry(parent(f).to_owned()).or_default() += 1;
}
let exists = |d: &str| d.is_empty() || root.join(d).is_dir();
let mut unknown = Vec::new();
let story_dir = match rule.testpaths.first() {
Some(t) if exists(t.trim_end_matches('/')) => Some(t.trim_end_matches('/').to_owned()),
Some(t) => {
unknown.push(format!(
"testpaths names `{t}`, which does not exist: no place pytest collects"
));
None
}
None if exists("tests") => Some("tests".to_owned()),
None => Some(String::new()),
};
let prefix = py
.iter()
.filter(|f| f.rsplit('/').next().is_some_and(|n| n.starts_with("test_")))
.count();
runners.push(RunnerLayout {
toolchain: Toolchain::Python,
root: String::new(),
rule: rule.rule(),
locations: by_dir
.into_iter()
.map(|(path, files)| Location {
kind: "tests_dir".to_owned(),
path,
files,
})
.collect(),
not_run: py
.iter()
.filter(|f| rule.breaks(f).is_some())
.map(|f| (*f).clone())
.collect(),
naming: Some(if prefix * 2 >= py.len() {
"test_<name>.py".to_owned()
} else {
"<name>_test.py".to_owned()
}),
story_dir,
extension: "py".to_owned(),
unknown,
});
}
for pkg in static_cfg::js_packages(root, &files, check_argvs) {
let inside = |f: &str| pkg.root.is_empty() || f.starts_with(&format!("{}/", pkg.root));
let js = tests_in(&|f| inside(f) && toolchain_of(f) == Some(Toolchain::JavaScript));
let rel = |f: &str| {
if pkg.root.is_empty() {
f.to_owned()
} else {
f.trim_start_matches(&format!("{}/", pkg.root)).to_owned()
}
};
let collected: Vec<&String> = js
.iter()
.copied()
.filter(|f| {
pkg.rule
.as_ref()
.map_or(true, |r| r.breaks(&rel(f)).is_none())
})
.collect();
let mut by_dir: BTreeMap<String, usize> = BTreeMap::new();
for f in &collected {
*by_dir.entry(parent(f).to_owned()).or_default() += 1;
}
let ts = collected.iter().filter(|f| f.contains(".ts")).count();
let extension = if collected.is_empty() {
if files.iter().any(|f| inside(f) && f.ends_with(".ts")) {
"ts"
} else {
"js"
}
} else if ts * 2 >= collected.len() {
"ts"
} else {
"js"
}
.to_owned();
let habit = by_dir
.iter()
.max_by_key(|(_, n)| **n)
.map(|(d, _)| d.clone());
let src = at(&pkg.root, "src");
let story_dir = habit.clone().or_else(|| {
Some(if root.join(&src).is_dir() {
src.clone()
} else {
pkg.root.clone()
})
});
let mut unknown = Vec::new();
let rule = match &pkg.rule {
Ok(r) => r.rule(),
Err(why) => {
unknown.push(why.clone());
CollectionRule {
toolchain: Toolchain::JavaScript,
text: format!(
"{} collects the files its configuration includes (not read: {why})",
pkg.runner.name()
),
source: at(&pkg.root, "package.json"),
}
}
};
runners.push(RunnerLayout {
toolchain: Toolchain::JavaScript,
root: pkg.root.clone(),
rule,
locations: by_dir
.into_iter()
.map(|(path, files)| Location {
kind: if path.contains("__tests__") {
"tests_dir"
} else {
"beside_sources"
}
.to_owned(),
path,
files,
})
.collect(),
not_run: js
.iter()
.filter(|f| !collected.contains(f))
.map(|f| (*f).clone())
.collect(),
naming: Some(match pkg.runner {
JsRunner::Vitest | JsRunner::Jest => format!("<name>.test.{extension}"),
}),
story_dir,
extension,
unknown,
});
}
for m in static_cfg::go_modules(root, &files) {
let inside = |f: &str| m.root.is_empty() || f.starts_with(&format!("{}/", m.root));
let go = tests_in(&|f| inside(f) && f.ends_with("_test.go"));
let mut by_dir: BTreeMap<String, usize> = BTreeMap::new();
for f in &go {
*by_dir.entry(parent(f).to_owned()).or_default() += 1;
}
let tags = static_cfg::go_tags(check_argvs);
runners.push(RunnerLayout {
toolchain: Toolchain::Go,
root: m.root.clone(),
rule: go_rule(),
locations: by_dir
.into_iter()
.map(|(path, files)| Location {
kind: "package".to_owned(),
path,
files,
})
.collect(),
not_run: go
.iter()
.filter(|f| {
speccoder_core::collection::go_breaks(f, &read(f).unwrap_or_default(), &tags)
.is_some()
})
.map(|f| (*f).clone())
.collect(),
naming: Some("<name>_test.go beside the package's sources".to_owned()),
story_dir: Some(m.root.clone()),
extension: "go".to_owned(),
unknown: Vec::new(),
});
}
let canonical = serde_json::to_string(&runners).unwrap_or_default();
TestLayout {
digest: crate::integrity::sha256_hex(canonical.as_bytes()),
runners,
pytest: pytest_command(check_argvs),
}
}
fn pytest_command(check_argvs: &[Vec<String>]) -> Vec<String> {
check_argvs
.iter()
.find_map(|a| {
let at = a
.iter()
.position(|w| w == "pytest" || w.ends_with("/pytest"))?;
Some(a[..=at].to_vec())
})
.unwrap_or_else(|| ["python3", "-m", "pytest"].map(str::to_owned).to_vec())
}
#[must_use]
pub fn text_at(root: &Path) -> String {
detect(root, &crate::verify::check_argvs(root)).text()
}
impl TestLayout {
#[must_use]
pub fn primary(&self) -> Option<&RunnerLayout> {
self.runners
.iter()
.find(|r| r.story_dir.is_some())
.or_else(|| self.runners.first())
}
#[must_use]
pub fn acceptance_file(&self, name: &str, package: Option<&str>) -> Option<String> {
let r = self.primary()?;
let dir = r.story_dir.as_ref()?;
let s = name.to_ascii_lowercase();
Some(match r.toolchain {
Toolchain::Rust => at(dir, &format!("acceptance_{s}.rs")),
Toolchain::Python => at(dir, &format!("test_acceptance_{s}.py")),
Toolchain::JavaScript => at(dir, &format!("acceptance-{s}.test.{}", r.extension)),
Toolchain::Go => at(package.unwrap_or(dir), &format!("acceptance_{s}_test.go")),
})
}
#[must_use]
pub fn command_for(&self, file: &str) -> Option<String> {
let r = self.primary()?;
let stem = file.rsplit('/').next()?.split('.').next()?.to_owned();
Some(match r.toolchain {
Toolchain::Rust => format!("cargo test --test {stem}"),
Toolchain::Python => format!("{} {file}", self.pytest.join(" ")),
Toolchain::JavaScript => format!("npx vitest run {file}"),
Toolchain::Go => format!("go test ./{}", parent(file)),
})
}
#[must_use]
pub fn runs_only(&self, argv: &[String], files: &[&str]) -> bool {
files.iter().any(|f| {
self.command_for(f)
.is_some_and(|c| c.split_whitespace().eq(argv.iter().map(String::as_str)))
})
}
#[must_use]
pub fn text(&self) -> String {
let mut out = String::from("# Test layout (detected by speccoder from this checkout)\n");
if self.runners.is_empty() {
out.push_str("\nNo test runner speccoder knows (Cargo, pytest, vitest/jest, go test) was found.\n");
}
for r in &self.runners {
let name = match r.toolchain {
Toolchain::Rust => "cargo (Rust)",
Toolchain::Python => "pytest (Python)",
Toolchain::JavaScript => "vitest/jest (JavaScript/TypeScript)",
Toolchain::Go => "go test (Go)",
};
out.push_str(&format!(
"\nRunner: {name}{}\nRule: {} — from {}\n",
if r.root.is_empty() {
String::new()
} else {
format!(", in `{}`", r.root)
},
r.rule.text,
r.rule.source
));
let today: Vec<String> = r
.locations
.iter()
.map(|l| match l.kind.as_str() {
"inline_modules" => format!(
"inline `#[cfg(test)]` modules in {} file(s) under {}",
l.files, l.path
),
_ => format!("{}: {} file(s)", l.path, l.files),
})
.collect();
if !today.is_empty() {
out.push_str(&format!("Tests today: {}\n", today.join("; ")));
}
if !r.not_run.is_empty() {
out.push_str(&format!("Not run by anything: {}\n", r.not_run.join(", ")));
}
if let Some(n) = &r.naming {
out.push_str(&format!("Naming: {n}\n"));
}
for u in &r.unknown {
out.push_str(&format!("Not determined: {u}\n"));
}
}
out.push_str(
"\nPut new tests where the runner collects them. A path the task names that the runner would not\ncollect is a mistake in the plan — use the nearest collected place and say so in your report.\nA test file that never runs fails the verdict.\n",
);
out
}
}
#[cfg(test)]
mod tests {
use super::*;
fn write(root: &Path, files: &[(&str, &str)]) {
for (p, t) in files {
let f = root.join(p);
std::fs::create_dir_all(f.parent().unwrap_or(root)).ok();
std::fs::write(f, t).ok();
}
}
#[test]
fn a_rust_crate_s_layout_names_its_rule_its_tests_and_its_dead_files() {
let d = tempfile::tempdir().unwrap_or_else(|e| panic!("{e}"));
write(
d.path(),
&[
(
"Cargo.toml",
"[package]\nname = \"greet\"\nversion = \"0.1.0\"\n",
),
(
"src/lib.rs",
"pub fn greet() {}\n#[cfg(test)]\nmod tests { #[test] fn t() {} }\n",
),
("tests/unit/greet_name.rs", "#[test]\nfn a() {}\n"),
],
);
let l = detect(d.path(), &[]);
assert_eq!(
l.acceptance_file("US1", None).as_deref(),
Some("tests/acceptance_us1.rs")
);
assert_eq!(
l.command_for("tests/acceptance_us1.rs").as_deref(),
Some("cargo test --test acceptance_us1")
);
let argv = |s: &str| s.split(' ').map(str::to_owned).collect::<Vec<_>>();
let file = ["tests/acceptance_us1.rs"];
assert!(l.runs_only(&argv("cargo test --test acceptance_us1"), &file));
assert!(!l.runs_only(&argv("cargo test --test acceptance_us2"), &file));
assert!(!l.runs_only(&argv("cargo test"), &file));
let text = l.text();
assert!(text.contains("Rule: Cargo builds `tests/*.rs`"), "{text}");
assert!(
text.contains("inline `#[cfg(test)]` modules in 1 file(s)"),
"{text}"
);
assert!(
text.contains("Not run by anything: tests/unit/greet_name.rs"),
"{text}"
);
assert_eq!(detect(d.path(), &[]), l);
}
#[test]
fn a_python_acceptance_file_runs_with_the_project_s_pytest() {
let d = tempfile::tempdir().unwrap_or_else(|e| panic!("{e}"));
write(
d.path(),
&[
("pytest.ini", "[pytest]\n"),
("tests/test_a.py", "def test_a(): pass\n"),
],
);
let argv = |s: &str| s.split(' ').map(str::to_owned).collect::<Vec<_>>();
let file = "tests/test_acceptance_us1.py";
let uv = detect(
d.path(),
&[argv(
"uv run --locked --offline python -m pytest -q --junitxml=.speccoder/junit.xml",
)],
);
assert_eq!(
uv.command_for(file).as_deref(),
Some("uv run --locked --offline python -m pytest tests/test_acceptance_us1.py")
);
assert!(uv.runs_only(
&argv("uv run --locked --offline python -m pytest tests/test_acceptance_us1.py"),
&[file]
));
assert!(!uv.runs_only(
&argv("python3 -m pytest tests/test_acceptance_us1.py"),
&[file]
));
assert_eq!(
detect(d.path(), &[argv(".venv/bin/pytest -q")])
.command_for(file)
.as_deref(),
Some(".venv/bin/pytest tests/test_acceptance_us1.py")
);
assert_eq!(
detect(d.path(), &[]).command_for(file).as_deref(),
Some("python3 -m pytest tests/test_acceptance_us1.py")
);
}
#[test]
fn no_collected_place_is_said_never_guessed() {
let d = tempfile::tempdir().unwrap_or_else(|e| panic!("{e}"));
write(
d.path(),
&[
(
"Cargo.toml",
"[package]\nname = \"g\"\nversion = \"0.1.0\"\nautotests = false\n",
),
("src/lib.rs", ""),
],
);
let l = detect(d.path(), &[]);
assert_eq!(l.acceptance_file("US1", None), None);
assert!(l.runners[0].unknown[0].contains("autotests = false"));
let d = tempfile::tempdir().unwrap_or_else(|e| panic!("{e}"));
write(d.path(), &[("pytest.ini", "[pytest]\ntestpaths = spec\n")]);
assert_eq!(detect(d.path(), &[]).acceptance_file("US1", None), None);
}
#[test]
fn python_js_and_go_acceptance_files_follow_the_layout() {
let d = tempfile::tempdir().unwrap_or_else(|e| panic!("{e}"));
write(
d.path(),
&[
("pytest.ini", "[pytest]\n"),
("tests/test_a.py", "def test_a():\n pass\n"),
],
);
assert_eq!(
detect(d.path(), &[])
.acceptance_file("US2", None)
.as_deref(),
Some("tests/test_acceptance_us2.py")
);
let d = tempfile::tempdir().unwrap_or_else(|e| panic!("{e}"));
write(
d.path(),
&[
(
"package.json",
"{ \"devDependencies\": { \"vitest\": \"3\" } }",
),
("src/a.test.ts", "it('a', () => {})\n"),
],
);
assert_eq!(
detect(d.path(), &[])
.acceptance_file("US1", None)
.as_deref(),
Some("src/acceptance-us1.test.ts")
);
let d = tempfile::tempdir().unwrap_or_else(|e| panic!("{e}"));
write(
d.path(),
&[("go.mod", "module ex.com/m\n"), ("g/g.go", "package g\n")],
);
assert_eq!(
detect(d.path(), &[])
.acceptance_file("US1", Some("g"))
.as_deref(),
Some("g/acceptance_us1_test.go")
);
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Command {
pub shown: String,
pub runs: Vec<String>,
}
impl Command {
#[must_use]
pub fn run_by(&self, test: &str) -> bool {
self.runs.iter().any(|w| test.contains(w.as_str()))
}
}
#[must_use]
pub fn commands(root: &Path) -> Vec<Command> {
let read = |p: &str| std::fs::read_to_string(root.join(p)).ok();
let quoted = |w: &str| vec![format!("\"{w}\""), format!("'{w}'")];
let mut out = Vec::new();
if let Some(t) = read("pyproject.toml").and_then(|t| t.parse::<toml::Table>().ok()) {
let scripts = t
.get("project")
.and_then(|p| p.get("scripts"))
.and_then(toml::Value::as_table);
for (name, target) in scripts.into_iter().flatten() {
let module = target
.as_str()
.and_then(|s| s.split(':').next())
.unwrap_or_default();
let top = module.split('.').next().unwrap_or_default();
let mut runs = quoted(name);
if !top.is_empty() {
runs.extend([
format!("import {top}"),
format!("from {top}"),
format!("\"{top}\""),
format!("'{top}'"),
]);
}
out.push(Command {
shown: name.clone(),
runs,
});
}
}
for base in ["src", "."] {
let Ok(dirs) = std::fs::read_dir(root.join(base)) else {
continue;
};
for d in dirs.flatten() {
let pkg = d.file_name().to_string_lossy().into_owned();
if d.path().join("__main__.py").is_file()
&& !out.iter().any(|c| c.runs.contains(&format!("\"{pkg}\"")))
{
let mut runs = quoted(&pkg);
runs.extend([
format!("-m {pkg}"),
format!("import {pkg}"),
format!("from {pkg}"),
]);
out.push(Command {
shown: format!("python -m {pkg}"),
runs,
});
}
}
}
if let Some(t) = read("Cargo.toml").and_then(|t| t.parse::<toml::Table>().ok()) {
let mut bins: Vec<String> = t
.get("bin")
.and_then(toml::Value::as_array)
.into_iter()
.flatten()
.filter_map(|b| {
b.get("name")
.and_then(toml::Value::as_str)
.map(str::to_owned)
})
.collect();
if root.join("src/main.rs").is_file()
&& let Some(name) = t
.get("package")
.and_then(|p| p.get("name"))
.and_then(toml::Value::as_str)
{
bins.push(name.to_owned());
}
for name in bins {
let env = format!("CARGO_BIN_EXE_{name}");
out.push(Command {
shown: name.clone(),
runs: vec![
env,
format!("cargo_bin(\"{name}\")"),
"\"cargo\", \"run\"".to_owned(),
"cargo run".to_owned(),
],
});
}
}
if let Some(v) =
read("package.json").and_then(|t| serde_json::from_str::<serde_json::Value>(&t).ok())
{
let name = v["name"]
.as_str()
.unwrap_or_default()
.rsplit('/')
.next()
.unwrap_or_default()
.to_owned();
let bins: Vec<(String, String)> = match &v["bin"] {
serde_json::Value::String(path) => vec![(name, path.clone())],
serde_json::Value::Object(m) => m
.iter()
.filter_map(|(k, p)| p.as_str().map(|p| (k.clone(), p.to_owned())))
.collect(),
_ => Vec::new(),
};
for (name, path) in bins {
let path = path.trim_start_matches("./").to_owned();
let mut runs = quoted(&name);
runs.push(path.clone());
runs.push(path.rsplit('/').next().unwrap_or(&path).to_owned());
out.push(Command { shown: name, runs });
}
}
out
}
#[must_use]
pub fn via_command(story: &str) -> bool {
let Some(line) = story
.lines()
.find(|l| l.contains("Independent Test"))
.map(str::to_ascii_lowercase)
else {
return false;
};
let words: Vec<&str> = line.split(|c: char| !c.is_alphanumeric()).collect();
let pair = |a: &str, b: &str| words.windows(2).any(|p| p[0] == a && p[1] == b);
["command", "cli", "stdin", "stdout", "stderr", "terminal"]
.iter()
.any(|w| words.contains(w))
|| pair("standard", "input")
|| pair("standard", "output")
|| pair("exit", "code")
|| pair("exit", "status")
}
#[cfg(test)]
mod command_tests {
use super::{commands, via_command};
#[test]
fn a_project_s_commands_and_what_runs_them() {
let dir = tempfile::tempdir().unwrap_or_else(|e| panic!("{e}"));
let r = dir.path();
let write = |p: &str, t: &str| {
std::fs::create_dir_all(r.join(p).parent().unwrap_or(r))
.unwrap_or_else(|e| panic!("{e}"));
std::fs::write(r.join(p), t).unwrap_or_else(|e| panic!("{e}"));
};
write(
"pyproject.toml",
"[project]\nname = \"speccoder-poc\"\n\n[project.scripts]\nspeccoder-poc = \"speccoder_poc:main\"\n",
);
write(
"src/speccoder_poc/__main__.py",
"from . import main\nmain()\n",
);
let c = commands(r);
assert_eq!(
c.iter().map(|c| c.shown.as_str()).collect::<Vec<_>>(),
["speccoder-poc"]
);
assert!(
c[0].run_by("subprocess.run([sys.executable, \"-m\", \"speccoder_poc\"], input=t)")
);
assert!(c[0].run_by("subprocess.run([\"speccoder-poc\", \"in.md\"])"));
assert!(c[0].run_by("from speccoder_poc import main\nmain([\"in.md\"])"));
assert!(!c[0].run_by("from src.services.table_parser import parse_markdown_table"));
let dir = tempfile::tempdir().unwrap_or_else(|e| panic!("{e}"));
let r = dir.path();
std::fs::create_dir_all(r.join("src")).unwrap_or_else(|e| panic!("{e}"));
std::fs::write(r.join("Cargo.toml"), "[package]\nname = \"md2csv\"\n")
.unwrap_or_else(|e| panic!("{e}"));
std::fs::write(r.join("src/main.rs"), "fn main() {}\n").unwrap_or_else(|e| panic!("{e}"));
std::fs::write(
r.join("package.json"),
"{\"name\": \"@x/tables\", \"bin\": {\"tables\": \"./bin/tables.js\"}}",
)
.unwrap_or_else(|e| panic!("{e}"));
let c = commands(r);
assert_eq!(
c.iter().map(|c| c.shown.as_str()).collect::<Vec<_>>(),
["md2csv", "tables"]
);
assert!(c[0].run_by("Command::new(env!(\"CARGO_BIN_EXE_md2csv\"))"));
assert!(c[1].run_by("execFileSync('node', ['bin/tables.js'])"));
assert!(!c[0].run_by("use md2csv::parse;"));
assert!(via_command(
"**Independent Test**: Can be tested by piping a valid Markdown table into the command"
));
assert!(!via_command(
"**Independent Test**: Can be tested by calling parse() with a table"
));
assert!(!via_command(
"**Independent Test**: the client follows the recommended flow"
));
assert!(!via_command("1. **Given** the command, **Then** CSV"));
}
}