#![allow(clippy::unwrap_used)]
#![allow(clippy::expect_used)]
use super::{export_graph, ExportFormat, ExportOptions};
use ggen_core::graph::Graph;
use std::fs;
use tempfile::tempdir;
#[allow(clippy::expect_used)]
#[test]
fn test_export_error_invalid_output_path() {
let invalid_path = "/nonexistent/path/that/does/not/exist/output.ttl";
let graph = Graph::new().expect("Failed to create graph");
graph
.insert_turtle(
r#"
@prefix ex: <http://example.org/> .
ex:test a ex:Test .
"#,
)
.unwrap();
let options = ExportOptions {
output_path: invalid_path.to_string(),
format: ExportFormat::Turtle,
pretty: false,
graph: Some(graph),
};
let result = export_graph(options);
assert!(result.is_err(), "Should fail for invalid output path");
if let Err(e) = result {
let error_msg = e.to_string();
assert!(
error_msg.contains("Failed")
|| error_msg.contains("path")
|| error_msg.contains("directory"),
"Error should mention path issue: {}",
error_msg
);
}
}
#[allow(clippy::expect_used)]
#[test]
fn test_export_error_permission_denied() {
let temp_dir = tempdir().unwrap();
let output_path = temp_dir.path().join("output.ttl");
let graph = Graph::new().expect("Failed to create graph");
let options = ExportOptions {
output_path: output_path.to_string_lossy().to_string(),
format: ExportFormat::Turtle,
pretty: false,
graph: Some(graph),
};
let result = export_graph(options);
assert!(
result.is_ok() || result.is_err(),
"Export should either succeed or fail gracefully"
);
}
#[allow(clippy::expect_used)]
#[test]
fn test_export_error_unsupported_format() {
let temp_dir = tempdir().unwrap();
let output_path = temp_dir.path().join("output.jsonld");
let graph = Graph::new().expect("Failed to create graph");
let options = ExportOptions {
output_path: output_path.to_string_lossy().to_string(),
format: ExportFormat::JsonLd,
pretty: false,
graph: Some(graph),
};
let result = export_graph(options);
assert!(result.is_err(), "Should fail for unsupported format");
if let Err(e) = result {
let error_msg = e.to_string();
assert!(
error_msg.contains("JSON-LD") || error_msg.contains("not yet supported"),
"Error should mention unsupported format: {}",
error_msg
);
}
}
#[allow(clippy::expect_used)]
#[test]
fn test_export_boundary_empty_graph() {
let temp_dir = tempdir().unwrap();
let output_path = temp_dir.path().join("empty.ttl");
let graph = Graph::new().expect("Failed to create graph");
let options = ExportOptions {
output_path: output_path.to_string_lossy().to_string(),
format: ExportFormat::Turtle,
pretty: false,
graph: Some(graph),
};
let result = export_graph(options);
assert!(result.is_ok(), "Should handle empty graph");
if let Ok(content) = result {
assert!(
content.is_empty() || content.len() < 100,
"Empty graph should produce minimal output"
);
}
}
#[allow(clippy::expect_used)]
#[test]
fn test_export_boundary_very_large_graph() {
let temp_dir = tempdir().unwrap();
let output_path = temp_dir.path().join("large.ttl");
let graph = Graph::new().expect("Failed to create graph");
for i in 0..100 {
let turtle = format!(
r#"
@prefix ex: <http://example.org/> .
ex:subject{} a ex:Test ;
ex:index {} .
"#,
i, i
);
graph.insert_turtle(&turtle).unwrap();
}
let options = ExportOptions {
output_path: output_path.to_string_lossy().to_string(),
format: ExportFormat::Turtle,
pretty: false,
graph: Some(graph),
};
let result = export_graph(options);
assert!(result.is_ok(), "Should handle large graph");
if let Ok(content) = result {
assert!(!content.is_empty(), "Large graph should produce output");
assert!(
content.contains("subject") || content.contains("Test"),
"Output should contain graph data"
);
}
}
#[allow(clippy::expect_used)]
#[test]
fn test_export_boundary_max_path_length() {
let temp_dir = tempdir().unwrap();
let long_name = "a".repeat(200);
let output_path = temp_dir.path().join(format!("{}.ttl", long_name));
let graph = Graph::new().expect("Failed to create graph");
graph
.insert_turtle(
r#"
@prefix ex: <http://example.org/> .
ex:test a ex:Test .
"#,
)
.unwrap();
let options = ExportOptions {
output_path: output_path.to_string_lossy().to_string(),
format: ExportFormat::Turtle,
pretty: false,
graph: Some(graph),
};
let result = export_graph(options);
assert!(
result.is_ok() || result.is_err(),
"Should handle long path without panic"
);
}
#[allow(clippy::expect_used)]
#[test]
fn test_export_resource_cleanup_temp_files() {
let temp_dir = tempdir().unwrap();
let output_path = temp_dir.path().join("output.ttl");
let graph = Graph::new().expect("Failed to create graph");
graph
.insert_turtle(
r#"
@prefix ex: <http://example.org/> .
ex:test a ex:Test .
"#,
)
.unwrap();
let options = ExportOptions {
output_path: output_path.to_string_lossy().to_string(),
format: ExportFormat::Turtle,
pretty: false,
graph: Some(graph),
};
let result = export_graph(options);
assert!(result.is_ok(), "Export should succeed");
let temp_files: Vec<_> = temp_dir
.path()
.read_dir()
.unwrap()
.filter_map(|e| e.ok())
.filter(|e| {
e.path()
.file_name()
.and_then(|n| n.to_str())
.map(|n| n.contains(".tmp") || n.contains(".temp"))
.unwrap_or(false)
})
.collect();
assert!(
temp_files.len() <= 1,
"Temp files should be cleaned up: found {:?}",
temp_files
);
}
#[allow(clippy::expect_used)]
#[test]
fn test_export_resource_cleanup_file_handles() {
let temp_dir = tempdir().unwrap();
let output_path = temp_dir.path().join("output.ttl");
let graph = Graph::new().expect("Failed to create graph");
graph
.insert_turtle(
r#"
@prefix ex: <http://example.org/> .
ex:test a ex:Test .
"#,
)
.unwrap();
for i in 0..5 {
let options = ExportOptions {
output_path: output_path.to_string_lossy().to_string(),
format: ExportFormat::Turtle,
pretty: false,
graph: Some(graph.clone()),
};
let result = export_graph(options);
assert!(result.is_ok(), "Export {} should succeed", i);
}
let content = fs::read_to_string(&output_path).unwrap();
assert!(!content.is_empty(), "File should be readable after exports");
}
#[tokio::test]
async fn test_export_concurrent_writes() {
use std::sync::Arc;
use tokio::sync::Barrier;
let temp_dir = tempdir().unwrap();
let barrier = Arc::new(Barrier::new(5));
let mut handles = vec![];
for i in 0..5 {
let temp_dir = temp_dir.path().to_path_buf();
let barrier = Arc::clone(&barrier);
let handle = tokio::spawn(async move {
barrier.wait().await;
let output_path = temp_dir.join(format!("output{}.ttl", i));
let graph = Graph::new().expect("Failed to create graph");
graph
.insert_turtle(&format!(
r#"
@prefix ex: <http://example.org/> .
ex:test{} a ex:Test .
"#,
i
))
.unwrap();
let options = ExportOptions {
output_path: output_path.to_string_lossy().to_string(),
format: ExportFormat::Turtle,
pretty: false,
graph: Some(graph),
};
export_graph(options)
});
handles.push(handle);
}
let mut results = vec![];
for handle in handles {
results.push(handle.await);
}
let success_count = results
.iter()
.filter(|r| r.is_ok() && r.as_ref().unwrap().is_ok())
.count();
assert_eq!(success_count, 5, "All concurrent exports should succeed");
for i in 0..5 {
let output_path = temp_dir.path().join(format!("output{}.ttl", i));
assert!(output_path.exists(), "File {} should exist", i);
}
}
#[allow(clippy::expect_used)]
#[test]
fn test_export_error_recovery_after_failure() {
let temp_dir = tempdir().unwrap();
let invalid_path = "/nonexistent/path/output.ttl";
let graph = Graph::new().expect("Failed to create graph");
graph
.insert_turtle(
r#"
@prefix ex: <http://example.org/> .
ex:test a ex:Test .
"#,
)
.unwrap();
let invalid_options = ExportOptions {
output_path: invalid_path.to_string(),
format: ExportFormat::Turtle,
pretty: false,
graph: Some(graph.clone()),
};
let result1 = export_graph(invalid_options);
assert!(result1.is_err(), "First export should fail");
let valid_path = temp_dir.path().join("output.ttl");
let valid_options = ExportOptions {
output_path: valid_path.to_string_lossy().to_string(),
format: ExportFormat::Turtle,
pretty: false,
graph: Some(graph),
};
let result2 = export_graph(valid_options);
assert!(
result2.is_ok(),
"Retry should succeed after initial failure"
);
assert!(
temp_dir.path().join("output.ttl").exists(),
"Output file should exist after successful retry"
);
}