fix(api): skip mode-bit tests when they cannot bind
Four tests force a filesystem failure by freezing a directory to 0o555. The CI container runs as root, mode bits do not constrain root, and the rename those tests expect to fail succeeds — main has been red on a_failed_rename_leaves_the_row_alone since the move-on-root-change work landed, with nextest's fail-fast hiding the other three. The guard probes the filesystem rather than the uid: what the tests depend on is the refusal, and a container can hold CAP_DAC_OVERRIDE without being uid 0.
This commit is contained in:
@@ -12,6 +12,8 @@ mod metadata;
|
||||
mod movies;
|
||||
mod owners;
|
||||
mod policies;
|
||||
#[cfg(test)]
|
||||
mod privilege;
|
||||
mod reclassify;
|
||||
mod relocate;
|
||||
mod roots;
|
||||
|
||||
@@ -1944,6 +1944,10 @@ mod tests {
|
||||
async fn a_failed_rename_leaves_the_row_alone() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
if !crate::privilege::mode_bits_bind().await {
|
||||
crate::privilege::skipped_because_privileged("a_failed_rename_leaves_the_row_alone");
|
||||
return;
|
||||
}
|
||||
let (_dir, state, base) = application().await;
|
||||
let movie = add_movie(&base, 693_134, 1).await;
|
||||
let id = movie["id"].as_i64().expect("id");
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
//! Whether mode bits actually constrain this process.
|
||||
//!
|
||||
//! Four tests force a filesystem failure by freezing a directory to `0o555`
|
||||
//! and asserting the handler reports it. Mode bits do not constrain a
|
||||
//! privileged user, so under `root` — which is what the CI container runs
|
||||
//! as — the operation succeeds and the assertion fails for a reason that has
|
||||
//! nothing to do with the code under test.
|
||||
//!
|
||||
//! The probe asks the filesystem rather than asking for the uid: what the
|
||||
//! tests depend on is the refusal, not the identity, and a container can
|
||||
//! hold `CAP_DAC_OVERRIDE` without being uid 0.
|
||||
|
||||
/// True when a read-only directory refuses a write to this process.
|
||||
pub(crate) async fn mode_bits_bind() -> bool {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let probe = tempfile::tempdir().expect("probe root");
|
||||
let frozen = probe.path().join("frozen");
|
||||
tokio::fs::create_dir(&frozen).await.expect("probe folder");
|
||||
tokio::fs::set_permissions(&frozen, std::fs::Permissions::from_mode(0o555))
|
||||
.await
|
||||
.expect("freeze the probe folder");
|
||||
let refused = tokio::fs::write(frozen.join("probe"), b"x").await.is_err();
|
||||
// `TempDir::drop` needs the write back to remove the tree
|
||||
tokio::fs::set_permissions(&frozen, std::fs::Permissions::from_mode(0o755))
|
||||
.await
|
||||
.expect("thaw the probe folder");
|
||||
refused
|
||||
}
|
||||
|
||||
/// Announces a test that cannot run here, on stderr, which `nextest` prints
|
||||
/// when the run is given `--no-capture` and swallows otherwise — the same
|
||||
/// deal every other skipped case in a Rust suite gets.
|
||||
pub(crate) fn skipped_because_privileged(test: &str) {
|
||||
eprintln!("{test}: skipped — this process overrides mode bits, so the failure it forces cannot happen");
|
||||
}
|
||||
@@ -1048,6 +1048,13 @@ mod tests {
|
||||
async fn one_folder_that_cannot_move_puts_the_others_back() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
if !crate::privilege::mode_bits_bind().await {
|
||||
crate::privilege::skipped_because_privileged(
|
||||
"one_folder_that_cannot_move_puts_the_others_back",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let (_dir, state, base) = application().await;
|
||||
let old = tempfile::tempdir().expect("old root");
|
||||
let home = tempfile::tempdir().expect("home of the new path");
|
||||
@@ -1125,6 +1132,13 @@ mod tests {
|
||||
async fn a_new_root_that_already_existed_survives_a_failed_move() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
if !crate::privilege::mode_bits_bind().await {
|
||||
crate::privilege::skipped_because_privileged(
|
||||
"a_new_root_that_already_existed_survives_a_failed_move",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let (_dir, state, base) = application().await;
|
||||
let old = tempfile::tempdir().expect("old root");
|
||||
let new = tempfile::tempdir().expect("new root, already there");
|
||||
@@ -1393,6 +1407,13 @@ mod tests {
|
||||
async fn a_failed_move_removes_every_level_it_created() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
if !crate::privilege::mode_bits_bind().await {
|
||||
crate::privilege::skipped_because_privileged(
|
||||
"a_failed_move_removes_every_level_it_created",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let (_dir, state, base) = application().await;
|
||||
let old = tempfile::tempdir().expect("old root");
|
||||
let home = tempfile::tempdir().expect("home of the new path");
|
||||
|
||||
Reference in New Issue
Block a user