use super::*;
pub(super) fn setup_use_case_stubs(base: &Path) {
fs::write(
base.join("puerto.toml"),
"[project]\nname = \"test-app\"\n\n[[entity]]\nname = \"Product\"\nuse_cases = [\"create_product\"]\n",
)
.unwrap();
fs::create_dir_all(base.join("business/src/domain/product")).unwrap();
fs::write(
base.join("business/src/lib.rs"),
"pub mod domain {\n pub mod product {\n pub mod errors;\n pub mod model;\n pub mod repository;\n pub mod use_cases {\n pub mod create_product;\n }\n }\n}\npub mod application {\n pub mod product {\n pub mod create_product;\n }\n}\n",
)
.unwrap();
fs::create_dir_all(base.join("business/src/application/product")).unwrap();
}
#[test]
fn use_case_creates_trait_file() {
let dir = temp_dir("uc_trait");
cleanup(&dir);
fs::create_dir_all(&dir).unwrap();
setup_use_case_stubs(&dir);
scaffold::run_use_case("Product", "delete_product", &dir).unwrap();
assert!(
dir.join("business/src/domain/product/use_cases/delete_product.rs")
.exists()
);
cleanup(&dir);
}
#[test]
fn use_case_creates_impl_file() {
let dir = temp_dir("uc_impl");
cleanup(&dir);
fs::create_dir_all(&dir).unwrap();
setup_use_case_stubs(&dir);
scaffold::run_use_case("Product", "delete_product", &dir).unwrap();
assert!(
dir.join("business/src/application/product/delete_product.rs")
.exists()
);
cleanup(&dir);
}
#[test]
fn use_case_trait_file_has_correct_content() {
let dir = temp_dir("uc_trait_content");
cleanup(&dir);
fs::create_dir_all(&dir).unwrap();
setup_use_case_stubs(&dir);
scaffold::run_use_case("Product", "delete_product", &dir).unwrap();
let content =
fs::read_to_string(dir.join("business/src/domain/product/use_cases/delete_product.rs"))
.unwrap();
assert!(content.contains("DeleteProductParams"));
assert!(content.contains("DeleteProductUseCaseTrait"));
cleanup(&dir);
}
#[test]
fn use_case_impl_file_has_correct_content() {
let dir = temp_dir("uc_impl_content");
cleanup(&dir);
fs::create_dir_all(&dir).unwrap();
setup_use_case_stubs(&dir);
scaffold::run_use_case("Product", "delete_product", &dir).unwrap();
let content =
fs::read_to_string(dir.join("business/src/application/product/delete_product.rs")).unwrap();
assert!(content.contains("DeleteProductUseCaseImpl"));
assert!(content.contains("ProductRepositoryTrait"));
assert!(content.contains("DeleteProductUseCaseTrait"));
cleanup(&dir);
}
#[test]
fn use_case_patches_business_lib_rs() {
let dir = temp_dir("uc_patch_lib");
cleanup(&dir);
fs::create_dir_all(&dir).unwrap();
setup_use_case_stubs(&dir);
scaffold::run_use_case("Product", "delete_product", &dir).unwrap();
let content = fs::read_to_string(dir.join("business/src/lib.rs")).unwrap();
assert!(content.contains("pub mod delete_product;"));
assert!(content.contains("pub mod create_product;")); assert_eq!(content.matches("pub mod delete_product;").count(), 2);
cleanup(&dir);
}
#[test]
fn use_case_updates_puerto_toml() {
let dir = temp_dir("uc_puerto_toml");
cleanup(&dir);
fs::create_dir_all(&dir).unwrap();
setup_use_case_stubs(&dir);
scaffold::run_use_case("Product", "delete_product", &dir).unwrap();
let content = fs::read_to_string(dir.join("puerto.toml")).unwrap();
assert!(content.contains("delete_product"));
assert!(content.contains("create_product"));
cleanup(&dir);
}
#[test]
fn use_case_regenerates_bootstrap() {
let dir = temp_dir("uc_bootstrap");
cleanup(&dir);
fs::create_dir_all(&dir).unwrap();
setup_use_case_stubs(&dir);
scaffold::run_use_case("Product", "delete_product", &dir).unwrap();
let content = fs::read_to_string(dir.join("presentation/src/generated/bootstrap.rs")).unwrap();
assert!(content.contains("DeleteProductUseCaseImpl"));
assert!(content.contains("CreateProductUseCaseImpl"));
cleanup(&dir);
}
#[test]
fn use_case_normalizes_entity_casing() {
let dir = temp_dir("uc_normalize");
cleanup(&dir);
fs::create_dir_all(&dir).unwrap();
setup_use_case_stubs(&dir);
scaffold::run_use_case("product", "delete_product", &dir).unwrap();
assert!(
dir.join("business/src/domain/product/use_cases/delete_product.rs")
.exists()
);
cleanup(&dir);
}
#[test]
fn use_case_errors_when_entity_not_in_puerto_toml() {
let dir = temp_dir("uc_missing_entity");
cleanup(&dir);
fs::create_dir_all(&dir).unwrap();
setup_use_case_stubs(&dir);
let result = scaffold::run_use_case("NonExistent", "do_something", &dir);
assert!(result.is_err());
cleanup(&dir);
}
#[test]
fn use_case_rerun_is_refused_and_leaves_files_untouched() {
let dir = temp_dir("uc_rerun_refused");
cleanup(&dir);
fs::create_dir_all(&dir).unwrap();
setup_use_case_stubs(&dir);
scaffold::run_use_case("Product", "delete_product", &dir).unwrap();
let impl_path = dir.join("business/src/application/product/delete_product.rs");
fs::write(&impl_path, "// implemented by hand\n").unwrap();
let result = scaffold::run_use_case("Product", "delete_product", &dir);
assert!(result.is_err(), "a second run must be refused");
assert!(result.unwrap_err().to_string().contains("already exists"));
assert_eq!(
fs::read_to_string(&impl_path).unwrap(),
"// implemented by hand\n"
);
let toml = fs::read_to_string(dir.join("puerto.toml")).unwrap();
assert_eq!(toml.matches("delete_product").count(), 1);
cleanup(&dir);
}
#[test]
fn use_case_rejects_an_action_that_is_not_snake_case() {
let dir = temp_dir("uc_not_snake");
cleanup(&dir);
fs::create_dir_all(&dir).unwrap();
setup_use_case_stubs(&dir);
let result = scaffold::run_use_case("Product", "ArchiveProduct", &dir);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("snake_case"));
assert!(
!dir.join("business/src/application/product/ArchiveProduct.rs")
.exists()
);
assert!(
!fs::read_to_string(dir.join("puerto.toml"))
.unwrap()
.contains("ArchiveProduct")
);
cleanup(&dir);
}
#[test]
fn use_case_refuses_to_replace_a_scaffolded_crud_use_case() {
let dir = temp_dir("uc_crud_clash");
cleanup(&dir);
fs::create_dir_all(&dir).unwrap();
let output = generate_project("uc-crud-clash", &dir).unwrap();
scaffold::run_scaffold(
"Product",
&output,
&[scaffold_test_field("title", "String")],
OverwritePolicy::Ask,
)
.unwrap();
let impl_path = output.join("business/src/application/product/create_product.rs");
let before = fs::read_to_string(&impl_path).unwrap();
let result = scaffold::run_use_case("Product", "create_product", &output);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("already exists"));
assert_eq!(fs::read_to_string(&impl_path).unwrap(), before);
cleanup(&dir);
}
#[test]
fn use_case_adds_its_field_to_the_entity_api_struct() {
let dir = temp_dir("uc_routes_field");
cleanup(&dir);
fs::create_dir_all(&dir).unwrap();
let output = generate_project("uc-routes-field", &dir).unwrap();
scaffold::run_scaffold(
"Product",
&output,
&[scaffold_test_field("title", "String")],
OverwritePolicy::Ask,
)
.unwrap();
scaffold::run_use_case("Product", "archive_product", &output).unwrap();
let routes = fs::read_to_string(output.join("presentation/src/api/product/routes.rs")).unwrap();
assert!(routes.contains("pub archive_product: Arc<dyn ArchiveProductUseCaseTrait>,"));
assert!(routes.contains(
"use business::domain::product::use_cases::archive_product::ArchiveProductUseCaseTrait;"
));
assert!(!routes.contains("UseCaseImpl"), "{routes}");
cleanup(&dir);
}
#[test]
fn rescaffold_with_force_keeps_custom_use_case_in_the_api_struct() {
let dir = temp_dir("uc_routes_force");
cleanup(&dir);
fs::create_dir_all(&dir).unwrap();
let output = generate_project("uc-routes-force", &dir).unwrap();
let fields = [scaffold_test_field("title", "String")];
scaffold::run_scaffold("Product", &output, &fields, OverwritePolicy::Ask).unwrap();
scaffold::run_use_case("Product", "archive_product", &output).unwrap();
scaffold::run_scaffold("Product", &output, &fields, OverwritePolicy::Force).unwrap();
let routes = fs::read_to_string(output.join("presentation/src/api/product/routes.rs")).unwrap();
assert_eq!(
routes
.matches("pub archive_product: Arc<dyn ArchiveProductUseCaseTrait>,")
.count(),
1
);
cleanup(&dir);
}