use error_engine::{Catalog, EngineError, UNKNOWN_CODE};
fn write_temp_toml(contents: &str) -> tempfile_path::TempPath {
tempfile_path::TempPath::new(contents)
}
mod tempfile_path {
use std::path::{Path, PathBuf};
pub struct TempPath {
path: PathBuf,
}
impl TempPath {
pub fn new(contents: &str) -> Self {
let mut path = std::env::temp_dir();
let unique = format!(
"error-engine-test-{}-{}.toml",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
);
path.push(unique);
std::fs::write(&path, contents).unwrap();
Self { path }
}
pub fn as_str(&self) -> &str {
self.path.to_str().unwrap()
}
}
impl Drop for TempPath {
fn drop(&mut self) {
let _ = std::fs::remove_file(&self.path);
}
}
impl AsRef<Path> for TempPath {
fn as_ref(&self) -> &Path {
&self.path
}
}
}
const VALID_CATALOG: &str = r#"
[CONFIG_NOT_FOUND]
template = "Could not find the configuration file at {path}"
hint = "Check that the path is correct"
[DB_CONN_FAILED]
template = "Could not connect to the database: {reason}"
"#;
#[test]
fn load_valid_toml_and_render_existing_code() {
let file = write_temp_toml(VALID_CATALOG);
let catalog = Catalog::load(file.as_str()).expect("valid catalog should load");
let rendered = catalog.render("CONFIG_NOT_FOUND", &[("path", "/etc/app.toml".to_string())]);
assert_eq!(rendered, "Could not find the configuration file at /etc/app.toml");
}
#[test]
fn load_missing_path_returns_catalog_read_error() {
let result = Catalog::load("/definitely/does/not/exist/errors.toml");
assert!(matches!(result, Err(EngineError::CatalogRead { .. })));
}
#[test]
fn load_malformed_toml_returns_catalog_parse_error() {
let file = write_temp_toml("this is not valid = = toml [[[");
let result = Catalog::load(file.as_str());
assert!(matches!(result, Err(EngineError::CatalogParse { .. })));
}
#[test]
fn load_or_fallback_never_panics_on_missing_file() {
let catalog = Catalog::load_or_fallback("/definitely/does/not/exist/errors.toml");
let rendered = catalog.render("ANY_CODE", &[]);
assert!(rendered.contains(UNKNOWN_CODE));
}
#[test]
fn render_with_load_error_returns_unk_000_for_any_code() {
let catalog = Catalog::load_or_fallback("/definitely/does/not/exist/errors.toml");
let rendered = catalog.render("CONFIG_NOT_FOUND", &[]);
assert!(rendered.contains(UNKNOWN_CODE));
assert!(rendered.contains("catalog failed to load"));
}
#[test]
fn render_unknown_code_on_valid_catalog_returns_unk_000() {
let file = write_temp_toml(VALID_CATALOG);
let catalog = Catalog::load(file.as_str()).expect("valid catalog should load");
let rendered = catalog.render("NOT_A_REAL_CODE", &[]);
assert!(rendered.contains(UNKNOWN_CODE));
assert!(rendered.contains("NOT_A_REAL_CODE"));
}
#[test]
fn placeholder_with_no_context_value_stays_literal() {
let file = write_temp_toml(VALID_CATALOG);
let catalog = Catalog::load(file.as_str()).expect("valid catalog should load");
let rendered = catalog.render("DB_CONN_FAILED", &[]);
assert_eq!(rendered, "Could not connect to the database: {reason}");
}
#[test]
fn hint_returns_none_on_load_failure() {
let catalog = Catalog::load_or_fallback("/definitely/does/not/exist/errors.toml");
assert_eq!(catalog.hint("CONFIG_NOT_FOUND"), None);
}
#[test]
fn hint_returns_none_on_missing_code() {
let file = write_temp_toml(VALID_CATALOG);
let catalog = Catalog::load(file.as_str()).expect("valid catalog should load");
assert_eq!(catalog.hint("NOT_A_REAL_CODE"), None);
}
#[test]
fn hint_returns_value_when_present() {
let file = write_temp_toml(VALID_CATALOG);
let catalog = Catalog::load(file.as_str()).expect("valid catalog should load");
assert_eq!(catalog.hint("CONFIG_NOT_FOUND"), Some("Check that the path is correct"));
}
#[test]
fn from_str_parses_valid_toml_and_renders() {
let catalog = Catalog::from_str(VALID_CATALOG).expect("valid TOML should parse");
let rendered = catalog.render("DB_CONN_FAILED", &[("reason", "timeout".to_string())]);
assert_eq!(rendered, "Could not connect to the database: timeout");
}
#[test]
fn from_str_malformed_toml_returns_catalog_parse_error() {
let result = Catalog::from_str("this is not valid = = toml [[[");
assert!(matches!(result, Err(EngineError::CatalogParse { .. })));
}
#[test]
fn merged_with_app_catalog_overrides_library_catalog_on_collision() {
let app_catalog = Catalog::from_str(
r#"
[CONFIG_NOT_FOUND]
template = "App-customized: config missing at {path}"
"#,
)
.unwrap();
let lib_catalog = Catalog::from_str(VALID_CATALOG).unwrap();
let merged = app_catalog.merged_with(lib_catalog);
let rendered = merged.render("CONFIG_NOT_FOUND", &[("path", "/etc/app.toml".to_string())]);
assert_eq!(rendered, "App-customized: config missing at /etc/app.toml");
}
#[test]
fn merged_with_falls_through_to_other_for_codes_not_in_self() {
let app_catalog = Catalog::from_str("[APP_ONLY_CODE]\ntemplate = \"app-specific\"").unwrap();
let lib_catalog = Catalog::from_str(VALID_CATALOG).unwrap();
let merged = app_catalog.merged_with(lib_catalog);
let rendered = merged.render("DB_CONN_FAILED", &[("reason", "timeout".to_string())]);
assert_eq!(rendered, "Could not connect to the database: timeout");
}
#[test]
fn merged_with_preserves_self_load_error_and_ignores_other_entries() {
let broken_app_catalog = Catalog::load_or_fallback("/definitely/does/not/exist/errors.toml");
let lib_catalog = Catalog::from_str(VALID_CATALOG).unwrap();
let merged = broken_app_catalog.merged_with(lib_catalog);
let rendered = merged.render("DB_CONN_FAILED", &[]);
assert!(rendered.contains(UNKNOWN_CODE));
assert!(rendered.contains("catalog failed to load"));
}