Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions src/assertions/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,39 @@ pub fn check_file_content(
}

/// Check if workspace file exists
pub fn check_workspace_file(work_dir: &Path, file_path: &str) -> Result<(), String> {
pub fn check_workspace_file(
work_dir: &Path,
file_path: &str,
copy_to_output: bool,
log_output_dir: Option<&Path>,
) -> Result<(), String> {
let full_path = work_dir.join(file_path);

if full_path.exists() && full_path.is_file() {
Ok(())
} else {
Err(format!("Workspace file '{}' does not exist", file_path))
if !(full_path.exists() && full_path.is_file()) {
return Err(format!("Workspace file '{}' does not exist", file_path));
}

// Copy file to output directory if requested
if copy_to_output {
if let Some(output_dir) = log_output_dir {
if let Err(e) = copy_file_to_dir(&full_path, file_path, output_dir) {
return Err(format!("Failed to copy '{}' to output: {}", file_path, e));
}
}
}

Ok(())
}

/// Copy a file to the output directory, preserving its relative path structure
fn copy_file_to_dir(source: &Path, relative_path: &str, output_dir: &Path) -> std::io::Result<()> {
std::fs::create_dir_all(output_dir)?;
let dest = output_dir.join(relative_path);
if let Some(parent) = dest.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::copy(source, &dest)?;
Ok(())
}

/// Check if workspace directory exists
Expand Down
11 changes: 9 additions & 2 deletions src/assertions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,17 @@ pub struct AssertionChecker {
#[cfg(not(test))]
log_data: Vec<Value>,
work_dir: std::path::PathBuf,
log_output_dir: Option<std::path::PathBuf>,
}

impl AssertionChecker {
/// Create a new assertion checker
pub fn new(log_file: &Path, work_dir: &Path) -> Self {
pub fn new(log_file: &Path, work_dir: &Path, log_output_dir: Option<&Path>) -> Self {
let log_data = Self::load_log_file(log_file);
Self {
log_data,
work_dir: work_dir.to_path_buf(),
log_output_dir: log_output_dir.map(|p| p.to_path_buf()),
}
}

Expand Down Expand Up @@ -71,7 +73,12 @@ impl AssertionChecker {
let result = match cmd.as_str() {
"workspace-file" => {
let path = check.command.path.as_ref().ok_or("Missing path")?;
file::check_workspace_file(&self.work_dir, path)
file::check_workspace_file(
&self.work_dir,
path,
check.command.copy_to_output.unwrap_or(false),
self.log_output_dir.as_deref(),
)
}
"workspace-dir" => {
let path = check.command.path.as_ref().ok_or("Missing path")?;
Expand Down
21 changes: 15 additions & 6 deletions src/assertions/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ mod tests {
fn create_checker(log_file: &str) -> AssertionChecker {
let log_path = Path::new(log_file);
let work_dir = tempfile::tempdir().unwrap();
AssertionChecker::new(log_path, work_dir.path())
AssertionChecker::new(log_path, work_dir.path(), None)
}

#[test]
Expand Down Expand Up @@ -132,11 +132,20 @@ mod tests {
let test_file = work_dir.path().join("output.txt");
std::fs::write(&test_file, "test content").unwrap();

let result = crate::assertions::file::check_workspace_file(work_dir.path(), "output.txt");
let result = crate::assertions::file::check_workspace_file(
work_dir.path(),
"output.txt",
false,
None,
);
assert!(result.is_ok(), "workspace file should exist");

let result =
crate::assertions::file::check_workspace_file(work_dir.path(), "nonexistent.txt");
let result = crate::assertions::file::check_workspace_file(
work_dir.path(),
"nonexistent.txt",
false,
None,
);
assert!(result.is_err(), "nonexistent file should not exist");
}

Expand Down Expand Up @@ -188,7 +197,7 @@ mod tests {
let empty_log = work_dir.path().join("empty.log");
std::fs::write(&empty_log, "").unwrap();

let checker = AssertionChecker::new(&empty_log, work_dir.path());
let checker = AssertionChecker::new(&empty_log, work_dir.path(), None);
let init = checker.init_message();
assert!(init.is_none(), "empty log should not have init message");
}
Expand Down Expand Up @@ -244,7 +253,7 @@ mod tests {
let nonexistent_log = work_dir.path().join("nonexistent.log");

// Should not panic, just return empty checker
let checker = AssertionChecker::new(&nonexistent_log, work_dir.path());
let checker = AssertionChecker::new(&nonexistent_log, work_dir.path(), None);
assert_eq!(
checker.log_data.len(),
0,
Expand Down
2 changes: 2 additions & 0 deletions src/models/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,6 @@ pub struct CheckData {
pub query: Option<String>,
#[serde(default)]
pub deny: Option<bool>,
#[serde(default)]
pub copy_to_output: Option<bool>,
}
3 changes: 2 additions & 1 deletion src/runtime/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ impl TestExecutor {
}

// Run assertions
let checker = AssertionChecker::new(&log_path, workspace.path());
let checker =
AssertionChecker::new(&log_path, workspace.path(), self.log_output_dir.as_deref());
let check_results: Vec<CheckResult> = desc
.test
.checks
Expand Down
Loading