From 96f9c40ca354ba6cb3f3fa329feb8075dcba5c07 Mon Sep 17 00:00:00 2001 From: Pierre Warnier Date: Fri, 3 Apr 2026 17:45:10 +0200 Subject: [PATCH] tests: deduplicate skip_unless_root() into common module Replace 13 local copies across all test files with a shared import from tests/common/mod.rs. Fix getuid/geteuid inconsistency in test_useradd.rs and test_userdel.rs (effective UID is correct for setuid-root tools). Fixes #133 --- tests/by-util/test_chage.rs | 24 ++++++++++----------- tests/by-util/test_chfn.rs | 10 ++++----- tests/by-util/test_chpasswd.rs | 14 ++++++------- tests/by-util/test_chsh.rs | 10 ++++----- tests/by-util/test_groupadd.rs | 24 ++++++++++----------- tests/by-util/test_groupdel.rs | 22 +++++++++----------- tests/by-util/test_groupmod.rs | 24 ++++++++++----------- tests/by-util/test_grpck.rs | 24 ++++++++++----------- tests/by-util/test_passwd.rs | 38 ++++++++++++++++------------------ tests/by-util/test_pwck.rs | 28 ++++++++++++------------- tests/by-util/test_useradd.rs | 30 +++++++++++++-------------- tests/by-util/test_userdel.rs | 24 ++++++++++----------- tests/by-util/test_usermod.rs | 36 +++++++++++++++----------------- tests/common/mod.rs | 31 --------------------------- 14 files changed, 141 insertions(+), 198 deletions(-) diff --git a/tests/by-util/test_chage.rs b/tests/by-util/test_chage.rs index f8076d3..2ebe213 100644 --- a/tests/by-util/test_chage.rs +++ b/tests/by-util/test_chage.rs @@ -6,16 +6,14 @@ //! Integration tests for the `chage` utility. //! -//! Tests that require root are guarded by `skip_unless_root()` and run inside +//! Tests that require root are guarded by `common::skip_unless_root()` and run inside //! Docker CI containers. Non-root tests exercise clap parsing and error paths //! that do not need privilege. use std::ffi::OsString; -/// Skip the test when not running as root (euid != 0). -fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() -} +#[path = "../common/mod.rs"] +mod common; /// Run `uumain` with the given args, returning the exit code. fn run(args: &[&str]) -> i32 { @@ -82,7 +80,7 @@ fn test_conflicting_list_and_lastday() { #[test] fn test_list_output() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -100,7 +98,7 @@ fn test_list_output() { #[test] fn test_set_mindays() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -129,7 +127,7 @@ fn test_set_mindays() { #[test] fn test_set_maxdays() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -156,7 +154,7 @@ fn test_set_maxdays() { #[test] fn test_set_warndays() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -183,7 +181,7 @@ fn test_set_warndays() { #[test] fn test_set_inactive() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -210,7 +208,7 @@ fn test_set_inactive() { #[test] fn test_set_expiredate() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -237,7 +235,7 @@ fn test_set_expiredate() { #[test] fn test_set_lastchange() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -264,7 +262,7 @@ fn test_set_lastchange() { #[test] fn test_remove_expiredate() { - if skip_unless_root() { + if common::skip_unless_root() { return; } diff --git a/tests/by-util/test_chfn.rs b/tests/by-util/test_chfn.rs index b59eb58..901766f 100644 --- a/tests/by-util/test_chfn.rs +++ b/tests/by-util/test_chfn.rs @@ -11,10 +11,8 @@ use std::ffi::OsString; -/// Skip the test when not running as root (euid != 0). -fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() -} +#[path = "../common/mod.rs"] +mod common; /// Run `uumain` with the given args, returning the exit code. fn run(args: &[&str]) -> i32 { @@ -59,7 +57,7 @@ fn test_unknown_flag_exits_one() { #[test] fn test_change_full_name() { - if skip_unless_root() { + if common::skip_unless_root() { return; } let dir = setup_prefix("testuser:x:1000:1000:Old Name,,,:/home/testuser:/bin/bash\n"); @@ -82,7 +80,7 @@ fn test_change_full_name() { #[test] fn test_no_flags_exits_error() { - if skip_unless_root() { + if common::skip_unless_root() { return; } // No flags specified — should error diff --git a/tests/by-util/test_chpasswd.rs b/tests/by-util/test_chpasswd.rs index af8d2fe..710e2d4 100644 --- a/tests/by-util/test_chpasswd.rs +++ b/tests/by-util/test_chpasswd.rs @@ -6,16 +6,14 @@ //! Integration tests for the `chpasswd` utility. //! -//! Tests that require root are guarded by `skip_unless_root()` and run inside +//! Tests that require root are guarded by `common::skip_unless_root()` and run inside //! Docker CI containers. Non-root tests exercise clap parsing and error paths //! that do not need privilege. use std::ffi::OsString; -/// Skip the test when not running as root (euid != 0). -fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() -} +#[path = "../common/mod.rs"] +mod common; /// Run `uumain` with the given args, returning the exit code. fn run(args: &[&str]) -> i32 { @@ -60,7 +58,7 @@ fn test_invalid_crypt_method_exits_error() { #[test] fn test_batch_password_update() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -96,7 +94,7 @@ fn test_batch_password_update() { #[test] fn test_preserves_other_fields() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -125,7 +123,7 @@ fn test_preserves_other_fields() { #[test] fn test_multiple_users_atomic() { - if skip_unless_root() { + if common::skip_unless_root() { return; } diff --git a/tests/by-util/test_chsh.rs b/tests/by-util/test_chsh.rs index 686a3a1..23ac9ec 100644 --- a/tests/by-util/test_chsh.rs +++ b/tests/by-util/test_chsh.rs @@ -10,10 +10,8 @@ use std::ffi::OsString; -/// Skip the test when not running as root (euid != 0). -fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() -} +#[path = "../common/mod.rs"] +mod common; /// Run `uumain` with the given args, returning the exit code. fn run(args: &[&str]) -> i32 { @@ -50,7 +48,7 @@ fn test_no_shell_flag_exits_error() { #[test] fn test_list_shells() { - if skip_unless_root() { + if common::skip_unless_root() { return; } // -l should list shells and exit 0 (assuming /etc/shells exists on the system) @@ -61,7 +59,7 @@ fn test_list_shells() { #[test] fn test_invalid_shell_path() { - if skip_unless_root() { + if common::skip_unless_root() { return; } // Relative path should be rejected diff --git a/tests/by-util/test_groupadd.rs b/tests/by-util/test_groupadd.rs index d856811..fff0a52 100644 --- a/tests/by-util/test_groupadd.rs +++ b/tests/by-util/test_groupadd.rs @@ -6,16 +6,14 @@ //! Integration tests for the `groupadd` utility. //! -//! Tests that require root are guarded by `skip_unless_root()` and run inside +//! Tests that require root are guarded by `common::skip_unless_root()` and run inside //! Docker CI containers. Non-root tests exercise clap parsing and error paths //! that do not need privilege. use std::ffi::OsString; -/// Skip the test when not running as root (euid != 0). -fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() -} +#[path = "../common/mod.rs"] +mod common; /// Run `uumain` with the given args, returning the exit code. fn run(args: &[&str]) -> i32 { @@ -77,7 +75,7 @@ fn test_missing_group_name_exits_error() { #[test] fn test_create_group_basic() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -101,7 +99,7 @@ fn test_create_group_basic() { #[test] fn test_create_group_with_gid() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -119,7 +117,7 @@ fn test_create_group_with_gid() { #[test] fn test_create_group_system() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -149,7 +147,7 @@ fn test_create_group_system() { #[test] fn test_create_group_non_unique() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -168,7 +166,7 @@ fn test_create_group_non_unique() { #[test] fn test_duplicate_group_fails() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -183,7 +181,7 @@ fn test_duplicate_group_fails() { #[test] fn test_duplicate_gid_fails() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -196,7 +194,7 @@ fn test_duplicate_gid_fails() { #[test] fn test_force_on_existing_succeeds() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -209,7 +207,7 @@ fn test_force_on_existing_succeeds() { #[test] fn test_other_entries_preserved() { - if skip_unless_root() { + if common::skip_unless_root() { return; } diff --git a/tests/by-util/test_groupdel.rs b/tests/by-util/test_groupdel.rs index 95ca55f..92c1977 100644 --- a/tests/by-util/test_groupdel.rs +++ b/tests/by-util/test_groupdel.rs @@ -6,16 +6,14 @@ //! Integration tests for the `groupdel` utility. //! -//! Tests that require root are guarded by `skip_unless_root()` and run inside +//! Tests that require root are guarded by `common::skip_unless_root()` and run inside //! Docker CI containers. Non-root tests exercise clap parsing and error paths //! that do not need privilege. use std::ffi::OsString; -/// Skip the test when not running as root (euid != 0). -fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() -} +#[path = "../common/mod.rs"] +mod common; /// Run `uumain` with the given args, returning the exit code. fn run(args: &[&str]) -> i32 { @@ -76,7 +74,7 @@ fn test_missing_group_name_exits_error() { #[test] fn test_delete_group_basic() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -99,7 +97,7 @@ fn test_delete_group_basic() { #[test] fn test_delete_nonexistent_group_fails() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -119,7 +117,7 @@ fn test_delete_nonexistent_group_fails() { #[test] fn test_delete_group_preserves_others() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -151,7 +149,7 @@ fn test_delete_group_preserves_others() { #[test] fn test_delete_primary_group_fails() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -178,7 +176,7 @@ fn test_delete_primary_group_fails() { #[test] fn test_delete_group_removes_gshadow() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -209,7 +207,7 @@ fn test_delete_group_removes_gshadow() { #[test] fn test_delete_with_root_flag() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -232,7 +230,7 @@ fn test_delete_with_root_flag() { #[test] fn test_delete_group_with_members() { - if skip_unless_root() { + if common::skip_unless_root() { return; } diff --git a/tests/by-util/test_groupmod.rs b/tests/by-util/test_groupmod.rs index 264dfe4..3c249bb 100644 --- a/tests/by-util/test_groupmod.rs +++ b/tests/by-util/test_groupmod.rs @@ -6,16 +6,14 @@ //! Integration tests for the `groupmod` utility. //! -//! Tests that require root are guarded by `skip_unless_root()` and run inside +//! Tests that require root are guarded by `common::skip_unless_root()` and run inside //! Docker CI containers. Non-root tests exercise clap parsing and error paths //! that do not need privilege. use std::ffi::OsString; -/// Skip the test when not running as root (euid != 0). -fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() -} +#[path = "../common/mod.rs"] +mod common; /// Run `uumain` with the given args, returning the exit code. fn run(args: &[&str]) -> i32 { @@ -76,7 +74,7 @@ fn test_missing_group_name_exits_error() { #[test] fn test_change_gid() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -99,7 +97,7 @@ fn test_change_gid() { #[test] fn test_change_name() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -126,7 +124,7 @@ fn test_change_name() { #[test] fn test_non_unique_gid() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -158,7 +156,7 @@ fn test_non_unique_gid() { #[test] fn test_nonexistent_group_fails() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -178,7 +176,7 @@ fn test_nonexistent_group_fails() { #[test] fn test_rename_preserves_members() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -205,7 +203,7 @@ fn test_rename_preserves_members() { #[test] fn test_rename_updates_gshadow() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -232,7 +230,7 @@ fn test_rename_updates_gshadow() { #[test] fn test_name_collision_fails() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -252,7 +250,7 @@ fn test_name_collision_fails() { #[test] fn test_change_gid_and_name_together() { - if skip_unless_root() { + if common::skip_unless_root() { return; } diff --git a/tests/by-util/test_grpck.rs b/tests/by-util/test_grpck.rs index 3bfdc63..982ea20 100644 --- a/tests/by-util/test_grpck.rs +++ b/tests/by-util/test_grpck.rs @@ -6,16 +6,14 @@ //! Integration tests for the `grpck` utility. //! -//! Tests that require root are guarded by `skip_unless_root()` and run inside +//! Tests that require root are guarded by `common::skip_unless_root()` and run inside //! Docker CI containers. Non-root tests exercise clap parsing and error paths //! that do not need privilege. use std::ffi::OsString; -/// Skip the test when not running as root (euid != 0). -fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() -} +#[path = "../common/mod.rs"] +mod common; /// Run `uumain` with the given args, returning the exit code. fn run(args: &[&str]) -> i32 { @@ -68,7 +66,7 @@ fn test_read_only_mode() { #[test] fn test_valid_files_exits_zero() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -80,7 +78,7 @@ fn test_valid_files_exits_zero() { #[test] fn test_missing_gshadow_entry() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -99,7 +97,7 @@ fn test_missing_gshadow_entry() { #[test] fn test_extra_gshadow_entry() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -118,7 +116,7 @@ fn test_extra_gshadow_entry() { #[test] fn test_invalid_gid() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -137,7 +135,7 @@ fn test_invalid_gid() { #[test] fn test_duplicate_group_name() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -149,7 +147,7 @@ fn test_duplicate_group_name() { #[test] fn test_empty_group_name() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -168,7 +166,7 @@ fn test_empty_group_name() { #[test] fn test_malformed_group_line() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -193,7 +191,7 @@ fn test_nonexistent_group_exits_cant_open() { #[test] fn test_valid_group_without_gshadow_file() { - if skip_unless_root() { + if common::skip_unless_root() { return; } diff --git a/tests/by-util/test_passwd.rs b/tests/by-util/test_passwd.rs index 8238367..7bddbc6 100644 --- a/tests/by-util/test_passwd.rs +++ b/tests/by-util/test_passwd.rs @@ -6,16 +6,14 @@ //! Integration tests for the `passwd` utility. //! -//! Tests that require root are guarded by `skip_unless_root()` and run inside +//! Tests that require root are guarded by `common::skip_unless_root()` and run inside //! Docker CI containers. Non-root tests exercise clap parsing and error paths //! that do not need privilege. use std::ffi::OsString; -/// Skip the test when not running as root (euid != 0). -fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() -} +#[path = "../common/mod.rs"] +mod common; /// Run `uumain` with the given args, returning the exit code. fn run(args: &[&str]) -> i32 { @@ -74,7 +72,7 @@ fn test_conflicting_flags_exits_two() { #[test] fn test_status_output_format() { - if skip_unless_root() { + if common::skip_unless_root() { return; } // Verify the status line matches the expected GNU format: @@ -86,7 +84,7 @@ fn test_status_output_format() { #[test] fn test_lock_unlock_cycle() { - if skip_unless_root() { + if common::skip_unless_root() { return; } let dir = setup_prefix("testuser:$6$hash:19500:0:99999:7:::\n"); @@ -126,7 +124,7 @@ fn test_lock_unlock_cycle() { #[test] fn test_expire_sets_epoch() { - if skip_unless_root() { + if common::skip_unless_root() { return; } let dir = setup_prefix("testuser:$6$hash:19500:0:99999:7:::\n"); @@ -143,7 +141,7 @@ fn test_expire_sets_epoch() { #[test] fn test_aging_all_fields() { - if skip_unless_root() { + if common::skip_unless_root() { return; } let dir = setup_prefix("testuser:$6$hash:19500:0:99999:7:::\n"); @@ -162,7 +160,7 @@ fn test_aging_all_fields() { #[test] fn test_nonexistent_user_fails() { - if skip_unless_root() { + if common::skip_unless_root() { return; } let dir = setup_prefix("testuser:$6$hash:19500:0:99999:7:::\n"); @@ -174,7 +172,7 @@ fn test_nonexistent_user_fails() { #[test] fn test_missing_shadow_fails() { - if skip_unless_root() { + if common::skip_unless_root() { return; } let dir = tempfile::tempdir().expect("failed to create temp dir"); @@ -187,7 +185,7 @@ fn test_missing_shadow_fails() { #[test] fn test_quiet_no_action_message() { - if skip_unless_root() { + if common::skip_unless_root() { return; } // -q suppresses the informational action message on stderr. @@ -205,7 +203,7 @@ fn test_quiet_no_action_message() { #[test] fn test_lock_and_aging_combined() { - if skip_unless_root() { + if common::skip_unless_root() { return; } // Mutation flag + aging flags must all apply in a single operation. @@ -227,7 +225,7 @@ fn test_lock_and_aging_combined() { #[test] fn test_multiple_users_only_target_modified() { - if skip_unless_root() { + if common::skip_unless_root() { return; } let shadow = "\ @@ -256,7 +254,7 @@ charlie:$6$charlie:19500:0:99999:7:::\n"; #[test] fn test_delete_password() { - if skip_unless_root() { + if common::skip_unless_root() { return; } let dir = setup_prefix("testuser:$6$hash:19500:0:99999:7:::\n"); @@ -272,7 +270,7 @@ fn test_delete_password() { #[test] fn test_unlock_only_bang_fails() { - if skip_unless_root() { + if common::skip_unless_root() { return; } // Password "!" cannot be unlocked (would leave empty). @@ -283,7 +281,7 @@ fn test_unlock_only_bang_fails() { #[test] fn test_full_lifecycle() { - if skip_unless_root() { + if common::skip_unless_root() { return; } let dir = setup_prefix("testuser:$6$hash:19500:0:99999:7:::\n"); @@ -329,7 +327,7 @@ fn test_full_lifecycle() { /// After both complete, the shadow file should still be valid and parseable. #[test] fn test_concurrent_lock_operations() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -393,7 +391,7 @@ fn test_concurrent_lock_operations() { /// verifies the output format is identical. #[test] fn test_gnu_compat_status_output() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -443,7 +441,7 @@ fn test_gnu_compat_status_output() { /// Compare lock/unlock cycle results with GNU passwd. #[test] fn test_gnu_compat_lock_unlock() { - if skip_unless_root() { + if common::skip_unless_root() { return; } diff --git a/tests/by-util/test_pwck.rs b/tests/by-util/test_pwck.rs index 018efd0..35b73f7 100644 --- a/tests/by-util/test_pwck.rs +++ b/tests/by-util/test_pwck.rs @@ -6,16 +6,14 @@ //! Integration tests for the `pwck` utility. //! -//! Tests that require root are guarded by `skip_unless_root()` and run inside +//! Tests that require root are guarded by `common::skip_unless_root()` and run inside //! Docker CI containers. Non-root tests exercise clap parsing and error paths //! that do not need privilege. use std::ffi::OsString; -/// Skip the test when not running as root (euid != 0). -fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() -} +#[path = "../common/mod.rs"] +mod common; /// Run `uumain` with the given args, returning the exit code. fn run(args: &[&str]) -> i32 { @@ -72,7 +70,7 @@ fn test_read_only_mode() { #[test] fn test_valid_files_exits_zero() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -100,7 +98,7 @@ fn test_valid_files_exits_zero() { #[test] fn test_missing_shadow_entry() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -132,7 +130,7 @@ fn test_missing_shadow_entry() { #[test] fn test_extra_shadow_entry() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -163,7 +161,7 @@ fn test_extra_shadow_entry() { #[test] fn test_invalid_uid() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -193,7 +191,7 @@ fn test_invalid_uid() { #[test] fn test_invalid_gid() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -223,7 +221,7 @@ fn test_invalid_gid() { #[test] fn test_duplicate_username() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -251,7 +249,7 @@ fn test_duplicate_username() { #[test] fn test_duplicate_uid() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -287,7 +285,7 @@ fn test_duplicate_uid() { #[test] fn test_empty_username() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -317,7 +315,7 @@ fn test_empty_username() { #[test] fn test_missing_home_dir() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -347,7 +345,7 @@ fn test_missing_home_dir() { #[test] fn test_malformed_passwd_line() { - if skip_unless_root() { + if common::skip_unless_root() { return; } diff --git a/tests/by-util/test_useradd.rs b/tests/by-util/test_useradd.rs index 85e895e..d24a1e4 100644 --- a/tests/by-util/test_useradd.rs +++ b/tests/by-util/test_useradd.rs @@ -6,16 +6,14 @@ //! Integration tests for the `useradd` utility. //! -//! Tests that require root are guarded by `skip_unless_root()` and run inside +//! Tests that require root are guarded by `common::skip_unless_root()` and run inside //! Docker CI containers. Non-root tests exercise clap parsing and error paths //! that do not need privilege. use std::ffi::OsString; -/// Skip the test when not running as root (uid != 0). -fn skip_unless_root() -> bool { - !nix::unistd::getuid().is_root() -} +#[path = "../common/mod.rs"] +mod common; /// Run `uumain` with the given args, returning the exit code. fn run(args: &[&str]) -> i32 { @@ -137,7 +135,7 @@ fn test_conflicting_user_group_no_user_group() { #[test] fn test_create_user_basic() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -160,7 +158,7 @@ fn test_create_user_basic() { #[test] fn test_create_user_with_home() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -192,7 +190,7 @@ fn test_create_user_with_home() { #[test] fn test_create_user_with_uid() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -209,7 +207,7 @@ fn test_create_user_with_uid() { #[test] fn test_create_user_with_shell() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -226,7 +224,7 @@ fn test_create_user_with_shell() { #[test] fn test_create_user_system() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -250,7 +248,7 @@ fn test_create_user_system() { #[test] fn test_create_user_with_group() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -275,7 +273,7 @@ fn test_create_user_with_group() { #[test] fn test_duplicate_user_fails() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -292,7 +290,7 @@ fn test_duplicate_user_fails() { #[test] fn test_create_user_with_comment() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -309,7 +307,7 @@ fn test_create_user_with_comment() { #[test] fn test_create_user_creates_user_group() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -327,7 +325,7 @@ fn test_create_user_creates_user_group() { #[test] fn test_create_user_preserves_existing_entries() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -354,7 +352,7 @@ fn test_create_user_preserves_existing_entries() { #[test] fn test_create_user_with_home_dir_flag() { - if skip_unless_root() { + if common::skip_unless_root() { return; } diff --git a/tests/by-util/test_userdel.rs b/tests/by-util/test_userdel.rs index c546398..5f10e1c 100644 --- a/tests/by-util/test_userdel.rs +++ b/tests/by-util/test_userdel.rs @@ -6,16 +6,14 @@ //! Integration tests for the `userdel` utility. //! -//! Tests that require root are guarded by `skip_unless_root()` and run inside +//! Tests that require root are guarded by `common::skip_unless_root()` and run inside //! Docker CI containers. Non-root tests exercise clap parsing and error paths //! that do not need privilege. use std::ffi::OsString; -/// Skip the test when not running as root (uid != 0). -fn skip_unless_root() -> bool { - !nix::unistd::getuid().is_root() -} +#[path = "../common/mod.rs"] +mod common; /// Run `uumain` with the given args, returning the exit code. fn run(args: &[&str]) -> i32 { @@ -114,7 +112,7 @@ fn test_missing_login_exits_error() { #[test] fn test_delete_user_basic() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -137,7 +135,7 @@ fn test_delete_user_basic() { #[test] fn test_delete_user_remove_home() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -179,7 +177,7 @@ otheruser:x:1001:1001:Other User:/home/otheruser:/bin/bash\n" #[test] fn test_delete_nonexistent_user_fails() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -190,7 +188,7 @@ fn test_delete_nonexistent_user_fails() { #[test] fn test_delete_user_preserves_others() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -225,7 +223,7 @@ fn test_delete_user_preserves_others() { #[test] fn test_delete_user_removes_group_membership() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -249,7 +247,7 @@ fn test_delete_user_removes_group_membership() { #[test] fn test_delete_user_shadow_entry_removed() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -270,7 +268,7 @@ fn test_delete_user_shadow_entry_removed() { #[test] fn test_delete_user_force_flag_accepted() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -288,7 +286,7 @@ fn test_delete_user_force_flag_accepted() { #[test] fn test_delete_multiple_users_sequentially() { - if skip_unless_root() { + if common::skip_unless_root() { return; } diff --git a/tests/by-util/test_usermod.rs b/tests/by-util/test_usermod.rs index 02b7cad..d4661df 100644 --- a/tests/by-util/test_usermod.rs +++ b/tests/by-util/test_usermod.rs @@ -6,16 +6,14 @@ //! Integration tests for the `usermod` utility. //! -//! Tests that require root are guarded by `skip_unless_root()` and run inside +//! Tests that require root are guarded by `common::skip_unless_root()` and run inside //! Docker CI containers. Non-root tests exercise clap parsing and error paths //! that do not need privilege. use std::ffi::OsString; -/// Skip the test when not running as root (euid != 0). -fn skip_unless_root() -> bool { - !nix::unistd::geteuid().is_root() -} +#[path = "../common/mod.rs"] +mod common; /// Run `uumain` with the given args, returning the exit code. fn run(args: &[&str]) -> i32 { @@ -99,7 +97,7 @@ fn test_lock_unlock_conflict() { #[test] fn test_change_shell() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -126,7 +124,7 @@ fn test_change_shell() { #[test] fn test_change_comment() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -148,7 +146,7 @@ fn test_change_comment() { #[test] fn test_change_home() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -170,7 +168,7 @@ fn test_change_home() { #[test] fn test_change_uid() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -192,7 +190,7 @@ fn test_change_uid() { #[test] fn test_add_to_groups() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -218,7 +216,7 @@ fn test_add_to_groups() { #[test] fn test_lock_user() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -240,7 +238,7 @@ fn test_lock_user() { #[test] fn test_unlock_user() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -262,7 +260,7 @@ fn test_unlock_user() { #[test] fn test_nonexistent_user_fails() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -278,7 +276,7 @@ fn test_nonexistent_user_fails() { #[test] fn test_multiple_modifications_combined() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -319,7 +317,7 @@ fn test_multiple_modifications_combined() { #[test] fn test_other_users_unchanged() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -353,7 +351,7 @@ fn test_other_users_unchanged() { #[test] fn test_uid_collision_fails() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -372,7 +370,7 @@ fn test_uid_collision_fails() { #[test] fn test_set_password() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -400,7 +398,7 @@ fn test_set_password() { #[test] fn test_set_password_long_flag() { - if skip_unless_root() { + if common::skip_unless_root() { return; } @@ -423,7 +421,7 @@ fn test_set_password_long_flag() { #[test] fn test_set_password_preserves_other_fields() { - if skip_unless_root() { + if common::skip_unless_root() { return; } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 312609b..eabdfc9 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -13,34 +13,3 @@ pub fn skip_unless_root() -> bool { !nix::unistd::geteuid().is_root() } - -/// Create a temp directory with synthetic `/etc/` files for testing. -/// -/// Returns a `TempDir` — the directory and files are cleaned up on drop. -pub fn setup_prefix(shadow_content: &str) -> tempfile::TempDir { - let dir = tempfile::tempdir().expect("failed to create temp dir"); - let etc = dir.path().join("etc"); - std::fs::create_dir_all(&etc).expect("failed to create etc dir"); - std::fs::write(etc.join("shadow"), shadow_content).expect("failed to write shadow"); - dir -} - -/// Create a temp directory with passwd, shadow, and group files. -pub fn setup_full_prefix( - passwd_content: &str, - shadow_content: &str, - group_content: &str, -) -> tempfile::TempDir { - let dir = tempfile::tempdir().expect("failed to create temp dir"); - let etc = dir.path().join("etc"); - std::fs::create_dir_all(&etc).expect("failed to create etc dir"); - std::fs::write(etc.join("passwd"), passwd_content).expect("failed to write passwd"); - std::fs::write(etc.join("shadow"), shadow_content).expect("failed to write shadow"); - std::fs::write(etc.join("group"), group_content).expect("failed to write group"); - dir -} - -/// Read a file from a temp prefix directory. -pub fn read_file(dir: &tempfile::TempDir, relative: &str) -> String { - std::fs::read_to_string(dir.path().join(relative)).expect("failed to read file") -}