use assert_cmd::Command;
use predicates::prelude::*;
use std::fs;
use tempfile::TempDir;
fn ggen() -> Command {
Command::cargo_bin("ggen").expect("Failed to find ggen binary")
}
fn create_test_template(temp_dir: &TempDir, name: &str) -> std::path::PathBuf {
let templates_dir = temp_dir.path().join(".ggen/templates");
fs::create_dir_all(&templates_dir).expect("Failed to create templates dir");
let template_dir = templates_dir.join(name);
fs::create_dir_all(&template_dir).expect("Failed to create template dir");
let template_content = r#"# {{ project_name }}
## Description
{{ description | default(value="A template-generated project") }}
## Features
{% for feature in features %}
- {{ feature }}
{% endfor %}
"#;
fs::write(template_dir.join("README.md.tera"), template_content)
.expect("Failed to write template");
let metadata = r#"
name = "{{ name }}"
version = "1.0.0"
description = "Test template"
[variables]
project_name = { type = "string", prompt = "Project name?" }
description = { type = "string", optional = true }
features = { type = "array", default = [] }
"#;
fs::write(template_dir.join("template.toml"), metadata).expect("Failed to write metadata");
template_dir
}
#[test]
fn test_template_new_creates_structure() {
let temp_dir = TempDir::new().unwrap();
ggen()
.arg("template")
.arg("new")
.arg("my-template")
.current_dir(&temp_dir)
.assert()
.success();
let template_path = temp_dir.path().join(".ggen/templates/my-template");
assert!(
template_path.exists(),
"Template directory should be created"
);
assert!(
template_path.join("template.toml").exists()
|| template_path.join("template.yaml").exists(),
"Template metadata should exist"
);
}
#[test]
fn test_template_list_empty() {
let temp_dir = TempDir::new().unwrap();
ggen()
.arg("template")
.arg("list")
.current_dir(&temp_dir)
.assert()
.success()
.stdout(
predicate::str::contains("No templates found")
.or(predicate::str::contains("templates")),
);
}
#[test]
fn test_template_list_shows_installed() {
let temp_dir = TempDir::new().unwrap();
create_test_template(&temp_dir, "test-template");
ggen()
.arg("template")
.arg("list")
.current_dir(&temp_dir)
.assert()
.success()
.stdout(predicate::str::contains("test-template"));
}
#[test]
fn test_template_list_json_format() {
let temp_dir = TempDir::new().unwrap();
create_test_template(&temp_dir, "test-template");
let output = ggen()
.arg("template")
.arg("list")
.arg("--json")
.current_dir(&temp_dir)
.output()
.expect("Failed to execute");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
if !stdout.is_empty() {
serde_json::from_str::<serde_json::Value>(&stdout).expect("Output should be valid JSON");
}
}
#[test]
fn test_template_show_displays_details() {
let temp_dir = TempDir::new().unwrap();
create_test_template(&temp_dir, "detailed-template");
ggen()
.arg("template")
.arg("show")
.arg("detailed-template")
.current_dir(&temp_dir)
.assert()
.success()
.stdout(predicate::str::contains("detailed-template"))
.stdout(predicate::str::contains("version").or(predicate::str::contains("Version")));
}
#[test]
fn test_template_show_missing_template() {
let temp_dir = TempDir::new().unwrap();
ggen()
.arg("template")
.arg("show")
.arg("nonexistent-template")
.current_dir(&temp_dir)
.assert()
.failure()
.stderr(predicate::str::contains("not found").or(predicate::str::contains("error")));
}
#[test]
fn test_template_lint_valid_template() {
let temp_dir = TempDir::new().unwrap();
let template_path = create_test_template(&temp_dir, "valid-template");
ggen()
.arg("template")
.arg("lint")
.arg(template_path.to_str().unwrap())
.current_dir(&temp_dir)
.assert()
.success();
}
#[test]
fn test_template_lint_invalid_syntax() {
let temp_dir = TempDir::new().unwrap();
let templates_dir = temp_dir.path().join(".ggen/templates");
fs::create_dir_all(&templates_dir).unwrap();
let template_dir = templates_dir.join("invalid-template");
fs::create_dir_all(&template_dir).unwrap();
let invalid_content = r#"{{ unclosed_variable"#;
fs::write(template_dir.join("invalid.tera"), invalid_content).unwrap();
let _ = ggen()
.arg("template")
.arg("lint")
.arg(template_dir.to_str().unwrap())
.current_dir(&temp_dir)
.output();
}
#[test]
fn test_template_generate_tree_basic() {
let temp_dir = TempDir::new().unwrap();
create_test_template(&temp_dir, "tree-template");
let data_content = r#"
project_name: "MyProject"
description: "A test project"
features:
- "Fast"
- "Reliable"
"#;
fs::write(temp_dir.path().join("data.yaml"), data_content).unwrap();
ggen()
.arg("template")
.arg("generate_tree")
.arg("tree-template")
.arg("--data")
.arg(temp_dir.path().join("data.yaml").to_str().unwrap())
.arg("--output")
.arg(temp_dir.path().to_str().unwrap())
.current_dir(&temp_dir)
.assert()
.success();
let output_file = temp_dir.path().join("README.md");
assert!(output_file.exists(), "Generated file should exist");
let content = fs::read_to_string(&output_file).unwrap();
assert!(
content.contains("MyProject"),
"Template should render project name"
);
assert!(
content.contains("A test project"),
"Template should render description"
);
}
#[test]
fn test_template_regenerate_basic() {
let temp_dir = TempDir::new().unwrap();
create_test_template(&temp_dir, "regen-template");
let initial_content = r#"# Project
<!-- ggen:start -->
Generated content
<!-- ggen:end -->
User content
"#;
fs::write(temp_dir.path().join("output.md"), initial_content).unwrap();
ggen()
.arg("template")
.arg("regenerate")
.arg("regen-template")
.current_dir(&temp_dir)
.assert()
.success();
assert!(temp_dir.path().join("output.md").exists());
}
#[test]
fn test_template_help_output() {
ggen()
.arg("template")
.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("Template management"))
.stdout(predicate::str::contains("new"))
.stdout(predicate::str::contains("list"))
.stdout(predicate::str::contains("show"))
.stdout(predicate::str::contains("lint"));
}
#[test]
fn test_template_new_help() {
ggen()
.arg("template")
.arg("new")
.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("Create a new template"));
}
#[test]
fn test_template_invalid_verb() {
ggen()
.arg("template")
.arg("invalid-verb")
.assert()
.failure()
.stderr(predicate::str::contains("error").or(predicate::str::contains("invalid")));
}
#[test]
fn test_template_new_with_description() {
let temp_dir = TempDir::new().unwrap();
ggen()
.arg("template")
.arg("new")
.arg("described-template")
.arg("--description")
.arg("A template with description")
.current_dir(&temp_dir)
.assert()
.success();
let template_path = temp_dir.path().join(".ggen/templates/described-template");
assert!(template_path.exists());
}
#[test]
fn test_template_generate_tree_missing_template() {
let temp_dir = TempDir::new().unwrap();
ggen()
.arg("template")
.arg("generate_tree")
.arg("nonexistent-template")
.current_dir(&temp_dir)
.assert()
.failure()
.stderr(predicate::str::contains("not found").or(predicate::str::contains("error")));
}
#[test]
fn test_template_performance_list() {
let temp_dir = TempDir::new().unwrap();
for i in 0..10 {
create_test_template(&temp_dir, &format!("template-{}", i));
}
let start = std::time::Instant::now();
ggen()
.arg("template")
.arg("list")
.current_dir(&temp_dir)
.assert()
.success();
let duration = start.elapsed();
assert!(
duration.as_secs() < 5,
"Listing templates should be fast: {:?}",
duration
);
}