use std::path::{Path, PathBuf};
use keelson_gen::Config;
fn manifest_path(rel: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join(rel)
}
fn generate() -> Vec<(String, String)> {
static NEXT: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let db_path = std::env::temp_dir().join(format!(
"keelson-gen-{}-{}.db",
std::process::id(),
NEXT.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
));
let _ = std::fs::remove_file(&db_path);
let conn = rusqlite::Connection::open(&db_path).expect("creating the fixture database");
conn.execute_batch(include_str!("fixtures/sqlite_schema.sql"))
.expect("applying the fixture DDL");
drop(conn);
let mut config =
Config::load(manifest_path("tests/fixtures/sqlite.toml")).expect("fixture config");
config.url = Some(format!("sqlite://{}", db_path.display()));
let files = keelson_gen::generate(&config).expect("generation");
let _ = std::fs::remove_file(&db_path);
files
}
#[test]
fn the_same_schema_generates_byte_identical_output_twice() {
assert_eq!(generate(), generate());
}
#[test]
fn the_output_matches_the_checked_in_fixture() {
let files = generate();
let dir = manifest_path("tests/generated/sqlite");
if std::env::var_os("KEELSON_GEN_BLESS").is_some() {
keelson_gen::write_files(&dir, &files).expect("blessing the fixture");
return;
}
for (name, contents) in &files {
let checked_in = std::fs::read_to_string(dir.join(name))
.unwrap_or_else(|e| panic!("{name}: reading the checked-in fixture: {e}"));
assert_eq!(
&checked_in, contents,
"{name} drifted from the checked-in fixture; \
regenerate with KEELSON_GEN_BLESS=1"
);
}
let mut on_disk: Vec<String> = std::fs::read_dir(&dir)
.expect("fixture dir")
.map(|e| e.unwrap().file_name().to_string_lossy().into_owned())
.filter(|n| n.ends_with(".rs"))
.collect();
on_disk.sort();
let mut expected: Vec<String> = files.iter().map(|(n, _)| n.clone()).collect();
expected.sort();
assert_eq!(on_disk, expected);
}
#[test]
fn the_cli_writes_the_out_directory_and_cleans_only_its_own_leftovers() {
let scratch = std::env::temp_dir().join(format!("keelson-gen-cli-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&scratch);
std::fs::create_dir_all(&scratch).unwrap();
let db_path = scratch.join("schema.db");
let conn = rusqlite::Connection::open(&db_path).unwrap();
conn.execute_batch(include_str!("fixtures/sqlite_schema.sql"))
.unwrap();
drop(conn);
let out = scratch.join("models");
std::fs::create_dir_all(&out).unwrap();
std::fs::write(
out.join("dropped_table.rs"),
"// @generated by keelson-gen. DO NOT EDIT.\n",
)
.unwrap();
std::fs::write(out.join("hand_written.rs"), "// mine\n").unwrap();
let status = std::process::Command::new(env!("CARGO_BIN_EXE_keelson-gen"))
.args([
"--config",
manifest_path("tests/fixtures/sqlite.toml")
.to_str()
.unwrap(),
"--url",
&format!("sqlite://{}", db_path.display()),
"--out",
out.to_str().unwrap(),
])
.status()
.expect("running the keelson-gen binary");
assert!(status.success());
let users = std::fs::read_to_string(out.join("users.rs")).expect("users.rs written");
let expected = generate();
assert_eq!(
users,
expected.iter().find(|(n, _)| n == "users.rs").unwrap().1,
"the CLI writes exactly what the library generates"
);
assert!(out.join("mod.rs").exists());
assert!(
!out.join("dropped_table.rs").exists(),
"a generated leftover is removed"
);
assert!(
out.join("hand_written.rs").exists(),
"a hand-written file is never deleted"
);
let _ = std::fs::remove_dir_all(&scratch);
}