use crate::service::lockfile::{load_lockfile, LockFile, LockPackage};
use crate::service::source::PackageSource;
use crate::service::test_support::{make_app_service, make_app_service_with_search_paths};
fn make_lock_with_pkg(name: &str) -> LockFile {
LockFile {
version: 1,
packages: vec![LockPackage {
name: name.to_string(),
version: None,
source: PackageSource::Installed,
}],
}
}
#[tokio::test]
async fn pkg_list_with_project() {
let tmp = tempfile::tempdir().unwrap();
let project_root = tmp.path();
std::fs::write(
project_root.join("alc.toml"),
"[packages]\nmy_local_pkg = \"*\"\n",
)
.unwrap();
let pkg_dir = project_root.join("my_local_pkg");
std::fs::create_dir_all(&pkg_dir).unwrap();
std::fs::write(pkg_dir.join("init.lua"), "return {}").unwrap();
let lock = LockFile {
version: 1,
packages: vec![LockPackage {
name: "my_local_pkg".to_string(),
version: None,
source: PackageSource::Path {
path: "my_local_pkg".to_string(),
},
}],
};
crate::service::lockfile::save_lockfile(project_root, &lock).unwrap();
let svc = make_app_service().await;
let result = svc
.pkg_list(Some(project_root.to_string_lossy().to_string()))
.await
.unwrap();
let json: serde_json::Value = serde_json::from_str(&result).unwrap();
let packages = json["packages"].as_array().unwrap();
let project_pkg = packages
.iter()
.find(|p| p["name"] == "my_local_pkg")
.expect("my_local_pkg not found in pkg_list output");
assert_eq!(project_pkg["scope"], "project");
assert_eq!(project_pkg["source_type"], "path");
assert_eq!(project_pkg["active"], true);
assert!(json["project_root"].is_string());
assert!(json["lockfile_path"].is_string());
}
#[tokio::test]
async fn pkg_list_no_project_root() {
let svc = make_app_service().await;
let result = svc.pkg_list(None).await.unwrap();
let json: serde_json::Value = serde_json::from_str(&result).unwrap();
assert!(json["packages"].is_array());
}
#[tokio::test]
async fn pkg_remove_project_scope() {
let tmp = tempfile::tempdir().unwrap();
let project_root = tmp.path();
std::fs::write(
project_root.join("alc.toml"),
"[packages]\nmy_local_pkg = \"*\"\n",
)
.unwrap();
let pkg_dir = project_root.join("my_local_pkg");
std::fs::create_dir_all(&pkg_dir).unwrap();
std::fs::write(pkg_dir.join("init.lua"), "return {}").unwrap();
let lock = LockFile {
version: 1,
packages: vec![LockPackage {
name: "my_local_pkg".to_string(),
version: None,
source: PackageSource::Path {
path: "my_local_pkg".to_string(),
},
}],
};
crate::service::lockfile::save_lockfile(project_root, &lock).unwrap();
let svc = make_app_service().await;
let result = svc
.pkg_remove(
"my_local_pkg",
Some(project_root.to_string_lossy().to_string()),
None, )
.await
.unwrap();
let json: serde_json::Value = serde_json::from_str(&result).unwrap();
assert_eq!(json["removed"], "my_local_pkg");
assert!(json["alc_toml"].is_string());
assert!(json["alc_lock"].is_string());
assert!(pkg_dir.exists(), "physical directory was deleted");
let lock_after = load_lockfile(project_root).unwrap().unwrap();
assert!(
lock_after.packages.is_empty(),
"alc.lock still contains the entry"
);
}
#[tokio::test]
async fn pkg_remove_project_scope_not_found_returns_error() {
let tmp = tempfile::tempdir().unwrap();
let project_root = tmp.path();
std::fs::write(
project_root.join("alc.toml"),
"[packages]\nother_pkg = \"*\"\n",
)
.unwrap();
let lock = make_lock_with_pkg("other_pkg");
crate::service::lockfile::save_lockfile(project_root, &lock).unwrap();
let svc = make_app_service().await;
let result = svc
.pkg_remove(
"nonexistent_pkg",
Some(project_root.to_string_lossy().to_string()),
None, )
.await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("not found in alc.lock"));
}
#[tokio::test]
async fn pkg_list_global_unregistered_has_no_source_type() {
let tmp = tempfile::tempdir().unwrap();
let search_dir = tmp.path().join("pkgs");
std::fs::create_dir_all(&search_dir).unwrap();
let pkg_dir = search_dir.join("hand_copied_pkg");
std::fs::create_dir_all(&pkg_dir).unwrap();
std::fs::write(
pkg_dir.join("init.lua"),
"return { meta = { name = 'hand_copied_pkg' } }",
)
.unwrap();
let search_path = crate::service::resolve::SearchPath {
path: search_dir,
source: crate::service::resolve::SearchPathSource::Env,
};
let svc = make_app_service_with_search_paths(vec![search_path]).await;
let result = svc.pkg_list(None).await.unwrap();
let json: serde_json::Value = serde_json::from_str(&result).unwrap();
let packages = json["packages"].as_array().unwrap();
let pkg = packages
.iter()
.find(|p| p["name"] == "hand_copied_pkg")
.expect("hand_copied_pkg not found in pkg_list output");
let pkg_map = pkg
.as_object()
.expect("package entry must be a JSON object");
assert!(
!pkg_map.contains_key("source_type"),
"source_type should be absent for unregistered package, got: {:?}",
pkg_map.get("source_type")
);
assert_eq!(pkg["scope"], "global");
assert_eq!(pkg["active"], true);
}