use sqlitegraph::backend::native::{GraphFile, NativeBackendError};
use std::io::Write;
use tempfile::NamedTempFile;
#[test]
fn test_read_bytes_direct_file_size_invariant() -> Result<(), Box<dyn std::error::Error>> {
let mut temp_file = NamedTempFile::new()?;
let test_data = b"Hello, World!"; temp_file.write_all(test_data)?;
temp_file.flush()?;
let result = GraphFile::open(temp_file.path());
assert!(result.is_err(), "Opening tiny file should fail");
if let Err(error) = result {
match error {
NativeBackendError::FileTooSmall { size, min_size } => {
assert_eq!(size, 13, "Should report actual file size");
assert_eq!(
min_size, 80,
"Should report minimum required size (HEADER_SIZE)"
);
}
other => panic!("Expected FileTooSmall error, got: {:?}", other),
}
}
let temp_file = NamedTempFile::new()?;
let graph_file = GraphFile::create(temp_file.path())?;
assert!(
graph_file.file_size()? >= 80,
"Created file should meet minimum size requirements"
);
let mut graph_file = GraphFile::open(temp_file.path())?;
let mut buffer = vec![0u8; 10];
let result = graph_file.read_bytes(0, &mut buffer);
assert!(
result.is_ok(),
"Valid read on proper file should succeed: {:?}",
result
);
let result = graph_file.read_bytes(40, &mut buffer);
assert!(result.is_ok(), "Reading at valid offset should succeed");
Ok(())
}
#[test]
fn test_read_header_file_size_invariant() -> Result<(), Box<dyn std::error::Error>> {
let temp_file = NamedTempFile::new()?;
let result = GraphFile::open(temp_file.path());
assert!(result.is_err(), "Opening empty file should fail");
if let Err(error) = result {
match error {
NativeBackendError::FileTooSmall { size, min_size } => {
assert_eq!(size, 0, "Should report actual file size as 0");
assert_eq!(
min_size, 80,
"Should report minimum required size (HEADER_SIZE)"
);
}
other => panic!(
"Expected FileTooSmall error for empty file, got: {:?}",
other
),
}
}
Ok(())
}
#[test]
fn test_read_edge_at_offset_file_size_invariant() -> Result<(), Box<dyn std::error::Error>> {
let temp_file = NamedTempFile::new()?;
let mut graph_file = GraphFile::create(temp_file.path())?;
let result = graph_file.read_edge_at_offset(1000000); assert!(
result.is_err(),
"Reading edge beyond file should return error"
);
if let Err(error) = result {
match error {
NativeBackendError::Io(_)
| NativeBackendError::CorruptNodeRecord { .. }
| NativeBackendError::InvalidHeader { .. } => {
}
other => panic!(
"Expected I/O, CorruptNodeRecord, or InvalidHeader error for reading beyond file, got: {:?}",
other
),
}
}
Ok(())
}
#[test]
fn test_detailed_error_message_format() -> Result<(), Box<dyn std::error::Error>> {
let mut temp_file = NamedTempFile::new()?;
temp_file.as_file().write_all(&vec![0u8; 50])?;
temp_file.flush()?;
let result = GraphFile::open(temp_file.path());
assert!(result.is_err());
if let Err(error) = result {
let error_msg = format!("{}", error);
assert!(
error_msg.contains("50") || error_msg.contains("size"),
"Error should mention file size: {}",
error_msg
);
}
Ok(())
}
#[test]
fn test_invariant_prevents_failed_to_fill_whole_buffer() -> Result<(), Box<dyn std::error::Error>> {
let temp_file = NamedTempFile::new()?;
let result = GraphFile::open(temp_file.path());
assert!(result.is_err());
if let Err(error) = result {
let error_str = error.to_string();
assert!(error_str.contains("File too small"));
assert!(error_str.contains("0 bytes"));
assert!(error_str.contains("80 bytes"));
assert!(!error_str.contains("failed to fill whole buffer"));
}
Ok(())
}