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
This commit is contained in:
Pierre Warnier
2026-04-03 17:45:10 +02:00
parent eea6b02d2b
commit 96f9c40ca3
14 changed files with 141 additions and 198 deletions
+11 -13
View File
@@ -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;
}
+4 -6
View File
@@ -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
+6 -8
View File
@@ -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;
}
+4 -6
View File
@@ -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
+11 -13
View File
@@ -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;
}
+10 -12
View File
@@ -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;
}
+11 -13
View File
@@ -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;
}
+11 -13
View File
@@ -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;
}
+18 -20
View File
@@ -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;
}
+13 -15
View File
@@ -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;
}
+14 -16
View File
@@ -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;
}
+11 -13
View File
@@ -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;
}
+17 -19
View File
@@ -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;
}
-31
View File
@@ -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")
}