mirror of
https://github.com/marcoallegretti/WEFT_OS.git
synced 2026-03-27 01:13:09 +00:00
feat(pack): add uninstall subcommand and full install/uninstall tests
weft-pack: - uninstall <app_id>: validates app ID, resolves store root, removes the installed package directory. Fails with an error if the package is not present or the app ID is malformed. - Extracted install_package_to(dir, root) and uninstall_package_from(app_id, root) inner functions so tests can drive them directly without touching process env vars (avoids parallel test env-var races). - install_package / uninstall_package remain the CLI-facing wrappers that call resolve_install_root(). Tests added (2): - install_package_copies_to_store: writes a valid temp package, calls install_package_to, verifies all files are present, confirms a second install fails. - uninstall_package_removes_directory: installs then uninstalls, verifies directory is removed, confirms a second uninstall fails. Both tests use process-ID-derived paths to avoid cross-test collisions.
This commit is contained in:
parent
265868bf67
commit
b2bb76125f
1 changed files with 93 additions and 8 deletions
|
|
@ -47,11 +47,16 @@ fn main() -> anyhow::Result<()> {
|
||||||
let dir = args.get(2).context("usage: weft-pack install <dir>")?;
|
let dir = args.get(2).context("usage: weft-pack install <dir>")?;
|
||||||
install_package(Path::new(dir))?;
|
install_package(Path::new(dir))?;
|
||||||
}
|
}
|
||||||
|
Some("uninstall") => {
|
||||||
|
let app_id = args.get(2).context("usage: weft-pack uninstall <app_id>")?;
|
||||||
|
uninstall_package(app_id)?;
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!("usage:");
|
eprintln!("usage:");
|
||||||
eprintln!(" weft-pack check <dir> validate a package directory");
|
eprintln!(" weft-pack check <dir> validate a package directory");
|
||||||
eprintln!(" weft-pack info <dir> print package metadata");
|
eprintln!(" weft-pack info <dir> print package metadata");
|
||||||
eprintln!(" weft-pack install <dir> install package to app store");
|
eprintln!(" weft-pack install <dir> install package to app store");
|
||||||
|
eprintln!(" weft-pack uninstall <app_id> remove installed package");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -160,13 +165,15 @@ fn resolve_install_root() -> anyhow::Result<PathBuf> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn install_package(dir: &Path) -> anyhow::Result<()> {
|
fn install_package(dir: &Path) -> anyhow::Result<()> {
|
||||||
|
let root = resolve_install_root()?;
|
||||||
|
install_package_to(dir, &root)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_package_to(dir: &Path, store_root: &Path) -> anyhow::Result<()> {
|
||||||
check_package(dir)?;
|
check_package(dir)?;
|
||||||
let manifest = load_manifest(dir)?;
|
let manifest = load_manifest(dir)?;
|
||||||
let app_id = &manifest.package.id;
|
let app_id = &manifest.package.id;
|
||||||
|
|
||||||
let store_root = resolve_install_root()?;
|
|
||||||
let dest = store_root.join(app_id);
|
let dest = store_root.join(app_id);
|
||||||
|
|
||||||
if dest.exists() {
|
if dest.exists() {
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
"package '{}' is already installed at {}; remove it first",
|
"package '{}' is already installed at {}; remove it first",
|
||||||
|
|
@ -174,14 +181,34 @@ fn install_package(dir: &Path) -> anyhow::Result<()> {
|
||||||
dest.display()
|
dest.display()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
copy_dir(dir, &dest)
|
copy_dir(dir, &dest)
|
||||||
.with_context(|| format!("copy {} -> {}", dir.display(), dest.display()))?;
|
.with_context(|| format!("copy {} -> {}", dir.display(), dest.display()))?;
|
||||||
|
|
||||||
println!("installed {} -> {}", app_id, dest.display());
|
println!("installed {} -> {}", app_id, dest.display());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn uninstall_package(app_id: &str) -> anyhow::Result<()> {
|
||||||
|
let root = resolve_install_root()?;
|
||||||
|
uninstall_package_from(app_id, &root)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn uninstall_package_from(app_id: &str, store_root: &Path) -> anyhow::Result<()> {
|
||||||
|
if !is_valid_app_id(app_id) {
|
||||||
|
anyhow::bail!("'{}' is not a valid app ID", app_id);
|
||||||
|
}
|
||||||
|
let target = store_root.join(app_id);
|
||||||
|
if !target.exists() {
|
||||||
|
anyhow::bail!(
|
||||||
|
"package '{}' is not installed at {}",
|
||||||
|
app_id,
|
||||||
|
target.display()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
std::fs::remove_dir_all(&target).with_context(|| format!("remove {}", target.display()))?;
|
||||||
|
println!("uninstalled {}", app_id);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn copy_dir(src: &Path, dst: &Path) -> anyhow::Result<()> {
|
fn copy_dir(src: &Path, dst: &Path) -> anyhow::Result<()> {
|
||||||
std::fs::create_dir_all(dst)?;
|
std::fs::create_dir_all(dst)?;
|
||||||
for entry in std::fs::read_dir(src)? {
|
for entry in std::fs::read_dir(src)? {
|
||||||
|
|
@ -260,6 +287,64 @@ entry = "ui/index.html"
|
||||||
let _ = fs::remove_dir_all(&tmp);
|
let _ = fs::remove_dir_all(&tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn install_package_copies_to_store() {
|
||||||
|
use std::fs;
|
||||||
|
let id = format!("weft.pack.install{}", std::process::id());
|
||||||
|
let src = std::env::temp_dir().join(format!("weft_pack_install_src_{}", id));
|
||||||
|
let store = std::env::temp_dir().join(format!("weft_pack_install_store_{}", id));
|
||||||
|
let ui_dir = src.join("ui");
|
||||||
|
let _ = fs::create_dir_all(&ui_dir);
|
||||||
|
fs::write(src.join("app.wasm"), b"\0asm").unwrap();
|
||||||
|
fs::write(ui_dir.join("index.html"), b"<!DOCTYPE html>").unwrap();
|
||||||
|
let app_id = format!("com.example.t{}", std::process::id());
|
||||||
|
fs::write(
|
||||||
|
src.join("wapp.toml"),
|
||||||
|
format!(
|
||||||
|
"[package]\nid = \"{app_id}\"\nname = \"Test\"\nversion = \"1.0.0\"\n\n\
|
||||||
|
[runtime]\nmodule = \"app.wasm\"\n\n[ui]\nentry = \"ui/index.html\"\n"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let result = install_package_to(&src, &store);
|
||||||
|
assert!(result.is_ok(), "{result:?}");
|
||||||
|
assert!(store.join(&app_id).join("app.wasm").exists());
|
||||||
|
assert!(store.join(&app_id).join("wapp.toml").exists());
|
||||||
|
assert!(store.join(&app_id).join("ui").join("index.html").exists());
|
||||||
|
assert!(install_package_to(&src, &store).is_err());
|
||||||
|
let _ = fs::remove_dir_all(&src);
|
||||||
|
let _ = fs::remove_dir_all(&store);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn uninstall_package_removes_directory() {
|
||||||
|
use std::fs;
|
||||||
|
let id = format!("weft.pack.uninstall{}", std::process::id());
|
||||||
|
let src = std::env::temp_dir().join(format!("weft_pack_uninstall_src_{}", id));
|
||||||
|
let store = std::env::temp_dir().join(format!("weft_pack_uninstall_store_{}", id));
|
||||||
|
let ui_dir = src.join("ui");
|
||||||
|
let _ = fs::create_dir_all(&ui_dir);
|
||||||
|
fs::write(src.join("app.wasm"), b"\0asm").unwrap();
|
||||||
|
fs::write(ui_dir.join("index.html"), b"").unwrap();
|
||||||
|
let app_id = format!("com.example.u{}", std::process::id());
|
||||||
|
fs::write(
|
||||||
|
src.join("wapp.toml"),
|
||||||
|
format!(
|
||||||
|
"[package]\nid = \"{app_id}\"\nname = \"U\"\nversion = \"1.0.0\"\n\n\
|
||||||
|
[runtime]\nmodule = \"app.wasm\"\n\n[ui]\nentry = \"ui/index.html\"\n"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
install_package_to(&src, &store).unwrap();
|
||||||
|
assert!(store.join(&app_id).exists());
|
||||||
|
let result = uninstall_package_from(&app_id, &store);
|
||||||
|
assert!(result.is_ok(), "{result:?}");
|
||||||
|
assert!(!store.join(&app_id).exists());
|
||||||
|
assert!(uninstall_package_from(&app_id, &store).is_err());
|
||||||
|
let _ = fs::remove_dir_all(&src);
|
||||||
|
let _ = fs::remove_dir_all(&store);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_package_invalid_app_id() {
|
fn check_package_invalid_app_id() {
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue