use std::fs;
use std::path::Path;
use projd_core::{IdentitySource, ProjectKind, render_markdown, scan_path};
use tempfile::TempDir;
#[test]
fn detects_rust_package_identity_from_cargo_manifest() {
let fixture = ProjectFixture::new();
fixture.write(
"Cargo.toml",
"[package]\nname = \"demo-rust\"\nversion = \"1.2.3\"\nedition = \"2024\"\n",
);
let scan = scan_path(fixture.path()).expect("scan succeeds");
assert_eq!(scan.identity.name, "demo-rust");
assert_eq!(scan.identity.version.as_deref(), Some("1.2.3"));
assert_eq!(scan.identity.kind, ProjectKind::RustPackage);
assert_eq!(scan.identity.source, IdentitySource::CargoToml);
}
#[test]
fn detects_rust_workspace_identity_from_workspace_manifest() {
let fixture = ProjectFixture::new();
fixture.write(
"Cargo.toml",
"[workspace]\nmembers = [\"crates/demo\"]\n[workspace.package]\nversion = \"0.4.0\"\n",
);
let scan = scan_path(fixture.path()).expect("scan succeeds");
assert_eq!(scan.identity.name, fixture.name());
assert_eq!(scan.identity.version.as_deref(), Some("0.4.0"));
assert_eq!(scan.identity.kind, ProjectKind::RustWorkspace);
assert_eq!(scan.identity.source, IdentitySource::CargoToml);
}
#[test]
fn detects_node_package_identity_from_package_json() {
let fixture = ProjectFixture::new();
fixture.write(
"package.json",
"{\"name\":\"demo-node\",\"version\":\"2.0.1\"}\n",
);
let scan = scan_path(fixture.path()).expect("scan succeeds");
assert_eq!(scan.identity.name, "demo-node");
assert_eq!(scan.identity.version.as_deref(), Some("2.0.1"));
assert_eq!(scan.identity.kind, ProjectKind::NodePackage);
assert_eq!(scan.identity.source, IdentitySource::PackageJson);
}
#[test]
fn detects_python_project_identity_from_pyproject_toml() {
let fixture = ProjectFixture::new();
fixture.write(
"pyproject.toml",
"[project]\nname = \"demo-python\"\nversion = \"3.1.4\"\n",
);
let scan = scan_path(fixture.path()).expect("scan succeeds");
assert_eq!(scan.identity.name, "demo-python");
assert_eq!(scan.identity.version.as_deref(), Some("3.1.4"));
assert_eq!(scan.identity.kind, ProjectKind::PythonProject);
assert_eq!(scan.identity.source, IdentitySource::PyprojectToml);
}
#[test]
fn falls_back_to_directory_identity() {
let fixture = ProjectFixture::new();
fixture.write("README.md", "# Generic\n");
let scan = scan_path(fixture.path()).expect("scan succeeds");
assert_eq!(scan.identity.name, fixture.name());
assert_eq!(scan.identity.version, None);
assert_eq!(scan.identity.kind, ProjectKind::Generic);
assert_eq!(scan.identity.source, IdentitySource::DirectoryName);
}
#[test]
fn renders_identity_in_markdown_report() {
let fixture = ProjectFixture::new();
fixture.write(
"Cargo.toml",
"[package]\nname = \"demo-rust\"\nversion = \"1.2.3\"\nedition = \"2024\"\n",
);
let scan = scan_path(fixture.path()).expect("scan succeeds");
let report = render_markdown(&scan);
assert!(report.contains("## Identity"));
assert!(report.contains("- Name: `demo-rust`"));
assert!(report.contains("- Version: `1.2.3`"));
assert!(report.contains("- Kind: RustPackage"));
assert!(report.contains("- Source: CargoToml"));
}
struct ProjectFixture {
temp_dir: TempDir,
}
impl ProjectFixture {
fn new() -> Self {
Self {
temp_dir: tempfile::tempdir().expect("create temp dir"),
}
}
fn path(&self) -> &Path {
self.temp_dir.path()
}
fn name(&self) -> String {
self.path()
.file_name()
.and_then(|value| value.to_str())
.expect("temp dir has name")
.to_owned()
}
fn write(&self, relative: &str, content: &str) {
let path = self.path().join(relative);
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).expect("create fixture parent");
}
fs::write(path, content).expect("write fixture file");
}
}