use super::*;
use std::os::unix::ffi::OsStrExt as _;
#[test]
fn existing_parent_selection_rejects_missing_and_directory_targets_before_production() {
let fixture = Fixture::new();
for path in [
fixture.root.join("missing/report"),
fixture.root.join("directory/"),
fixture.root.join("directory/."),
fixture.root.join(OsStr::from_bytes(b"nul\0name")),
] {
let mut produced = false;
let result = write_existing_parent_with(&path, CREATE, |_| {
produced = true;
Ok::<_, io::Error>(())
});
assert!(matches!(
result,
Err(NamedWriteError::BeforePublication { .. })
));
assert!(!produced);
}
assert_eq!(fs::read_dir(&fixture.root).unwrap().count(), 0);
}
#[test]
fn existing_parent_modes_keep_permissions_symlinks_and_typed_admission() {
let fixture = Fixture::new();
let output = fixture.root.join("report");
write_existing_parent_with(&output, CREATE, |file| file.write_all(b"first")).unwrap();
assert_eq!(
fs::metadata(&output).unwrap().permissions().mode() & 0o777,
0o600
);
let rejected = write_existing_parent_validated_with(
&output,
REPLACE,
|file| {
file.write_all(b"rejected").map_err(|_| 7_u8)?;
Ok(42_u8)
},
|value| {
assert_eq!(*value, 42);
Err(9_u8)
},
);
assert!(matches!(
rejected,
Err(NamedWriteError::Producer {
source: 9,
cleanup_error: None
})
));
assert_eq!(fs::read(&output).unwrap(), b"first");
assert!(
write_existing_parent_with(&output, CREATE, |file| file.write_all(b"duplicate")).is_err()
);
assert_eq!(fs::read(&output).unwrap(), b"first");
let alias = fixture.root.join("parent alias");
symlink(&fixture.root, &alias).unwrap();
write_existing_parent_validated_with(
&alias.join("report"),
REPLACE,
|file| {
file.write_all(b"accepted")?;
Ok::<_, io::Error>(42)
},
|value| {
assert_eq!(*value, 42);
Ok(())
},
)
.unwrap();
assert_eq!(fs::read(&output).unwrap(), b"accepted");
let link = fixture.root.join("final-link");
symlink(&output, &link).unwrap();
assert!(
write_existing_parent_with(&link, CREATE, |file| file.write_all(b"duplicate")).is_err()
);
assert!(
fs::symlink_metadata(&link)
.unwrap()
.file_type()
.is_symlink()
);
assert_eq!(fs::read(&output).unwrap(), b"accepted");
write_existing_parent_with(&link, REPLACE, |file| file.write_all(b"link replacement")).unwrap();
assert!(fs::symlink_metadata(&link).unwrap().is_file());
assert_eq!(fs::read(&link).unwrap(), b"link replacement");
assert_eq!(fs::read(&output).unwrap(), b"accepted");
}
#[test]
fn existing_parent_remains_selected_after_path_rename() {
let fixture = Fixture::new();
let original = fixture.root.join("parent");
let renamed = fixture.root.join("renamed");
fs::create_dir(&original).unwrap();
write_existing_parent_with(&original.join("report"), CREATE, |file| {
fs::rename(&original, &renamed)?;
fs::create_dir(&original)?;
file.write_all(b"retained directory")
})
.unwrap();
assert_eq!(
fs::read(renamed.join("report")).unwrap(),
b"retained directory"
);
assert!(!original.join("report").exists());
}
#[test]
fn existing_parent_refuses_invalid_permissions_before_production_or_admission() {
let fixture = Fixture::new();
let output = fixture.root.join("report");
let error = write_existing_parent_validated_with(
&output,
WriteOptions {
permissions: 0o1000,
..CREATE
},
|_| -> io::Result<()> { panic!("invalid permissions ran producer") },
|()| panic!("invalid permissions ran admission"),
)
.unwrap_err();
assert!(
matches!(error, NamedWriteError::BeforePublication { source, cleanup_error: None }
if source.kind() == io::ErrorKind::InvalidInput)
);
assert_eq!(fs::read_dir(&fixture.root).unwrap().count(), 0);
}
#[test]
fn existing_parent_refuses_non_directories_without_blocking() {
use std::{
process::Command,
time::{Duration, Instant},
};
const CHILD: &str = "IC_HOST_FS_EXISTING_PARENT_CHILD";
if let Some(parent) = std::env::var_os(CHILD) {
let path = std::path::PathBuf::from(parent).join("report");
let ordinary = write_existing_parent_with(&path, CREATE, |_| -> io::Result<()> {
panic!("non-directory parent ran producer")
});
let validated = write_existing_parent_validated_with(
&path,
CREATE,
|_| -> io::Result<()> { panic!("non-directory parent ran producer") },
|()| panic!("non-directory parent ran admission"),
);
for result in [ordinary, validated] {
assert!(
matches!(result, Err(NamedWriteError::BeforePublication { source, cleanup_error: None })
if source.kind() == io::ErrorKind::NotADirectory)
);
}
return;
}
let fixture = Fixture::new();
let regular = fixture.root.join("regular");
fs::write(®ular, b"preserved").unwrap();
let fifo = fixture.root.join("fifo");
crate::test_support::create_fifo(&fifo, None).unwrap();
for parent in [&fifo, ®ular] {
let mut child = Command::new(std::env::current_exe().unwrap())
.args(["--exact", "durable::stream::tests::existing_parent::existing_parent_refuses_non_directories_without_blocking", "--test-threads=1"])
.env(CHILD, parent)
.spawn().unwrap();
let started = Instant::now();
let result = loop {
match child.try_wait() {
Ok(Some(status)) => break Ok(status),
Err(error) => break Err(error),
Ok(None) => {}
}
if started.elapsed() >= Duration::from_secs(5) {
break Err(io::Error::from(io::ErrorKind::TimedOut));
}
std::thread::sleep(Duration::from_millis(10));
};
if result.is_err() {
let _ = child.kill();
let _ = child.wait();
}
assert!(result.expect("parent admission must not block").success());
}
assert_eq!(fs::read(®ular).unwrap(), b"preserved");
assert_eq!(fs::read_dir(&fixture.root).unwrap().count(), 2);
}