use crate::common::run_rsconstruct_with_env;
use std::fs;
use tempfile::TempDir;
#[test]
fn product_show_resolves_by_output_and_prints_all_sections() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let p = temp_dir.path();
fs::create_dir_all(p.join("tera.templates")).unwrap();
fs::create_dir_all(p.join("data")).unwrap();
fs::write(p.join("data/a.md"), "a").unwrap();
fs::write(p.join("data/b.md"), "b").unwrap();
fs::write(
p.join("rsconstruct.toml"),
"[processor.tera]\nsrc_dirs = [\"tera.templates\"]\n[analyzer.tera]\n",
)
.unwrap();
fs::write(
p.join("tera.templates/report.txt.tera"),
"Total: {{ glob(pattern=\"data/*.md\") | length }}\n",
)
.unwrap();
let build = run_rsconstruct_with_env(p, &["build"], &[("NO_COLOR", "1")]);
assert!(
build.status.success(),
"build failed: {}",
String::from_utf8_lossy(&build.stderr)
);
let out = run_rsconstruct_with_env(p, &["product", "show", "report.txt"], &[("NO_COLOR", "1")]);
assert!(
out.status.success(),
"product show failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("processor:") && stdout.contains("tera"),
"missing processor section: {}",
stdout
);
assert!(
stdout.contains("primary") && stdout.contains("tera.templates/report.txt.tera"),
"missing primary input: {}",
stdout
);
assert!(
stdout.contains("hash_pieces:") && stdout.contains("glob") && stdout.contains("data/*.md"),
"missing hash_pieces section: {}",
stdout
);
assert!(
stdout.contains("glob_resolved") && stdout.contains("2 files"),
"expected collapsed resolved summary: {}",
stdout
);
assert!(
!stdout.contains("data/a.md") && !stdout.contains("data/b.md"),
"resolved file list should be hidden without --verbose: {}",
stdout
);
assert!(
stdout.contains("descriptor_key:") && stdout.contains("cache_state:"),
"missing descriptor/cache footer: {}",
stdout
);
let out_v = run_rsconstruct_with_env(
p,
&["--verbose", "product", "show", "report.txt"],
&[("NO_COLOR", "1")],
);
assert!(
out_v.status.success(),
"product show --verbose failed: {}",
String::from_utf8_lossy(&out_v.stderr)
);
let stdout_v = String::from_utf8_lossy(&out_v.stdout);
assert!(
stdout_v.contains("data/a.md") && stdout_v.contains("data/b.md"),
"expected resolved file list under --verbose: {}",
stdout_v
);
}
#[test]
fn product_show_falls_back_to_primary_input() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let p = temp_dir.path();
fs::create_dir_all(p.join("tera.templates")).unwrap();
fs::write(
p.join("rsconstruct.toml"),
"[processor.tera]\nsrc_dirs = [\"tera.templates\"]\n[analyzer.tera]\n",
)
.unwrap();
fs::write(p.join("tera.templates/report.txt.tera"), "Hello\n").unwrap();
let build = run_rsconstruct_with_env(p, &["build"], &[("NO_COLOR", "1")]);
assert!(build.status.success());
let out = run_rsconstruct_with_env(
p,
&["product", "show", "tera.templates/report.txt.tera"],
&[("NO_COLOR", "1")],
);
assert!(
out.status.success(),
"show by primary input failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("outputs:") && stdout.contains("report.txt"),
"expected output report.txt: {}",
stdout
);
}
#[test]
fn product_show_unknown_path_errors() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let p = temp_dir.path();
fs::write(
p.join("rsconstruct.toml"),
"[processor.tera]\nsrc_dirs = [\"tera.templates\"]\n",
)
.unwrap();
fs::create_dir_all(p.join("tera.templates")).unwrap();
fs::write(p.join("tera.templates/x.txt.tera"), "x\n").unwrap();
let out = run_rsconstruct_with_env(
p,
&["product", "show", "does/not/exist.txt"],
&[("NO_COLOR", "1")],
);
assert!(!out.status.success(), "unknown path must fail");
let combined = format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
assert!(
combined.contains("No product owns or consumes"),
"expected actionable error: {}",
combined
);
}
#[test]
fn product_show_json_shape() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let p = temp_dir.path();
fs::create_dir_all(p.join("tera.templates")).unwrap();
fs::create_dir_all(p.join("data")).unwrap();
fs::write(p.join("data/a.md"), "a").unwrap();
fs::write(
p.join("rsconstruct.toml"),
"[processor.tera]\nsrc_dirs = [\"tera.templates\"]\n[analyzer.tera]\n",
)
.unwrap();
fs::write(
p.join("tera.templates/report.txt.tera"),
"Total: {{ glob(pattern=\"data/*.md\") | length }}\n",
)
.unwrap();
let build = run_rsconstruct_with_env(p, &["build"], &[("NO_COLOR", "1")]);
assert!(build.status.success());
let out = run_rsconstruct_with_env(
p,
&["--json", "product", "show", "report.txt"],
&[("NO_COLOR", "1")],
);
assert!(
out.status.success(),
"json product show failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout);
let parsed: serde_json::Value = serde_json::from_str(&stdout)
.unwrap_or_else(|e| panic!("invalid JSON: {}\n---\n{}", e, stdout));
assert_eq!(parsed["processor"], "tera");
assert_eq!(parsed["outputs"][0], "report.txt");
assert_eq!(
parsed["inputs"]["primary"],
"tera.templates/report.txt.tera"
);
let tera_pieces = parsed["hash_pieces"]["tera"]
.as_array()
.unwrap_or_else(|| panic!("hash_pieces.tera missing or not array: {}", stdout));
let joined = tera_pieces
.iter()
.filter_map(|v| v.as_str())
.collect::<Vec<_>>()
.join("\n");
assert!(
joined.contains("glob:data/*.md"),
"expected glob piece: {}",
joined
);
assert!(
parsed["descriptor_key"].is_string(),
"descriptor_key must be a string"
);
assert!(
parsed["cache_state"].is_string(),
"cache_state must be a string"
);
}