use assert_fs::prelude::*;
use assert_fs::TempDir;
use zackstrap::ConfigGenerator;
#[tokio::test]
async fn test_generate_basic_config() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator.generate_basic(false, true).await.unwrap();
temp_dir
.child(".editorconfig")
.assert(predicates::path::exists());
temp_dir
.child(".prettierrc")
.assert(predicates::path::exists());
let editor_config = std::fs::read_to_string(temp_dir.child(".editorconfig").path()).unwrap();
assert!(editor_config.contains("root = true"));
assert!(editor_config.contains("charset = utf-8"));
assert!(editor_config.contains("end_of_line = lf"));
let prettier_config = std::fs::read_to_string(temp_dir.child(".prettierrc").path()).unwrap();
assert!(prettier_config.contains("\"semi\": true"));
assert!(prettier_config.contains("\"singleQuote\": true"));
}
#[tokio::test]
async fn test_generate_ruby_config() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator.generate_ruby(false).await.unwrap();
temp_dir
.child(".editorconfig")
.assert(predicates::path::exists());
temp_dir
.child(".prettierrc")
.assert(predicates::path::exists());
temp_dir
.child(".ruby-version")
.assert(predicates::path::exists());
temp_dir
.child(".node-version")
.assert(predicates::path::exists());
temp_dir
.child(".rubocop.yml")
.assert(predicates::path::exists());
temp_dir
.child("package.json")
.assert(predicates::path::exists());
let ruby_version = std::fs::read_to_string(temp_dir.child(".ruby-version").path()).unwrap();
assert_eq!(ruby_version.trim(), "3.3.0");
let node_version = std::fs::read_to_string(temp_dir.child(".node-version").path()).unwrap();
assert_eq!(node_version.trim(), "24");
let package_json = std::fs::read_to_string(temp_dir.child("package.json").path()).unwrap();
assert!(package_json.contains("prettier-plugin-ruby"));
assert!(package_json.contains("github:prettier/plugin-ruby"));
let rubocop_config = std::fs::read_to_string(temp_dir.child(".rubocop.yml").path()).unwrap();
assert!(rubocop_config.contains("TargetRubyVersion: 3.3"));
assert!(rubocop_config.contains("Max: 120"));
}
#[tokio::test]
async fn test_generate_python_config() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator
.generate_python_with_template(false, "default")
.await
.unwrap();
temp_dir
.child(".editorconfig")
.assert(predicates::path::exists());
temp_dir
.child(".prettierrc")
.assert(predicates::path::exists());
temp_dir
.child(".python-version")
.assert(predicates::path::exists());
temp_dir
.child("pyproject.toml")
.assert(predicates::path::exists());
temp_dir.child(".flake8").assert(predicates::path::exists());
temp_dir
.child("requirements-dev.txt")
.assert(predicates::path::exists());
let python_version = std::fs::read_to_string(temp_dir.child(".python-version").path()).unwrap();
assert_eq!(python_version.trim(), "3.12");
let pyproject_toml = std::fs::read_to_string(temp_dir.child("pyproject.toml").path()).unwrap();
assert!(pyproject_toml.contains("line-length = 88"));
assert!(pyproject_toml.contains("target-version = ['py312']"));
assert!(pyproject_toml.contains("strict = true"));
let flake8_config = std::fs::read_to_string(temp_dir.child(".flake8").path()).unwrap();
assert!(flake8_config.contains("max-line-length = 88"));
assert!(flake8_config.contains("extend-ignore = E203, W503"));
let requirements_dev =
std::fs::read_to_string(temp_dir.child("requirements-dev.txt").path()).unwrap();
assert!(requirements_dev.contains("black=="));
assert!(requirements_dev.contains("flake8=="));
assert!(requirements_dev.contains("mypy=="));
assert!(requirements_dev.contains("pytest=="));
}
#[tokio::test]
async fn test_generate_python_config_with_templates() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator
.generate_python_with_template(false, "django")
.await
.unwrap();
let pyproject_toml = std::fs::read_to_string(temp_dir.child("pyproject.toml").path()).unwrap();
assert!(pyproject_toml.contains("django_settings_module"));
std::fs::remove_file(temp_dir.child("pyproject.toml").path()).unwrap();
std::fs::remove_file(temp_dir.child("justfile").path()).unwrap();
std::fs::remove_file(temp_dir.child(".editorconfig").path()).unwrap();
std::fs::remove_file(temp_dir.child(".prettierrc").path()).unwrap();
std::fs::remove_file(temp_dir.child(".python-version").path()).unwrap();
std::fs::remove_file(temp_dir.child(".flake8").path()).unwrap();
std::fs::remove_file(temp_dir.child("requirements-dev.txt").path()).unwrap();
generator
.generate_python_with_template(false, "flask")
.await
.unwrap();
let pyproject_toml = std::fs::read_to_string(temp_dir.child("pyproject.toml").path()).unwrap();
assert!(pyproject_toml.contains("app_name = \"app\""));
}
#[tokio::test]
async fn test_generate_node_config() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator
.generate_node_with_template(false, "default")
.await
.unwrap();
temp_dir
.child(".editorconfig")
.assert(predicates::path::exists());
temp_dir
.child(".prettierrc")
.assert(predicates::path::exists());
temp_dir.child(".nvmrc").assert(predicates::path::exists());
temp_dir
.child(".eslintrc.json")
.assert(predicates::path::exists());
temp_dir
.child("package.json")
.assert(predicates::path::exists());
let nvmrc = std::fs::read_to_string(temp_dir.child(".nvmrc").path()).unwrap();
assert_eq!(nvmrc.trim(), "20");
let eslint_config = std::fs::read_to_string(temp_dir.child(".eslintrc.json").path()).unwrap();
assert!(eslint_config.contains("es2021"));
assert!(eslint_config.contains("browser"));
assert!(eslint_config.contains("node"));
let package_json = std::fs::read_to_string(temp_dir.child("package.json").path()).unwrap();
assert!(package_json.contains("node-app"));
assert!(package_json.contains("0.1.0"));
}
#[tokio::test]
async fn test_generate_node_config_with_templates() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator
.generate_node_with_template(false, "express")
.await
.unwrap();
let eslint_config = std::fs::read_to_string(temp_dir.child(".eslintrc.json").path()).unwrap();
assert!(eslint_config.contains("es2021"));
assert!(eslint_config.contains("node"));
assert!(eslint_config.contains("no-console"));
std::fs::remove_file(temp_dir.child(".eslintrc.json").path()).unwrap();
std::fs::remove_file(temp_dir.child("package.json").path()).unwrap();
std::fs::remove_file(temp_dir.child("justfile").path()).unwrap();
std::fs::remove_file(temp_dir.child(".editorconfig").path()).unwrap();
std::fs::remove_file(temp_dir.child(".prettierrc").path()).unwrap();
std::fs::remove_file(temp_dir.child(".nvmrc").path()).unwrap();
generator
.generate_node_with_template(false, "react")
.await
.unwrap();
let eslint_config = std::fs::read_to_string(temp_dir.child(".eslintrc.json").path()).unwrap();
assert!(eslint_config.contains("es2021"));
assert!(eslint_config.contains("browser"));
assert!(eslint_config.contains("jsx"));
}
#[tokio::test]
async fn test_generate_go_config() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator
.generate_go_with_template(false, "default")
.await
.unwrap();
temp_dir
.child(".editorconfig")
.assert(predicates::path::exists());
temp_dir
.child(".prettierrc")
.assert(predicates::path::exists());
temp_dir.child("go.mod").assert(predicates::path::exists());
temp_dir
.child(".golangci.yml")
.assert(predicates::path::exists());
let go_mod = std::fs::read_to_string(temp_dir.child("go.mod").path()).unwrap();
assert!(go_mod.contains("module myproject"));
assert!(go_mod.contains("go 1.21"));
let golangci_config = std::fs::read_to_string(temp_dir.child(".golangci.yml").path()).unwrap();
assert!(golangci_config.contains("gofmt"));
assert!(golangci_config.contains("golint"));
assert!(golangci_config.contains("govet"));
assert!(golangci_config.contains("errcheck"));
}
#[tokio::test]
async fn test_generate_go_config_with_templates() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator
.generate_go_with_template(false, "web")
.await
.unwrap();
let justfile = std::fs::read_to_string(temp_dir.child("justfile").path()).unwrap();
assert!(justfile.contains("go-web"));
std::fs::remove_file(temp_dir.child("justfile").path()).unwrap();
std::fs::remove_file(temp_dir.child(".editorconfig").path()).unwrap();
std::fs::remove_file(temp_dir.child(".prettierrc").path()).unwrap();
std::fs::remove_file(temp_dir.child("go.mod").path()).unwrap();
std::fs::remove_file(temp_dir.child(".golangci.yml").path()).unwrap();
generator
.generate_go_with_template(true, "cli")
.await
.unwrap();
let justfile = std::fs::read_to_string(temp_dir.child("justfile").path()).unwrap();
assert!(justfile.contains("go-cli"));
}
#[tokio::test]
async fn test_generate_rust_config() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator
.generate_rust_with_template(false, "default")
.await
.unwrap();
temp_dir
.child(".editorconfig")
.assert(predicates::path::exists());
temp_dir
.child(".prettierrc")
.assert(predicates::path::exists());
temp_dir
.child("rustfmt.toml")
.assert(predicates::path::exists());
temp_dir
.child(".clippy.toml")
.assert(predicates::path::exists());
temp_dir
.child(".cargo/config.toml")
.assert(predicates::path::exists());
let rustfmt_config = std::fs::read_to_string(temp_dir.child("rustfmt.toml").path()).unwrap();
assert!(rustfmt_config.contains("edition = \"2021\""));
assert!(rustfmt_config.contains("max_width = 100"));
let cargo_config =
std::fs::read_to_string(temp_dir.child(".cargo/config.toml").path()).unwrap();
assert!(cargo_config.contains("target-cpu=native"));
}
#[tokio::test]
async fn test_generate_rust_config_with_templates() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator
.generate_rust_with_template(false, "web")
.await
.unwrap();
let justfile = std::fs::read_to_string(temp_dir.child("justfile").path()).unwrap();
assert!(justfile.contains("cargo-web"));
std::fs::remove_file(temp_dir.child("justfile").path()).unwrap();
std::fs::remove_file(temp_dir.child(".editorconfig").path()).unwrap();
std::fs::remove_file(temp_dir.child(".prettierrc").path()).unwrap();
std::fs::remove_file(temp_dir.child("rustfmt.toml").path()).unwrap();
std::fs::remove_file(temp_dir.child(".clippy.toml").path()).unwrap();
std::fs::remove_dir_all(temp_dir.child(".cargo").path()).unwrap();
generator
.generate_rust_with_template(false, "cli")
.await
.unwrap();
let justfile = std::fs::read_to_string(temp_dir.child("justfile").path()).unwrap();
assert!(justfile.contains("cargo-cli"));
}
#[tokio::test]
async fn test_generate_bash_config() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator
.generate_bash_with_template(false, "default")
.await
.unwrap();
temp_dir
.child(".editorconfig")
.assert(predicates::path::exists());
temp_dir
.child(".prettierrc")
.assert(predicates::path::exists());
temp_dir
.child(".shellcheckrc")
.assert(predicates::path::exists());
temp_dir
.child("justfile")
.assert(predicates::path::exists());
let shellcheckrc = std::fs::read_to_string(temp_dir.child(".shellcheckrc").path()).unwrap();
assert!(shellcheckrc.contains("shell=bash"));
assert!(shellcheckrc.contains("enable=require-variable-braces"));
assert!(shellcheckrc.contains("disable=SC1091"));
let justfile = std::fs::read_to_string(temp_dir.child("justfile").path()).unwrap();
assert!(justfile.contains("ShellCheck"));
assert!(justfile.contains("shfmt"));
assert!(justfile.contains("bats"));
}
#[tokio::test]
async fn test_generate_bash_config_with_templates() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator
.generate_bash_with_template(false, "devops")
.await
.unwrap();
let justfile = std::fs::read_to_string(temp_dir.child("justfile").path()).unwrap();
assert!(justfile.contains("DevOps"));
assert!(justfile.contains("deploy"));
std::fs::remove_file(temp_dir.child("justfile").path()).unwrap();
std::fs::remove_file(temp_dir.child(".editorconfig").path()).unwrap();
std::fs::remove_file(temp_dir.child(".prettierrc").path()).unwrap();
std::fs::remove_file(temp_dir.child(".shellcheckrc").path()).unwrap();
generator
.generate_bash_with_template(false, "cli")
.await
.unwrap();
let justfile = std::fs::read_to_string(temp_dir.child("justfile").path()).unwrap();
assert!(justfile.contains("CLI"));
assert!(justfile.contains("main.sh"));
assert!(justfile.contains("install"));
}
#[tokio::test]
async fn test_project_type_detection() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
std::fs::write(temp_dir.child("main.py").path(), "print('Hello')").unwrap();
let project_type = generator.detect_project_type().await.unwrap();
assert!(matches!(project_type, zackstrap::ProjectType::Python));
std::fs::remove_file(temp_dir.child("main.py").path()).unwrap();
std::fs::write(temp_dir.child("package.json").path(), "{}").unwrap();
let project_type = generator.detect_project_type().await.unwrap();
assert!(matches!(project_type, zackstrap::ProjectType::Node));
std::fs::remove_file(temp_dir.child("package.json").path()).unwrap();
std::fs::write(temp_dir.child("main.go").path(), "package main").unwrap();
let project_type = generator.detect_project_type().await.unwrap();
assert!(matches!(project_type, zackstrap::ProjectType::Go));
std::fs::remove_file(temp_dir.child("main.go").path()).unwrap();
std::fs::write(temp_dir.child("main.rs").path(), "fn main() {}").unwrap();
let project_type = generator.detect_project_type().await.unwrap();
assert!(matches!(project_type, zackstrap::ProjectType::Rust));
std::fs::remove_file(temp_dir.child("main.rs").path()).unwrap();
std::fs::write(
temp_dir.child("Gemfile").path(),
"source 'https://rubygems.org'",
)
.unwrap();
let project_type = generator.detect_project_type().await.unwrap();
assert!(matches!(project_type, zackstrap::ProjectType::Ruby));
std::fs::remove_file(temp_dir.child("Gemfile").path()).unwrap();
std::fs::write(temp_dir.child(".shellcheckrc").path(), "shell=bash").unwrap();
let project_type = generator.detect_project_type().await.unwrap();
assert!(matches!(project_type, zackstrap::ProjectType::Bash));
std::fs::remove_file(temp_dir.child(".shellcheckrc").path()).unwrap();
let project_type = generator.detect_project_type().await.unwrap();
assert!(matches!(project_type, zackstrap::ProjectType::Basic));
}
#[tokio::test]
async fn test_dry_run_modes() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator
.dry_run_python_with_template("default")
.await
.unwrap();
generator
.dry_run_node_with_template("default")
.await
.unwrap();
generator.dry_run_go_with_template("default").await.unwrap();
generator
.dry_run_rust_with_template("default")
.await
.unwrap();
generator
.dry_run_bash_with_template("default")
.await
.unwrap();
assert!(!temp_dir.child(".python-version").exists());
assert!(!temp_dir.child(".nvmrc").exists());
assert!(!temp_dir.child("go.mod").exists());
assert!(!temp_dir.child("rustfmt.toml").exists());
assert!(!temp_dir.child(".shellcheckrc").exists());
}
#[tokio::test]
async fn test_force_overwrite_for_new_languages() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator
.generate_python_with_template(false, "default")
.await
.unwrap();
let result = generator
.generate_python_with_template(false, "default")
.await;
assert!(result.is_ok());
generator
.generate_python_with_template(true, "default")
.await
.unwrap();
let _ = std::fs::remove_file(temp_dir.child("package.json").path());
let _ = std::fs::remove_file(temp_dir.child(".nvmrc").path());
let _ = std::fs::remove_file(temp_dir.child(".eslintrc.json").path());
let _ = std::fs::remove_file(temp_dir.child("justfile").path());
let _ = std::fs::remove_file(temp_dir.child(".editorconfig").path());
let _ = std::fs::remove_file(temp_dir.child(".prettierrc").path());
generator
.generate_node_with_template(false, "default")
.await
.unwrap();
let result = generator
.generate_node_with_template(false, "default")
.await;
assert!(result.is_ok());
generator
.generate_node_with_template(true, "default")
.await
.unwrap();
}
#[tokio::test]
async fn test_force_overwrite() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator.generate_basic(false, true).await.unwrap();
let result = generator.generate_basic(false, true).await;
assert!(result.is_err());
generator.generate_basic(true, true).await.unwrap();
}
#[tokio::test]
async fn test_editor_config_sections() {
let temp_dir = TempDir::new().unwrap();
let generator = ConfigGenerator::new(temp_dir.path().to_path_buf());
generator.generate_basic(false, true).await.unwrap();
let editor_config = std::fs::read_to_string(temp_dir.child(".editorconfig").path()).unwrap();
assert!(editor_config.contains("[*]"));
assert!(editor_config.contains("indent_style = space"));
assert!(editor_config.contains("indent_size = 2"));
assert!(editor_config.contains("[*.{rb,erb,ru,rake,gemspec}]"));
assert!(editor_config.contains("[*.{yml,yaml,haml,jbuilder,jsx,html,sls,tf}]"));
assert!(editor_config.contains("[{*[Mm]akefile*,*.mak,*.mk,depend}]"));
assert!(editor_config.contains("[enc/*]"));
assert!(editor_config.contains("[reg*.[ch]]"));
}