malvin 0.2.4

Non-interactive research and coding agent
use std::collections::{HashMap, HashSet};

use super::session_spawn_affiliation::{
    AffiliationCtx, clear_session_spawn_affiliation_for_test, is_session_affiliated_pid,
    note_session_affiliated_pid, pid_is_session_affiliated,
};
use super::{affiliated_session_pids, malvin_session_spawn_pids, reparented_init_orphans};
use crate::acp::unix_process_group_ps::ProcRow;

fn affiliate_proven_fixture_pid(pid: u32, start_ppid: u32, ctx: &AffiliationCtx<'_>) {
    assert!(
        pid_is_session_affiliated(pid, start_ppid, ctx),
        "fixture pid {pid} must be provably session-affiliated before recording"
    );
    note_session_affiliated_pid(pid);
}

#[test]
fn affiliated_pid_survives_reparent_to_init_in_store() {
    clear_session_spawn_affiliation_for_test();
    let malvin_pid = std::process::id();
    let agent_pgid = 500;
    let baseline = HashSet::from([malvin_pid, 1, 2]);
    let rows = vec![
        ProcRow {
            pid: malvin_pid,
            pgid: malvin_pid,
            ppid: 1,
        },
        ProcRow {
            pid: 500,
            pgid: agent_pgid,
            ppid: malvin_pid,
        },
        ProcRow {
            pid: 600,
            pgid: 600,
            ppid: 1,
        },
    ];
    let first_seen_map = HashMap::from([(600, 500)]);
    let ctx = AffiliationCtx {
        rows: &rows,
        agent_pgid: Some(agent_pgid),
        baseline: &baseline,
        first_seen: &first_seen_map,
    };
    assert!(pid_is_session_affiliated(600, 500, &ctx));
    note_session_affiliated_pid(600);
    assert!(is_session_affiliated_pid(600));
    clear_session_spawn_affiliation_for_test();
}

#[test]
fn unrelated_init_orphan_not_affiliated() {
    clear_session_spawn_affiliation_for_test();
    let malvin_pid = std::process::id();
    let baseline = HashSet::from([malvin_pid, 1, 2, 50]);
    let rows = vec![
        ProcRow {
            pid: malvin_pid,
            pgid: malvin_pid,
            ppid: 1,
        },
        ProcRow {
            pid: 50,
            pgid: 50,
            ppid: 1,
        },
        ProcRow {
            pid: 700,
            pgid: 700,
            ppid: 1,
        },
    ];
    let first_seen_map = HashMap::from([(700, 50)]);
    let ctx = AffiliationCtx {
        rows: &rows,
        agent_pgid: Some(999),
        baseline: &baseline,
        first_seen: &first_seen_map,
    };
    assert!(!pid_is_session_affiliated(700, 50, &ctx));
    clear_session_spawn_affiliation_for_test();
}

#[test]
fn reparented_init_orphans_skips_unaffiliated_user_daemon() {
    clear_session_spawn_affiliation_for_test();
    let baseline = HashSet::from([10, 50]);
    let rows = vec![
        ProcRow {
            pid: 10,
            pgid: 10,
            ppid: 1,
        },
        ProcRow {
            pid: 50,
            pgid: 50,
            ppid: 1,
        },
        ProcRow {
            pid: 700,
            pgid: 700,
            ppid: 1,
        },
    ];
    let orphans = reparented_init_orphans(&baseline, &rows);
    assert!(
        !orphans.contains(&700),
        "unaffiliated user init-reparented daemon must not be a teardown target"
    );
    clear_session_spawn_affiliation_for_test();
}

#[test]
fn reparented_init_orphans_includes_proven_affiliated_setsid_and_double_fork() {
    clear_session_spawn_affiliation_for_test();
    let malvin_pid = std::process::id();
    let agent_pgid = 500;
    let baseline = HashSet::from([malvin_pid, 10]);
    let rows = vec![
        ProcRow {
            pid: malvin_pid,
            pgid: malvin_pid,
            ppid: 1,
        },
        ProcRow {
            pid: 10,
            pgid: 10,
            ppid: 1,
        },
        ProcRow {
            pid: agent_pgid,
            pgid: agent_pgid,
            ppid: malvin_pid,
        },
        ProcRow {
            pid: 99,
            pgid: 99,
            ppid: 1,
        },
        ProcRow {
            pid: 100,
            pgid: 50,
            ppid: 1,
        },
    ];
    let first_seen = HashMap::from([(99, agent_pgid), (100, agent_pgid)]);
    let ctx = AffiliationCtx {
        rows: &rows,
        agent_pgid: Some(agent_pgid),
        baseline: &baseline,
        first_seen: &first_seen,
    };
    affiliate_proven_fixture_pid(99, agent_pgid, &ctx);
    affiliate_proven_fixture_pid(100, agent_pgid, &ctx);
    let orphans = reparented_init_orphans(&baseline, &rows);
    assert!(
        orphans.contains(&99),
        "setsid orphan affiliated via agent PG"
    );
    assert!(
        orphans.contains(&100),
        "double-fork orphan affiliated via agent PG"
    );
    assert!(
        !orphans.contains(&10),
        "baseline pid must not be an orphan target"
    );
    clear_session_spawn_affiliation_for_test();
}

#[test]
fn malvin_session_spawn_pids_includes_proven_same_pg_descendant() {
    clear_session_spawn_affiliation_for_test();
    let malvin_pid = std::process::id();
    let baseline = HashSet::from([malvin_pid, 999_999]);
    let rows = vec![
        ProcRow {
            pid: malvin_pid,
            pgid: malvin_pid,
            ppid: 1,
        },
        ProcRow {
            pid: 50,
            pgid: malvin_pid,
            ppid: malvin_pid,
        },
        ProcRow {
            pid: 51,
            pgid: 51,
            ppid: malvin_pid,
        },
    ];
    let first_seen = HashMap::from([(50, malvin_pid), (51, malvin_pid)]);
    let ctx = AffiliationCtx {
        rows: &rows,
        agent_pgid: None,
        baseline: &baseline,
        first_seen: &first_seen,
    };
    affiliate_proven_fixture_pid(50, malvin_pid, &ctx);
    let spawns = malvin_session_spawn_pids(&baseline, &rows);
    assert!(
        spawns.contains(&50),
        "same-PG malvin descendant must be a session spawn target"
    );
    assert!(
        !spawns.contains(&51),
        "isolated-PG agent child must not be targeted via malvin-PG walk"
    );
    assert!(!spawns.contains(&malvin_pid));
    clear_session_spawn_affiliation_for_test();
}

#[test]
fn kiss_cov_affiliation_unit_names() {
    let _ = affiliated_pid_survives_reparent_to_init_in_store;
    let _ = unrelated_init_orphan_not_affiliated;
    let _ = reparented_init_orphans_skips_unaffiliated_user_daemon;
    let _ = reparented_init_orphans_includes_proven_affiliated_setsid_and_double_fork;
    let _ = malvin_session_spawn_pids_includes_proven_same_pg_descendant;
    let _ = affiliated_session_pids;
}