test(settings): respect effective filesystem permissions

Probe effective write restrictions so privileged container runs do not
assert errors the OS cannot provide, while retaining unprivileged coverage.

Closes #46

Co-Authored-By: OpenAI Codex <noreply@openai.com>
This commit is contained in:
euisuhandOpenAI Codex committed 2026-10-14 16:03:59 +09:00
1 parent 457a078809
commit 11cbf52ea7
4 files changed
+35 -4

No files matched your search

+18
View File
@@ -94,3 +94,21 @@ pub fn write_config(dir: &Path, text: &str) {
pub fn read_config(dir: &Path) -> String {
std::fs::read_to_string(config_path(dir)).unwrap()
}
#[cfg(unix)]
pub fn restrict_directory_writes(dir: &Path) -> bool {
use std::os::unix::fs::PermissionsExt;
let permissions = std::fs::metadata(dir).unwrap().permissions();
std::fs::set_permissions(dir, std::fs::Permissions::from_mode(0o500)).unwrap();
match tempfile::NamedTempFile::new_in(dir) {
Ok(probe) => {
drop(probe);
std::fs::set_permissions(dir, permissions).unwrap();
eprintln!("skipping permission test: environment permits writes into a 0500 directory");
false
}
Err(error) if error.kind() == std::io::ErrorKind::PermissionDenied => true,
Err(error) => panic!("unexpected directory write-probe failure: {error}"),
}
}
+3 -1
View File
@@ -134,7 +134,9 @@ fn unwritable_folder_is_reported_at_open_not_first_save() {
let tmp = tempfile::tempdir().unwrap();
let dir = tmp.path().join("ro");
std::fs::create_dir(&dir).unwrap();
std::fs::set_permissions(&dir, std::fs::Permissions::from_mode(0o500)).unwrap();
if !common::restrict_directory_writes(&dir) {
return;
}
let (mut store, report) = open_in(&dir);
std::fs::set_permissions(&dir, std::fs::Permissions::from_mode(0o700)).unwrap();
assert!(store.is_read_only());
+6 -3
View File
@@ -77,9 +77,10 @@ fn damaged_file_that_cannot_be_moved_is_left_alone() {
use std::os::unix::fs::PermissionsExt;
let tmp = tempfile::tempdir().unwrap();
write_config(tmp.path(), "not = [toml");
// The folder passes the write probe only while it is writable, so open first, then lock it.
let dir = tmp.path();
std::fs::set_permissions(dir, std::fs::Permissions::from_mode(0o500)).unwrap();
if !common::restrict_directory_writes(dir) {
return;
}
let (mut store, report) = open_in(dir);
std::fs::set_permissions(dir, std::fs::Permissions::from_mode(0o700)).unwrap();
assert!(store.is_read_only(), "{report:?}");
@@ -152,7 +153,9 @@ fn failed_migration_in_an_unwritable_folder_shows_defaults() {
let tmp = tempfile::tempdir().unwrap();
// Version 0 has no migration step, so upgrading fails.
write_config(tmp.path(), "schema_version = 0\n[general]\nlanguage = \"de\"\n");
std::fs::set_permissions(tmp.path(), std::fs::Permissions::from_mode(0o500)).unwrap();
if !common::restrict_directory_writes(tmp.path()) {
return;
}
let (store, report) = open_in(tmp.path());
std::fs::set_permissions(tmp.path(), std::fs::Permissions::from_mode(0o700)).unwrap();
assert!(matches!(report.read_only, Some(ReadOnlyReason::DirNotWritable(_))), "{report:?}");
+8
View File
@@ -204,6 +204,14 @@ fn read_only_file_makes_the_save_fail_visibly() {
let (mut store, _) = open_in(tmp.path());
store.flush().unwrap();
std::fs::set_permissions(config_path(tmp.path()), std::fs::Permissions::from_mode(0o400)).unwrap();
match std::fs::File::options().write(true).open(config_path(tmp.path())) {
Ok(_) => {
eprintln!("skipping permission test: environment permits writes into a 0400 file");
return;
}
Err(error) if error.kind() == std::io::ErrorKind::PermissionDenied => {}
Err(error) => panic!("unexpected file write-probe failure: {error}"),
}
store.update(|p| p.general.language = "fr".into());
let r = store.tick(Instant::now() + Duration::from_secs(1));
assert!(matches!(r.status, SaveStatus::Failed(craft_settings::SettingsError::Io(_))), "{r:?}");