use crate::Result;
use serde::de::DeserializeOwned;
use std::path::Path;
use tracing::{debug, warn};
pub fn load_fixtures_from_dir<T: DeserializeOwned>(
dir: impl AsRef<Path>,
missing_dir_ok: bool,
) -> Result<Vec<T>> {
let dir = dir.as_ref();
if !dir.exists() {
if missing_dir_ok {
debug!("Fixtures directory does not exist: {:?}", dir);
return Ok(Vec::new());
}
return Err(crate::Error::internal(format!(
"Fixtures directory does not exist: {}",
dir.display()
)));
}
let mut fixtures = Vec::new();
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if !path.is_file() {
continue;
}
let extension = path.extension().and_then(|s| s.to_str());
match extension {
Some("yaml") | Some("yml") | Some("json") => match load_fixture_file::<T>(&path) {
Ok(fixture) => fixtures.push(fixture),
Err(e) => {
warn!("Failed to load fixture from {:?}: {}", path, e);
}
},
_ => {
debug!("Skipping non-fixture file: {:?}", path);
}
}
}
Ok(fixtures)
}
pub fn load_fixture_list_from_dir<T: DeserializeOwned>(
dir: impl AsRef<Path>,
missing_dir_ok: bool,
) -> Result<Vec<T>> {
let dir = dir.as_ref();
if !dir.exists() {
if missing_dir_ok {
debug!("Fixtures directory does not exist: {:?}", dir);
return Ok(Vec::new());
}
return Err(crate::Error::internal(format!(
"Fixtures directory does not exist: {}",
dir.display()
)));
}
let mut fixtures = Vec::new();
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if !path.is_file() {
continue;
}
let extension = path.extension().and_then(|s| s.to_str());
match extension {
Some("yaml") | Some("yml") | Some("json") => match load_fixture_file::<Vec<T>>(&path) {
Ok(file_fixtures) => fixtures.extend(file_fixtures),
Err(e) => {
warn!("Failed to load fixtures from {:?}: {}", path, e);
}
},
_ => {
debug!("Skipping non-fixture file: {:?}", path);
}
}
}
Ok(fixtures)
}
fn load_fixture_file<T: DeserializeOwned>(path: &Path) -> Result<T> {
debug!("Loading fixture from: {:?}", path);
let content = std::fs::read_to_string(path)?;
let extension = path.extension().and_then(|s| s.to_str());
let fixture: T = if extension == Some("json") {
serde_json::from_str(&content).map_err(|e| {
crate::Error::internal(format!("Failed to parse JSON fixture {:?}: {}", path, e))
})?
} else {
serde_yaml::from_str(&content).map_err(|e| {
crate::Error::internal(format!("Failed to parse YAML fixture {:?}: {}", path, e))
})?
};
Ok(fixture)
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
use tempfile::TempDir;
#[derive(Debug, Clone, Deserialize, PartialEq)]
struct TestFixture {
identifier: String,
name: String,
value: Option<i32>,
}
#[test]
fn test_load_fixtures_from_dir_empty() {
let temp_dir = TempDir::new().unwrap();
let fixtures: Vec<TestFixture> = load_fixtures_from_dir(temp_dir.path(), true).unwrap();
assert!(fixtures.is_empty());
}
#[test]
fn test_load_fixtures_from_dir_nonexistent_ok() {
let fixtures: Vec<TestFixture> = load_fixtures_from_dir("/nonexistent/path", true).unwrap();
assert!(fixtures.is_empty());
}
#[test]
fn test_load_fixtures_from_dir_nonexistent_error() {
let result: Result<Vec<TestFixture>> = load_fixtures_from_dir("/nonexistent/path", false);
assert!(result.is_err());
}
#[test]
fn test_load_fixtures_from_dir_yaml() {
let temp_dir = TempDir::new().unwrap();
let yaml_content = "identifier: test-1\nname: Test Fixture\nvalue: 42\n";
std::fs::write(temp_dir.path().join("fixture.yaml"), yaml_content).unwrap();
let fixtures: Vec<TestFixture> = load_fixtures_from_dir(temp_dir.path(), true).unwrap();
assert_eq!(fixtures.len(), 1);
assert_eq!(fixtures[0].identifier, "test-1");
assert_eq!(fixtures[0].value, Some(42));
}
#[test]
fn test_load_fixtures_from_dir_yml() {
let temp_dir = TempDir::new().unwrap();
let yaml_content = "identifier: test-yml\nname: YML Fixture\n";
std::fs::write(temp_dir.path().join("fixture.yml"), yaml_content).unwrap();
let fixtures: Vec<TestFixture> = load_fixtures_from_dir(temp_dir.path(), true).unwrap();
assert_eq!(fixtures.len(), 1);
assert_eq!(fixtures[0].identifier, "test-yml");
}
#[test]
fn test_load_fixtures_from_dir_json() {
let temp_dir = TempDir::new().unwrap();
let json_content = r#"{"identifier": "test-json", "name": "JSON Fixture", "value": 99}"#;
std::fs::write(temp_dir.path().join("fixture.json"), json_content).unwrap();
let fixtures: Vec<TestFixture> = load_fixtures_from_dir(temp_dir.path(), true).unwrap();
assert_eq!(fixtures.len(), 1);
assert_eq!(fixtures[0].identifier, "test-json");
assert_eq!(fixtures[0].value, Some(99));
}
#[test]
fn test_load_fixtures_from_dir_mixed() {
let temp_dir = TempDir::new().unwrap();
std::fs::write(temp_dir.path().join("a.yaml"), "identifier: yaml-fix\nname: YAML\n")
.unwrap();
std::fs::write(
temp_dir.path().join("b.json"),
r#"{"identifier": "json-fix", "name": "JSON"}"#,
)
.unwrap();
std::fs::write(temp_dir.path().join("c.txt"), "not a fixture").unwrap();
let fixtures: Vec<TestFixture> = load_fixtures_from_dir(temp_dir.path(), true).unwrap();
assert_eq!(fixtures.len(), 2);
}
#[test]
fn test_load_fixtures_from_dir_invalid_files_skipped() {
let temp_dir = TempDir::new().unwrap();
std::fs::write(temp_dir.path().join("valid.yaml"), "identifier: valid\nname: Valid\n")
.unwrap();
std::fs::write(temp_dir.path().join("invalid.yaml"), "this is not valid yaml: [unclosed")
.unwrap();
std::fs::write(temp_dir.path().join("invalid.json"), "{invalid json}").unwrap();
let fixtures: Vec<TestFixture> = load_fixtures_from_dir(temp_dir.path(), true).unwrap();
assert_eq!(fixtures.len(), 1);
assert_eq!(fixtures[0].identifier, "valid");
}
#[test]
fn test_load_fixture_list_from_dir() {
let temp_dir = TempDir::new().unwrap();
let yaml_content =
"- identifier: list-1\n name: First\n- identifier: list-2\n name: Second\n";
std::fs::write(temp_dir.path().join("fixtures.yaml"), yaml_content).unwrap();
let fixtures: Vec<TestFixture> = load_fixture_list_from_dir(temp_dir.path(), true).unwrap();
assert_eq!(fixtures.len(), 2);
assert_eq!(fixtures[0].identifier, "list-1");
assert_eq!(fixtures[1].identifier, "list-2");
}
#[test]
fn test_load_fixture_list_from_dir_multiple_files() {
let temp_dir = TempDir::new().unwrap();
std::fs::write(temp_dir.path().join("file1.yaml"), "- identifier: a\n name: A\n").unwrap();
std::fs::write(temp_dir.path().join("file2.yaml"), "- identifier: b\n name: B\n").unwrap();
let fixtures: Vec<TestFixture> = load_fixture_list_from_dir(temp_dir.path(), true).unwrap();
assert_eq!(fixtures.len(), 2);
}
#[test]
fn test_load_fixture_list_from_dir_nonexistent_ok() {
let fixtures: Vec<TestFixture> =
load_fixture_list_from_dir("/nonexistent/path", true).unwrap();
assert!(fixtures.is_empty());
}
#[test]
fn test_load_fixture_list_from_dir_nonexistent_error() {
let result: Result<Vec<TestFixture>> =
load_fixture_list_from_dir("/nonexistent/path", false);
assert!(result.is_err());
}
#[test]
fn test_load_fixtures_skips_directories() {
let temp_dir = TempDir::new().unwrap();
std::fs::create_dir(temp_dir.path().join("subdir")).unwrap();
std::fs::write(temp_dir.path().join("fixture.yaml"), "identifier: test\nname: Test\n")
.unwrap();
let fixtures: Vec<TestFixture> = load_fixtures_from_dir(temp_dir.path(), true).unwrap();
assert_eq!(fixtures.len(), 1);
}
}