mod tests {
use std::fs;
use std::path::PathBuf;
use tempfile::TempDir;
use crate::models::{DatasourceId, PackageType};
use crate::parsers::{ClojureDepsEdnParser, ClojureProjectCljParser, PackageParser};
fn create_temp_file(file_name: &str, content: &str) -> (TempDir, PathBuf) {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let file_path = temp_dir.path().join(file_name);
fs::write(&file_path, content).expect("Failed to write temp file");
(temp_dir, file_path)
}
#[test]
fn test_deps_edn_is_match() {
assert!(ClojureDepsEdnParser::is_match(&PathBuf::from(
"/repo/deps.edn"
)));
assert!(!ClojureDepsEdnParser::is_match(&PathBuf::from(
"/repo/project.clj"
)));
assert!(!ClojureDepsEdnParser::is_match(&PathBuf::from(
"/repo/deps.edn.bak"
)));
}
#[test]
fn test_project_clj_is_match() {
assert!(ClojureProjectCljParser::is_match(&PathBuf::from(
"/repo/project.clj"
)));
assert!(!ClojureProjectCljParser::is_match(&PathBuf::from(
"/repo/deps.edn"
)));
assert!(!ClojureProjectCljParser::is_match(&PathBuf::from(
"/repo/core.clj"
)));
}
#[test]
fn test_extract_from_deps_edn_with_top_level_and_alias_deps() {
let content = r#"
{:paths ["src" "resources"]
:deps {org.clojure/clojure {:mvn/version "1.12.0"}
io.github.clojure/tools.build {:git/url "https://github.com/clojure/tools.build.git"
:git/tag "v0.10.5"
:git/sha "abc1234"}
my.local/lib {:local/root "../lib"}
exact/lib {:mvn/version "=1.5.0"}}
:mvn/repos {"clojars" {:url "https://repo.clojars.org/"}}
:aliases {:test {:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}}
:build {:deps {io.github.clojure/tools.cli {:mvn/version "1.1.230"}}}}}
"#;
let (_temp_dir, path) = create_temp_file("deps.edn", content);
let package_data = ClojureDepsEdnParser::extract_first_package(&path);
assert_eq!(package_data.package_type, Some(PackageType::Maven));
assert_eq!(package_data.primary_language.as_deref(), Some("Clojure"));
assert_eq!(
package_data.datasource_id,
Some(DatasourceId::ClojureDepsEdn)
);
assert!(package_data.name.is_none());
assert!(package_data.version.is_none());
let extra_data = package_data
.extra_data
.as_ref()
.expect("missing extra_data");
assert!(extra_data.get("paths").is_some());
assert_eq!(
extra_data
.get("mvn_repos")
.and_then(|value| value.get("clojars"))
.and_then(|value| value.get("url"))
.and_then(|value| value.as_str()),
Some("https://repo.clojars.org/")
);
assert!(extra_data.get("aliases").is_some());
assert_eq!(package_data.dependencies.len(), 6);
}
#[test]
fn test_extract_from_project_clj_literal_metadata_and_profile_dependencies() {
let content = r#"
(defproject org.example/sample "1.0.0"
:description "Sample project"
:url "https://example.org/sample"
:license {:name "Eclipse Public License"
:url "https://www.eclipse.org/legal/epl-v10.html"}
:scm {:url "https://github.com/example/sample"}
:dependencies [[org.clojure/clojure "1.11.1"]
[cheshire "5.12.0"]
["ring/ring-core" "1.12.2" :classifier "tests"]]
:profiles {:dev {:dependencies [[midje "1.10.10"]]}
:provided {:dependencies [[javax.servlet/servlet-api "2.5"]]}
:test {:dependencies [[lambdaisland/kaocha "1.91.1392"]]}})
"#;
let (_temp_dir, path) = create_temp_file("project.clj", content);
let package_data = ClojureProjectCljParser::extract_first_package(&path);
assert_eq!(package_data.package_type, Some(PackageType::Maven));
assert_eq!(package_data.primary_language.as_deref(), Some("Clojure"));
assert_eq!(
package_data.datasource_id,
Some(DatasourceId::ClojureProjectClj)
);
assert_eq!(package_data.namespace.as_deref(), Some("org.example"));
assert_eq!(package_data.name.as_deref(), Some("sample"));
assert_eq!(package_data.version.as_deref(), Some("1.0.0"));
assert_eq!(
package_data.purl.as_deref(),
Some("pkg:maven/org.example/sample@1.0.0")
);
assert_eq!(package_data.description.as_deref(), Some("Sample project"));
assert_eq!(
package_data.homepage_url.as_deref(),
Some("https://example.org/sample")
);
assert_eq!(
package_data.vcs_url.as_deref(),
Some("https://github.com/example/sample")
);
assert_eq!(package_data.dependencies.len(), 6);
}
#[test]
fn test_project_clj_skips_non_literal_constructs() {
let content = r#"
(defproject org.example/dynamic "0.2.0"
:description dynamic-description
:url homepage-url
:dependencies [[org.clojure/clojure "1.11.1"]
[foo/bar dep-version]
~(concat [] [])
[org.valid/lib "1.0.0"]]
:profiles {:dev {:dependencies [[midje "1.10.10"]]}
:test {:dependencies dynamic-test-deps}})
"#;
let (_temp_dir, path) = create_temp_file("project.clj", content);
let package_data = ClojureProjectCljParser::extract_first_package(&path);
assert_eq!(package_data.namespace.as_deref(), Some("org.example"));
assert_eq!(package_data.name.as_deref(), Some("dynamic"));
assert_eq!(package_data.version.as_deref(), Some("0.2.0"));
assert!(package_data.description.is_none());
assert!(package_data.homepage_url.is_none());
assert_eq!(package_data.dependencies.len(), 3);
assert!(
package_data
.dependencies
.iter()
.any(|dep| dep.purl.as_deref() == Some("pkg:maven/midje/midje@1.10.10"))
);
}
#[test]
fn test_graceful_error_handling_for_invalid_forms() {
let (_temp_dir, deps_path) = create_temp_file("deps.edn", "{:deps {foo/bar");
let deps_package = ClojureDepsEdnParser::extract_first_package(&deps_path);
assert_eq!(deps_package.package_type, Some(PackageType::Maven));
assert_eq!(
deps_package.datasource_id,
Some(DatasourceId::ClojureDepsEdn)
);
assert!(deps_package.dependencies.is_empty());
let (_temp_dir, project_path) = create_temp_file("project.clj", "(defproject foo");
let project_package = ClojureProjectCljParser::extract_first_package(&project_path);
assert_eq!(project_package.package_type, Some(PackageType::Maven));
assert_eq!(
project_package.datasource_id,
Some(DatasourceId::ClojureProjectClj)
);
assert!(project_package.name.is_none());
}
#[test]
fn test_deps_edn_allows_commas_as_whitespace() {
let content = r#"
{:paths ["src", "resources"],
:deps {org.clojure/clojure {:mvn/version "1.12.0"},
cheshire {:mvn/version "5.12.0"}}}
"#;
let (_temp_dir, path) = create_temp_file("deps.edn", content);
let package_data = ClojureDepsEdnParser::extract_first_package(&path);
assert_eq!(package_data.dependencies.len(), 2);
assert!(
package_data
.dependencies
.iter()
.any(|dep| dep.purl.as_deref() == Some("pkg:maven/org.clojure/clojure@1.12.0"))
);
assert!(
package_data
.dependencies
.iter()
.any(|dep| dep.purl.as_deref() == Some("pkg:maven/cheshire/cheshire@5.12.0"))
);
}
#[test]
fn test_deps_edn_unsupported_dispatch_macro_falls_back_safely() {
let content = r#"
{:deps {foo/bar {:mvn/version "1.0.0"}}
:aliases {:test #=(eval {:extra-deps {baz/qux {:mvn/version "2.0.0"}}})}}
"#;
let (_temp_dir, path) = create_temp_file("deps.edn", content);
let package_data = ClojureDepsEdnParser::extract_first_package(&path);
assert_eq!(package_data.package_type, Some(PackageType::Maven));
assert_eq!(
package_data.datasource_id,
Some(DatasourceId::ClojureDepsEdn)
);
assert!(package_data.dependencies.is_empty());
}
}