use super::{command_mechanism, ActionSpec};
use sysknife_types::RiskLevel;
const ADD_APT_REPOSITORY: &str = "add-apt-repository";
const YES_FLAG: &str = "-y";
const REMOVE_FLAG: &str = "--remove";
pub fn specs() -> Vec<ActionSpec> {
vec![add_ppa("deadsnakes/ppa"), remove_ppa("deadsnakes/ppa")]
}
fn ppa_arg(name: &str) -> String {
format!("ppa:{name}")
}
pub fn add_ppa(name: &str) -> ActionSpec {
ActionSpec {
action_name: "AddPpa",
mechanism: command_mechanism("sudo", [ADD_APT_REPOSITORY, YES_FLAG, &ppa_arg(name)]),
risk_level: RiskLevel::High,
reboot_required: false,
rollback_available: false,
}
}
pub fn remove_ppa(name: &str) -> ActionSpec {
ActionSpec {
action_name: "RemovePpa",
mechanism: command_mechanism(
"sudo",
[ADD_APT_REPOSITORY, YES_FLAG, REMOVE_FLAG, &ppa_arg(name)],
),
risk_level: RiskLevel::Medium,
reboot_required: false,
rollback_available: false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::actions::ActionMechanism;
fn extract_args(spec: &ActionSpec) -> (&'static str, Vec<String>) {
match &spec.mechanism {
ActionMechanism::Command { program, args } => (*program, args.clone()),
_ => panic!("expected Command mechanism"),
}
}
#[test]
fn add_ppa_action_name() {
assert_eq!(add_ppa("deadsnakes/ppa").action_name, "AddPpa");
}
#[test]
fn add_ppa_argv_correct() {
let spec = add_ppa("deadsnakes/ppa");
let (prog, args) = extract_args(&spec);
assert_eq!(prog, "sudo");
let joined = args.join(" ");
assert!(
joined.contains("add-apt-repository"),
"missing add-apt-repository: {joined}"
);
assert!(joined.contains("-y"), "missing -y: {joined}");
assert!(
joined.contains("ppa:deadsnakes/ppa"),
"missing ppa:deadsnakes/ppa: {joined}"
);
assert!(
!joined.contains("--remove"),
"unexpected --remove: {joined}"
);
}
#[test]
fn add_ppa_risk_is_high() {
assert_eq!(add_ppa("deadsnakes/ppa").risk_level, RiskLevel::High);
}
#[test]
fn add_ppa_no_automatic_rollback() {
assert!(!add_ppa("deadsnakes/ppa").rollback_available);
}
#[test]
fn remove_ppa_action_name() {
assert_eq!(remove_ppa("deadsnakes/ppa").action_name, "RemovePpa");
}
#[test]
fn remove_ppa_argv_contains_remove_flag() {
let spec = remove_ppa("deadsnakes/ppa");
let (prog, args) = extract_args(&spec);
assert_eq!(prog, "sudo");
let joined = args.join(" ");
assert!(joined.contains("--remove"), "missing --remove: {joined}");
assert!(
joined.contains("ppa:deadsnakes/ppa"),
"missing ppa arg: {joined}"
);
assert!(joined.contains("-y"), "missing -y: {joined}");
}
#[test]
fn remove_ppa_risk_is_medium() {
assert_eq!(remove_ppa("deadsnakes/ppa").risk_level, RiskLevel::Medium);
}
#[test]
fn remove_ppa_no_automatic_rollback() {
assert!(!remove_ppa("deadsnakes/ppa").rollback_available);
}
#[test]
fn ppa_arg_prepends_ppa_prefix() {
assert_eq!(ppa_arg("user/repo"), "ppa:user/repo");
}
#[test]
fn specs_covers_all_action_names() {
let expected = ["AddPpa", "RemovePpa"];
let spec_names: Vec<&str> = specs().iter().map(|s| s.action_name).collect();
for name in &expected {
assert!(spec_names.contains(name), "specs() missing {name}");
}
}
}