feat: implement uninstall functionality for managed artifacts and persist install metadata

This commit is contained in:
stoorps 2026-03-19 23:07:25 +00:00
parent 38f900ad50
commit 842c390260
Signed by: stoorps
SSH key fingerprint: SHA256:AZlPfu9hTu042EGtZElmDQoy+KvMOeShLDan/fYLoNI
11 changed files with 626 additions and 21 deletions

View file

@ -41,6 +41,7 @@ fn registry_round_trips_update_strategy_and_alternates() {
],
}),
metadata: Vec::new(),
install: None,
}],
};
@ -51,3 +52,52 @@ fn registry_round_trips_update_strategy_and_alternates() {
assert_eq!(strategy.preferred.reason, "install-origin-match");
assert_eq!(strategy.alternates.len(), 2);
}
#[test]
fn registry_round_trips_install_metadata() {
let dir = tempdir().unwrap();
let store = RegistryStore::new(dir.path().join("registry.toml"));
let registry = aim_core::registry::model::Registry {
version: 1,
apps: vec![aim_core::domain::app::AppRecord {
stable_id: "t3code".to_owned(),
display_name: "T3 Code".to_owned(),
source_input: Some("pingdotgg/t3code".to_owned()),
source: None,
installed_version: Some("0.0.11".to_owned()),
update_strategy: None,
metadata: Vec::new(),
install: Some(aim_core::domain::app::InstallMetadata {
scope: aim_core::domain::app::InstallScope::User,
payload_path: Some(
"/tmp/install-home/.local/lib/aim/appimages/t3code.AppImage".to_owned(),
),
desktop_entry_path: Some(
"/tmp/install-home/.local/share/applications/aim-t3code.desktop".to_owned(),
),
icon_path: Some(
"/tmp/install-home/.local/share/icons/hicolor/256x256/apps/t3code.png"
.to_owned(),
),
}),
}],
};
store.save(&registry).unwrap();
let loaded = store.load().unwrap();
let install = loaded.apps[0].install.as_ref().unwrap();
assert_eq!(install.scope, aim_core::domain::app::InstallScope::User);
assert_eq!(
install.payload_path.as_deref(),
Some("/tmp/install-home/.local/lib/aim/appimages/t3code.AppImage")
);
assert_eq!(
install.desktop_entry_path.as_deref(),
Some("/tmp/install-home/.local/share/applications/aim-t3code.desktop")
);
assert_eq!(
install.icon_path.as_deref(),
Some("/tmp/install-home/.local/share/icons/hicolor/256x256/apps/t3code.png")
);
}

View file

@ -1,7 +1,8 @@
use aim_core::app::interaction::{InteractionKind, InteractionRequest};
use aim_core::app::list::build_list_rows;
use aim_core::app::remove::resolve_registered_app;
use aim_core::domain::app::AppRecord;
use aim_core::app::remove::{build_removal_plan, resolve_registered_app};
use aim_core::domain::app::{AppRecord, InstallMetadata, InstallScope};
use std::path::Path;
#[test]
fn remove_flow_rejects_unknown_app_names() {
@ -20,6 +21,7 @@ fn list_flow_returns_display_rows_for_registered_apps() {
installed_version: None,
update_strategy: None,
metadata: Vec::new(),
install: None,
}]);
assert_eq!(rows.len(), 1);
@ -38,6 +40,7 @@ fn ambiguous_remove_matches_include_stable_ids_for_client_choice() {
installed_version: None,
update_strategy: None,
metadata: Vec::new(),
install: None,
},
AppRecord {
stable_id: "bat-nightly".to_owned(),
@ -47,6 +50,7 @@ fn ambiguous_remove_matches_include_stable_ids_for_client_choice() {
installed_version: None,
update_strategy: None,
metadata: Vec::new(),
install: None,
},
];
@ -65,3 +69,59 @@ fn ambiguous_remove_matches_include_stable_ids_for_client_choice() {
}
);
}
#[test]
fn removal_plan_prefers_persisted_install_metadata_paths() {
let app = AppRecord {
stable_id: "bat".to_owned(),
display_name: "Bat".to_owned(),
source_input: None,
source: None,
installed_version: None,
update_strategy: None,
metadata: Vec::new(),
install: Some(InstallMetadata {
scope: InstallScope::System,
payload_path: Some("/opt/aim/appimages/bat.AppImage".to_owned()),
desktop_entry_path: Some("/usr/share/applications/aim-bat.desktop".to_owned()),
icon_path: Some("/usr/share/icons/hicolor/256x256/apps/bat.png".to_owned()),
}),
};
let plan = build_removal_plan(&app, Path::new("/home/test"));
assert_eq!(plan.stable_id, "bat");
assert_eq!(
plan.artifact_paths,
vec![
"/opt/aim/appimages/bat.AppImage".to_owned(),
"/usr/share/applications/aim-bat.desktop".to_owned(),
"/usr/share/icons/hicolor/256x256/apps/bat.png".to_owned(),
]
);
}
#[test]
fn removal_plan_falls_back_to_derived_managed_user_paths() {
let app = AppRecord {
stable_id: "bat".to_owned(),
display_name: "Bat".to_owned(),
source_input: None,
source: None,
installed_version: None,
update_strategy: None,
metadata: Vec::new(),
install: None,
};
let plan = build_removal_plan(&app, Path::new("/home/test"));
assert_eq!(
plan.artifact_paths,
vec![
"/home/test/.local/lib/aim/appimages/bat.AppImage".to_owned(),
"/home/test/.local/share/applications/aim-bat.desktop".to_owned(),
"/home/test/.local/share/icons/hicolor/256x256/apps/bat.png".to_owned(),
]
);
}

View file

@ -19,6 +19,7 @@ fn installed_apps_are_carried_into_review_plan() {
installed_version: None,
update_strategy: None,
metadata: Vec::new(),
install: None,
}];
let plan = build_update_plan(&apps).unwrap();
@ -49,6 +50,7 @@ fn update_plan_uses_alternate_channel_after_preferred_failure() {
}],
}),
metadata: Vec::new(),
install: None,
}];
let plan = build_update_plan(&apps).unwrap();