use crate::service::error::CodeNexusError;
use crate::storage::capability::Storage;
use crate::storage::is_table_missing_error;
pub fn resolve_project_id(storage: &dyn Storage, project: &str) -> Result<String, CodeNexusError> {
let projects = match storage.list_projects() {
Ok(projects) => projects,
Err(err) if is_table_missing_error(&err) => {
return Err(CodeNexusError::ProjectNotFound(project.to_string()));
}
Err(err) => return Err(err.into()),
};
let project_id = projects
.iter()
.find(|p| p.name == project)
.map(|p| p.id.clone())
.or_else(|| {
if projects.iter().any(|p| p.id == project) {
Some(project.to_string())
} else {
None
}
});
project_id.ok_or_else(|| CodeNexusError::ProjectNotFound(project.to_string()))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::kit::{build_kit, KitBootstrapConfig, StorageModule};
use tempfile::TempDir;
fn fresh_db_path() -> (TempDir, std::path::PathBuf) {
let dir = TempDir::new().unwrap();
let path = dir.path().join("svc_project_testdb");
(dir, path)
}
fn build_kit_for_db(db: &std::path::Path) -> crate::kit::AsyncKit<crate::kit::AsyncReady> {
let config = KitBootstrapConfig::new(db.to_path_buf());
tokio::runtime::Runtime::new()
.unwrap()
.block_on(build_kit(&config))
.expect("build_kit")
}
fn seed_project(storage: &dyn Storage, id: &str, name: &str) {
storage
.execute(&format!(
"CREATE (:Project {{id: '{id}', name: '{name}', rootPath: '/demo', language: 'rust', fileCount: 1, indexedAt: 1000, lastCommit: 'abc'}});"
))
.expect("create project");
}
#[test]
fn resolve_by_name_returns_id() {
let (_dir, db) = fresh_db_path();
let kit = build_kit_for_db(&db);
let storage = kit.require::<StorageModule>().expect("storage");
seed_project(&*storage, "proj_abc", "my_project");
let id = resolve_project_id(&*storage, "my_project").expect("should resolve");
assert_eq!(id, "proj_abc");
}
#[test]
fn resolve_by_id_returns_id() {
let (_dir, db) = fresh_db_path();
let kit = build_kit_for_db(&db);
let storage = kit.require::<StorageModule>().expect("storage");
seed_project(&*storage, "proj_abc", "my_project");
let id = resolve_project_id(&*storage, "proj_abc").expect("should resolve");
assert_eq!(id, "proj_abc");
}
#[test]
fn resolve_unknown_returns_project_not_found() {
let (_dir, db) = fresh_db_path();
let kit = build_kit_for_db(&db);
let storage = kit.require::<StorageModule>().expect("storage");
seed_project(&*storage, "proj_abc", "my_project");
let err = resolve_project_id(&*storage, "nonexistent").expect_err("should error");
assert!(matches!(err, CodeNexusError::ProjectNotFound(_)));
}
#[test]
fn resolve_name_takes_precedence_over_id() {
let (_dir, db) = fresh_db_path();
let kit = build_kit_for_db(&db);
let storage = kit.require::<StorageModule>().expect("storage");
seed_project(&*storage, "p1", "p2");
seed_project(&*storage, "p2", "other");
let id = resolve_project_id(&*storage, "p2").expect("should resolve");
assert_eq!(id, "p1");
}
#[test]
fn resolve_empty_db_returns_project_not_found() {
let (_dir, db) = fresh_db_path();
let kit = build_kit_for_db(&db);
let storage = kit.require::<StorageModule>().expect("storage");
let err = resolve_project_id(&*storage, "anything").expect_err("should error");
assert!(matches!(err, CodeNexusError::ProjectNotFound(_)));
}
#[test]
fn resolve_when_project_table_dropped_returns_project_not_found() {
let (_dir, db) = fresh_db_path();
let kit = build_kit_for_db(&db);
let storage = kit.require::<StorageModule>().expect("storage");
storage
.execute("DROP TABLE Project;")
.expect("drop project table");
let err = resolve_project_id(&*storage, "anything").expect_err("should error");
assert!(
matches!(err, CodeNexusError::ProjectNotFound(_)),
"expected ProjectNotFound, got: {err:?}"
);
}
}