monochromatic-git-policy-cli 0.5.34

Repository-owned Git policy wrapper: forwards Git commands and checks commits against configured policies.
Documentation
//! Controls for the PID namespace rule.

use super::*;
use std::fs::File;

/// A scratch record file whose modification time is `age` in the past.
fn record_aged(directory: &Path, age: Duration) -> std::path::PathBuf {
    let path: std::path::PathBuf = directory.join("owner.json");
    let file: File = File::create(path.as_path()).expect("create the record");
    file.set_modified(SystemTime::now() - age).expect("age the record");
    return path;
}

/// This process names its own namespace on Linux, in the kernel's spelling.
#[test]
#[cfg(target_os = "linux")]
fn the_current_namespace_is_named_the_way_the_kernel_names_it() {
    let name: String = current_pid_namespace().expect("a Linux process has a PID namespace");
    assert!(name.starts_with("pid:["), "{name}");
    assert!(name.ends_with(']'), "{name}");
}

/// Only two known and different names make a namespace foreign.
#[test]
fn only_two_known_and_different_names_are_another_namespace() {
    assert!(is_other_namespace(Some("pid:[1]"), Some("pid:[2]")));
    assert!(!is_other_namespace(Some("pid:[1]"), Some("pid:[1]")));
    assert!(!is_other_namespace(None, Some("pid:[2]")), "an old record names no namespace");
    assert!(!is_other_namespace(Some("pid:[1]"), None), "a reader without a namespace cannot tell");
    assert!(!is_other_namespace(None, None));
}

/// A fresh record has not outlived the patience; one past it has.
#[test]
fn a_foreign_record_expires_only_after_the_patience() {
    let directory: std::path::PathBuf = crate::test_support::fixture("pid-namespace-1");
    let fresh = record_aged(directory.as_path(), Duration::from_secs(60));
    assert!(!age_outlives_patience(foreign_record_age(fresh.as_path()).expect("a measurable age")));
    let old = record_aged(directory.as_path(), FOREIGN_OWNER_PATIENCE + Duration::from_secs(60));
    assert!(age_outlives_patience(foreign_record_age(old.as_path()).expect("a measurable age")));
    let nearly = record_aged(directory.as_path(), FOREIGN_OWNER_PATIENCE - Duration::from_secs(60));
    assert!(!age_outlives_patience(foreign_record_age(nearly.as_path()).expect("a measurable age")), "just inside the patience stays alive");
}

/// A record that cannot be measured keeps its owner alive.
#[test]
fn a_record_that_cannot_be_measured_does_not_expire() {
    let directory: std::path::PathBuf = crate::test_support::fixture("pid-namespace-2");
    assert_eq!(foreign_record_age(directory.as_path().join("absent.json").as_path()), None);
    let future = record_aged(directory.as_path(), Duration::ZERO);
    File::options().write(true).open(future.as_path()).expect("open").set_modified(
        SystemTime::now() + Duration::from_secs(7200),
    ).expect("move the time ahead");
    assert_eq!(foreign_record_age(future.as_path()), None, "a record from the future has no age, so it is not old");
}

/// The shared rule: unknown or same namespace leaves the decision to the PID; another one answers by age.
#[test]
#[cfg(target_os = "linux")]
fn a_foreign_owner_is_alive_until_its_record_expires() {
    let directory: std::path::PathBuf = crate::test_support::fixture("pid-namespace-3");
    let fresh = record_aged(directory.as_path(), Duration::from_secs(60));
    let own: String = current_pid_namespace().expect("namespace");
    assert_eq!(foreign_owner_standing(Some(own.as_str()), fresh.as_path()), None);
    assert_eq!(foreign_owner_standing(None, fresh.as_path()), None);
    assert_eq!(foreign_owner_standing(Some("pid:[1]"), fresh.as_path()), Some(Ok(())));
    let old = record_aged(directory.as_path(), FOREIGN_OWNER_PATIENCE + Duration::from_secs(60));
    // Neither alive nor dead (owner, 2026-11-04, "Refuse and name it (Recommended)"): the record is refused, with what a person needs.
    let Some(Err(expired)) = foreign_owner_standing(Some("pid:[1]"), old.as_path()) else { panic!("an expired foreign record is refused") };
    let text: String = expired.describe(31337);
    assert!(text.contains("31337") && text.contains("pid:[1]") && text.contains("21 minutes old") && text.contains("20 minutes") && text.contains("rm -- "), "{text}");
    assert!(text.contains("another container") && text.contains("left running") && text.contains(old.to_string_lossy().as_ref()), "{text}");
}

/// Exactly the patience is still alive; one nanosecond more is expired.
#[test]
fn the_patience_boundary_is_exclusive() {
    assert!(!age_outlives_patience(FOREIGN_OWNER_PATIENCE));
    assert!(age_outlives_patience(FOREIGN_OWNER_PATIENCE + Duration::from_nanos(1)));
    assert!(!age_outlives_patience(FOREIGN_OWNER_PATIENCE - Duration::from_nanos(1)));
}

/// The patience is derived from the lock ceiling, so the two cannot drift apart again: a hold of another namespace's owner lasts
/// at most one wait for a foreign `index.lock` plus the work around it, and the patience is twice that wait.
#[test]
fn the_patience_is_twice_the_lock_ceiling() {
    assert_eq!(FOREIGN_OWNER_PATIENCE, 2 * crate::wait_ceilings::LOCK_CEILING);
}

/// The patience outlasts the longest command measured on this host (the commit timing log, 750.6 s), so a live owner's record is
/// not judged dead while its command is still running.
#[test]
fn the_patience_outlasts_the_longest_measured_command() {
    const LONGEST_COMMAND_MEASURED: Duration = Duration::from_millis(750_604);
    assert!(FOREIGN_OWNER_PATIENCE > LONGEST_COMMAND_MEASURED);
}