use super::*;
use compile_matrix::{
CUSTOM_USE_CASE, ENUM_ONLY, ENUM_VO, EVENTS_DB, EVENTS_KAFKA, MULTI_ENTITY, NO_DEMO, NO_WEB,
OPT_VEC_PRIM, OPT_VEC_VO, PLAIN_VO, POSTGRES_EVERY_TYPE, PRIMITIVES, SHARED_VO, Scenario,
UUID_VO, VO_KITCHEN_SINK, WEB_CRUD, WEB_EVERY_TYPE, apply_scenario,
};
use std::collections::BTreeMap;
pub(super) fn project_tree(root: &Path) -> BTreeMap<String, String> {
fn walk(root: &Path, dir: &Path, out: &mut BTreeMap<String, String>) {
let mut entries: Vec<_> = fs::read_dir(dir).unwrap().map(|e| e.unwrap()).collect();
entries.sort_by_key(|e| e.file_name());
for entry in entries {
let path = entry.path();
if path.is_dir() {
walk(root, &path, out);
} else {
let rel = path
.strip_prefix(root)
.unwrap()
.to_string_lossy()
.into_owned();
let content = String::from_utf8_lossy(&fs::read(&path).unwrap()).into_owned();
out.insert(rel, content);
}
}
}
let mut out = BTreeMap::new();
walk(root, root, &mut out);
out
}
fn normalize_path(path: &str) -> String {
match path.rsplit_once('/') {
Some((dir, file)) if dir.ends_with("migrations") => match file.split_once('_') {
Some((version, rest)) if version.chars().all(|c| c.is_ascii_digit()) => {
format!("{dir}/<version>_{rest}")
}
_ => path.to_string(),
},
_ => path.to_string(),
}
}
fn generated_output(scenario: &Scenario) -> String {
let dir = temp_dir(&format!("snapshot_{}", scenario.name));
cleanup(&dir);
fs::create_dir_all(&dir).unwrap();
let output = generate_project("snapshot", &dir).unwrap();
let before = project_tree(&output);
apply_scenario(scenario, &output);
let after = project_tree(&output);
cleanup(&dir);
let mut sections = BTreeMap::new();
for (path, content) in &after {
if before.get(path) != Some(content) {
sections.insert(normalize_path(path), content.clone());
}
}
for path in before.keys().filter(|p| !after.contains_key(*p)) {
sections.insert(normalize_path(path), "(deleted)\n".to_string());
}
sections
.iter()
.map(|(path, content)| format!("=== {path} ===\n{content}"))
.collect::<Vec<_>>()
.join("\n")
}
macro_rules! snapshot_scenarios {
($($test:ident => $scenario:ident),* $(,)?) => {$(
#[test]
fn $test() {
insta::assert_snapshot!($scenario.name, generated_output(&$scenario));
}
)*};
}
snapshot_scenarios! {
snapshot_no_demo => NO_DEMO,
snapshot_no_web => NO_WEB,
snapshot_primitives => PRIMITIVES,
snapshot_uuid_vo => UUID_VO,
snapshot_opt_vec_prim => OPT_VEC_PRIM,
snapshot_plain_vo => PLAIN_VO,
snapshot_opt_vec_vo => OPT_VEC_VO,
snapshot_enum_vo => ENUM_VO,
snapshot_shared_vo => SHARED_VO,
snapshot_multi_entity => MULTI_ENTITY,
snapshot_vo_kitchen_sink => VO_KITCHEN_SINK,
snapshot_enum_only => ENUM_ONLY,
snapshot_custom_use_case => CUSTOM_USE_CASE,
snapshot_postgres_every_type => POSTGRES_EVERY_TYPE,
snapshot_events_db => EVENTS_DB,
snapshot_events_kafka => EVENTS_KAFKA,
snapshot_web_crud => WEB_CRUD,
snapshot_web_every_type => WEB_EVERY_TYPE,
}
#[test]
fn should_hide_only_the_migration_version_from_snapshots() {
assert_eq!(
normalize_path("infrastructure/migrations/20261011093000_create_product_table.sql"),
"infrastructure/migrations/<version>_create_product_table.sql"
);
assert_eq!(
normalize_path("business/src/domain/product/model.rs"),
"business/src/domain/product/model.rs"
);
}