use crate::cache::models::VersionSearchType;
use crate::cache::{DistributionCache, MetadataCache};
use crate::config::KopiConfig;
use crate::models::distribution::Distribution;
use crate::models::metadata::JdkMetadata;
use crate::models::package::{ArchiveType, ChecksumType, PackageType};
use crate::models::platform::{Architecture, OperatingSystem};
use crate::platform::{get_current_architecture, get_current_os, get_foojay_libc_type};
use crate::version::Version;
use crate::version::parser::{ParsedVersionRequest, VersionParser};
use std::str::FromStr;
fn create_test_config() -> KopiConfig {
KopiConfig::new(std::env::temp_dir()).expect("Failed to create test config")
}
fn get_test_platform() -> (String, String) {
(get_current_architecture(), get_current_os())
}
fn create_test_cache() -> MetadataCache {
let mut cache = MetadataCache::new();
let current_arch = get_current_architecture();
let current_os = get_current_os();
let current_libc = get_foojay_libc_type();
let archive_type = if current_os == "windows" {
ArchiveType::Zip
} else {
ArchiveType::TarGz
};
let packages = vec![
JdkMetadata {
id: "test-21".to_string(),
distribution: "temurin".to_string(),
version: Version::new(21, 0, 1),
distribution_version: Version::from_str("21.0.1").unwrap(),
architecture: Architecture::from_str(¤t_arch).unwrap_or(Architecture::X64),
operating_system: OperatingSystem::from_str(¤t_os)
.unwrap_or(OperatingSystem::Linux),
package_type: PackageType::Jdk,
archive_type,
download_url: Some("https://example.com/jdk21.tar.gz".to_string()),
checksum: None,
checksum_type: Some(ChecksumType::Sha256),
size: 100_000_000,
lib_c_type: Some(current_libc.to_string()),
javafx_bundled: false,
term_of_support: Some("lts".to_string()),
release_status: Some("ga".to_string()),
latest_build_available: Some(true),
},
JdkMetadata {
id: "test-17".to_string(),
distribution: "temurin".to_string(),
version: Version::new(17, 0, 9),
distribution_version: Version::from_str("17.0.9").unwrap(),
architecture: Architecture::from_str(¤t_arch).unwrap_or(Architecture::X64),
operating_system: OperatingSystem::from_str(¤t_os)
.unwrap_or(OperatingSystem::Linux),
package_type: PackageType::Jdk,
archive_type,
download_url: Some("https://example.com/jdk17.tar.gz".to_string()),
checksum: None,
checksum_type: Some(ChecksumType::Sha256),
size: 90_000_000,
lib_c_type: Some(current_libc.to_string()),
javafx_bundled: false,
term_of_support: Some("lts".to_string()),
release_status: Some("ga".to_string()),
latest_build_available: Some(true),
},
];
let dist_cache = DistributionCache {
distribution: Distribution::Temurin,
display_name: "Eclipse Temurin".to_string(),
packages,
};
cache
.distributions
.insert("temurin".to_string(), dist_cache);
cache
}
#[test]
fn test_search_by_major_version() {
let cache = create_test_cache();
let config = create_test_config();
let parser = VersionParser::new(&config);
let parsed_request = parser.parse("21").unwrap();
let results = cache
.search(&parsed_request, VersionSearchType::Auto)
.unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0].package.version.major(), 21);
}
#[test]
fn test_search_with_distribution() {
let cache = create_test_cache();
let config = create_test_config();
let parser = VersionParser::new(&config);
let parsed_request = parser.parse("temurin@17").unwrap();
let results = cache
.search(&parsed_request, VersionSearchType::Auto)
.unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0].package.version.major(), 17);
assert_eq!(results[0].distribution, "temurin");
}
#[test]
fn test_search_with_platform_filter() {
let cache = create_test_cache();
let config = create_test_config();
let parser = VersionParser::new(&config);
let parsed_request = parser.parse("17").unwrap();
let results = cache
.search(&parsed_request, VersionSearchType::Auto)
.unwrap();
assert_eq!(results.len(), 1);
let (test_arch, test_os) = get_test_platform();
assert_eq!(results[0].package.architecture.to_string(), test_arch);
assert_eq!(results[0].package.operating_system.to_string(), test_os);
}
#[test]
fn test_lookup() {
let cache = create_test_cache();
let (test_arch, test_os) = get_test_platform();
let package = cache.lookup(
&Distribution::Temurin,
"21.0.1",
&test_arch,
&test_os,
None,
None,
);
assert!(package.is_some());
assert_eq!(package.unwrap().version.to_string(), "21.0.1");
}
#[test]
fn test_search_distribution_only() {
let cache = create_test_cache();
let parsed_request = ParsedVersionRequest {
version: None,
distribution: Some(Distribution::Temurin),
package_type: None,
latest: false,
};
let results = cache
.search(&parsed_request, VersionSearchType::Auto)
.unwrap();
assert_eq!(results.len(), 2);
assert!(results.iter().all(|r| r.distribution == "temurin"));
}
#[test]
fn test_search_latest() {
let cache = create_test_cache();
let parsed_request = ParsedVersionRequest {
version: None,
distribution: None,
package_type: None,
latest: true,
};
let results = cache
.search(&parsed_request, VersionSearchType::Auto)
.unwrap();
assert_eq!(results.len(), 1); assert_eq!(results[0].package.version.major(), 21); }
#[test]
fn test_search_latest_with_distribution() {
let cache = create_test_cache();
let parsed_request = ParsedVersionRequest {
version: None,
distribution: Some(Distribution::Temurin),
package_type: None,
latest: true,
};
let results = cache
.search(&parsed_request, VersionSearchType::Auto)
.unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0].package.version.major(), 21);
assert_eq!(results[0].distribution, "temurin");
}
#[test]
fn test_search_with_package_type_filter() {
let cache = create_test_cache();
let parsed_request = ParsedVersionRequest {
version: None,
distribution: Some(Distribution::Temurin),
package_type: Some(PackageType::Jdk),
latest: false,
};
let results = cache
.search(&parsed_request, VersionSearchType::Auto)
.unwrap();
assert!(
results
.iter()
.all(|r| r.package.package_type == PackageType::Jdk)
);
}
#[test]
fn test_search_no_cache() {
let config = create_test_config();
let cache = MetadataCache::new();
let parser = VersionParser::new(&config);
let parsed_request = parser.parse("21").unwrap();
let results = cache
.search(&parsed_request, VersionSearchType::Auto)
.unwrap();
assert_eq!(results.len(), 0);
}
#[test]
fn test_search_invalid_version() {
let config = create_test_config();
let parser = VersionParser::new(&config);
let result = parser.parse("invalid@version@format");
assert!(result.is_err());
}
#[test]
fn test_search_non_existent_distribution() {
let cache = create_test_cache();
let config = create_test_config();
let parser = VersionParser::new(&config);
let parsed_request = parser.parse("corretto@21").unwrap();
let results = cache
.search(&parsed_request, VersionSearchType::Auto)
.unwrap();
assert_eq!(results.len(), 0);
}
#[test]
fn test_search_non_existent_version() {
let cache = create_test_cache();
let config = create_test_config();
let parser = VersionParser::new(&config);
let parsed_request = parser.parse("99").unwrap();
let results = cache
.search(&parsed_request, VersionSearchType::Auto)
.unwrap();
assert_eq!(results.len(), 0);
}
#[test]
fn test_platform_filter_no_match() {
let cache = create_test_cache();
let config = create_test_config();
let parser = VersionParser::new(&config);
let parsed_request = parser.parse("21").unwrap();
let results = cache
.search(&parsed_request, VersionSearchType::Auto)
.unwrap();
let current_arch = get_current_architecture();
let expected_arch = Architecture::from_str(¤t_arch).unwrap_or(Architecture::X64);
assert!(
results
.iter()
.all(|r| r.package.architecture == expected_arch)
);
}
#[test]
fn test_platform_filter_lib_c_mismatch() {
let cache = create_test_cache();
let config = create_test_config();
let current_libc = get_foojay_libc_type();
let parser = VersionParser::new(&config);
let parsed_request = parser.parse("21").unwrap();
let results = cache
.search(&parsed_request, VersionSearchType::Auto)
.unwrap();
assert!(
results
.iter()
.all(|r| r.package.lib_c_type == Some(current_libc.to_string()))
);
}
#[test]
fn test_platform_filter_missing_lib_c() {
let mut cache = create_test_cache();
let config = create_test_config();
if let Some(dist_cache) = cache.distributions.get_mut("temurin") {
let mut package = dist_cache.packages[0].clone();
package.id = "test-no-libc".to_string();
package.lib_c_type = None;
dist_cache.packages.push(package);
}
let parser = VersionParser::new(&config);
let parsed_request = parser.parse("21").unwrap();
let results = cache
.search(&parsed_request, VersionSearchType::Auto)
.unwrap();
assert_eq!(results.len(), 2);
assert_eq!(
results
.iter()
.filter(|r| r.package.lib_c_type.is_some())
.count(),
1
);
assert_eq!(
results
.iter()
.filter(|r| r.package.lib_c_type.is_none())
.count(),
1
);
}
#[test]
fn test_lookup_single_match() {
let cache = create_test_cache();
let (test_arch, test_os) = get_test_platform();
let package = cache.lookup(
&Distribution::Temurin,
"21.0.1",
&test_arch,
&test_os,
None,
None,
);
assert!(package.is_some());
assert_eq!(package.unwrap().version.to_string(), "21.0.1");
}
#[test]
fn test_lookup_multiple_packages() {
let mut cache = create_test_cache();
if let Some(dist_cache) = cache.distributions.get_mut("temurin") {
let mut jre_package = dist_cache.packages[0].clone();
jre_package.id = "test-jre".to_string();
jre_package.package_type = PackageType::Jre;
dist_cache.packages.push(jre_package);
}
let (test_arch, test_os) = get_test_platform();
let jdk_package = cache.lookup(
&Distribution::Temurin,
"21.0.1",
&test_arch,
&test_os,
Some(&PackageType::Jdk),
None,
);
assert!(jdk_package.is_some());
assert_eq!(jdk_package.unwrap().package_type, PackageType::Jdk);
let jre_package = cache.lookup(
&Distribution::Temurin,
"21.0.1",
&test_arch,
&test_os,
Some(&PackageType::Jre),
None,
);
assert!(jre_package.is_some());
assert_eq!(jre_package.unwrap().package_type, PackageType::Jre);
}
#[test]
fn test_lookup_with_requested_type() {
let mut cache = create_test_cache();
if let Some(dist_cache) = cache.distributions.get_mut("temurin") {
let mut jre_package = dist_cache.packages[0].clone();
jre_package.id = "test-jre".to_string();
jre_package.package_type = PackageType::Jre;
dist_cache.packages.push(jre_package);
}
let (test_arch, test_os) = get_test_platform();
let package = cache.lookup(
&Distribution::Temurin,
"21.0.1",
&test_arch,
&test_os,
Some(&PackageType::Jre),
None,
);
assert!(package.is_some());
assert_eq!(package.unwrap().package_type, PackageType::Jre);
}
#[test]
fn test_empty_cache() {
let cache = MetadataCache::new();
let config = create_test_config();
let parser = VersionParser::new(&config);
let parsed_request = parser.parse("21").unwrap();
let results = cache
.search(&parsed_request, VersionSearchType::Auto)
.unwrap();
assert_eq!(results.len(), 0);
let (test_arch, test_os) = get_test_platform();
let exact = cache.lookup(
&Distribution::Temurin,
"21.0.1",
&test_arch,
&test_os,
None,
None,
);
assert!(exact.is_none());
}
#[test]
fn test_latest_with_version_filter() {
let mut cache = create_test_cache();
if let Some(dist_cache) = cache.distributions.get_mut("temurin") {
let mut v21_0_2 = dist_cache.packages[0].clone();
v21_0_2.id = "test-21.0.2".to_string();
v21_0_2.version = Version::new(21, 0, 2);
v21_0_2.distribution_version = Version::from_str("21.0.2").unwrap();
dist_cache.packages.push(v21_0_2);
let mut v22 = dist_cache.packages[0].clone();
v22.id = "test-22".to_string();
v22.version = Version::new(22, 0, 0);
v22.distribution_version = Version::from_str("22.0.0").unwrap();
dist_cache.packages.push(v22);
}
let parsed_request = ParsedVersionRequest {
version: Some(Version::from_str("21").unwrap()),
distribution: None,
package_type: None,
latest: true,
};
let results = cache
.search(&parsed_request, VersionSearchType::Auto)
.unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0].package.version.to_string(), "21.0.2");
}
#[test]
fn test_lookup_with_javafx_filter() {
let mut cache = MetadataCache::new();
let current_arch = get_current_architecture();
let current_os = get_current_os();
let current_libc = get_foojay_libc_type();
let archive_type = if current_os == "windows" {
ArchiveType::Zip
} else {
ArchiveType::TarGz
};
let packages = vec![
JdkMetadata {
id: "liberica-21-no-fx".to_string(),
distribution: "liberica".to_string(),
version: Version::new(21, 0, 1),
distribution_version: Version::from_str("21.0.1").unwrap(),
architecture: Architecture::from_str(¤t_arch).unwrap_or(Architecture::X64),
operating_system: OperatingSystem::from_str(¤t_os)
.unwrap_or(OperatingSystem::Linux),
package_type: PackageType::Jdk,
archive_type,
download_url: Some("https://example.com/liberica-21.tar.gz".to_string()),
checksum: None,
checksum_type: Some(ChecksumType::Sha256),
size: 200_000_000,
lib_c_type: Some(current_libc.to_string()),
javafx_bundled: false,
term_of_support: Some("lts".to_string()),
release_status: Some("ga".to_string()),
latest_build_available: Some(true),
},
JdkMetadata {
id: "liberica-21-with-fx".to_string(),
distribution: "liberica".to_string(),
version: Version::new(21, 0, 1),
distribution_version: Version::from_str("21.0.1").unwrap(),
architecture: Architecture::from_str(¤t_arch).unwrap_or(Architecture::X64),
operating_system: OperatingSystem::from_str(¤t_os)
.unwrap_or(OperatingSystem::Linux),
package_type: PackageType::Jdk,
archive_type,
download_url: Some("https://example.com/liberica-21-fx.tar.gz".to_string()),
checksum: None,
checksum_type: Some(ChecksumType::Sha256),
size: 250_000_000, lib_c_type: Some(current_libc.to_string()),
javafx_bundled: true,
term_of_support: Some("lts".to_string()),
release_status: Some("ga".to_string()),
latest_build_available: Some(true),
},
];
let dist = DistributionCache {
distribution: Distribution::Liberica,
display_name: "BellSoft Liberica".to_string(),
packages,
};
cache.distributions.insert("liberica".to_string(), dist);
let (test_arch, test_os) = get_test_platform();
let without_fx = cache.lookup(
&Distribution::Liberica,
"21.0.1",
&test_arch,
&test_os,
None,
Some(false),
);
assert!(without_fx.is_some());
assert_eq!(without_fx.as_ref().unwrap().id, "liberica-21-no-fx");
assert!(!without_fx.unwrap().javafx_bundled);
let with_fx = cache.lookup(
&Distribution::Liberica,
"21.0.1",
&test_arch,
&test_os,
None,
Some(true),
);
assert!(with_fx.is_some());
assert_eq!(with_fx.as_ref().unwrap().id, "liberica-21-with-fx");
assert!(with_fx.unwrap().javafx_bundled);
let no_preference = cache.lookup(
&Distribution::Liberica,
"21.0.1",
&test_arch,
&test_os,
None,
None,
);
assert!(no_preference.is_some());
}
#[test]
fn test_detect_version_type() {
assert_eq!(
MetadataCache::detect_version_type("21"),
VersionSearchType::JavaVersion
);
assert_eq!(
MetadataCache::detect_version_type("21.0"),
VersionSearchType::JavaVersion
);
assert_eq!(
MetadataCache::detect_version_type("21.0.1"),
VersionSearchType::JavaVersion
);
assert_eq!(
MetadataCache::detect_version_type("21.0.1+7"),
VersionSearchType::JavaVersion
);
assert_eq!(
MetadataCache::detect_version_type("21.0.7.6"),
VersionSearchType::DistributionVersion
);
assert_eq!(
MetadataCache::detect_version_type("21.0.7.6.1"),
VersionSearchType::DistributionVersion
);
assert_eq!(
MetadataCache::detect_version_type("21.0.7.0.7.6"),
VersionSearchType::DistributionVersion
);
assert_eq!(
MetadataCache::detect_version_type("21.0.1+9.1"),
VersionSearchType::DistributionVersion
);
assert_eq!(
MetadataCache::detect_version_type("21.0.1+LTS"),
VersionSearchType::DistributionVersion
);
}
#[test]
fn test_search_by_distribution_version() {
let mut cache = create_test_cache();
if let Some(dist_cache) = cache.distributions.get_mut("temurin") {
let mut corretto_pkg = dist_cache.packages[0].clone();
corretto_pkg.id = "corretto-21".to_string();
corretto_pkg.distribution = "corretto".to_string();
corretto_pkg.distribution_version = Version::from_str("21.0.7.6.1").unwrap();
dist_cache.packages.push(corretto_pkg);
let mut dragonwell_pkg = dist_cache.packages[0].clone();
dragonwell_pkg.id = "dragonwell-21".to_string();
dragonwell_pkg.distribution = "dragonwell".to_string();
dragonwell_pkg.distribution_version = Version::from_str("21.0.7.0.7.6").unwrap();
dist_cache.packages.push(dragonwell_pkg);
}
let request = ParsedVersionRequest {
version: Some(Version::from_str("21.0.7.6").unwrap()),
distribution: None,
package_type: None,
latest: false,
};
let results = cache.search(&request, VersionSearchType::Auto).unwrap();
assert_eq!(results.len(), 1);
assert_eq!(
results[0].package.distribution_version,
Version::from_str("21.0.7.6.1").unwrap()
);
let results = cache
.search(&request, VersionSearchType::DistributionVersion)
.unwrap();
assert_eq!(results.len(), 1);
assert_eq!(
results[0].package.distribution_version,
Version::from_str("21.0.7.6.1").unwrap()
);
let request = ParsedVersionRequest {
version: Some(Version::from_str("21.0.7.0.7").unwrap()),
distribution: None,
package_type: None,
latest: false,
};
let results = cache
.search(&request, VersionSearchType::DistributionVersion)
.unwrap();
assert_eq!(results.len(), 1);
assert_eq!(
results[0].package.distribution_version,
Version::from_str("21.0.7.0.7.6").unwrap()
);
}
#[test]
fn test_search_forced_java_version() {
let mut cache = create_test_cache();
if let Some(dist_cache) = cache.distributions.get_mut("temurin") {
let mut pkg = dist_cache.packages[0].clone();
pkg.id = "extended-21".to_string();
pkg.distribution_version = Version::from_str("21.0.1.9.1").unwrap(); dist_cache.packages.push(pkg);
}
let request = ParsedVersionRequest {
version: Some(Version::from_str("21.0.1").unwrap()),
distribution: None,
package_type: None,
latest: false,
};
let results = cache
.search(&request, VersionSearchType::JavaVersion)
.unwrap();
assert_eq!(results.len(), 2); }
#[test]
fn test_distribution_version_boundary_matching() {
let mut cache = create_test_cache();
if let Some(dist_cache) = cache.distributions.get_mut("temurin") {
dist_cache.packages.clear();
let current_arch = get_current_architecture();
let current_os = get_current_os();
let current_libc = get_foojay_libc_type();
let archive_type = if current_os == "windows" {
ArchiveType::Zip
} else {
ArchiveType::TarGz
};
let base_pkg = JdkMetadata {
id: "test".to_string(),
distribution: "corretto".to_string(),
version: Version::new(21, 0, 7),
distribution_version: Version::from_str("21.0.7").unwrap(),
architecture: Architecture::from_str(¤t_arch).unwrap_or(Architecture::X64),
operating_system: OperatingSystem::from_str(¤t_os)
.unwrap_or(OperatingSystem::Linux),
package_type: PackageType::Jdk,
archive_type,
download_url: Some("https://example.com/jdk.tar.gz".to_string()),
checksum: None,
checksum_type: Some(ChecksumType::Sha256),
size: 100_000_000,
lib_c_type: Some(current_libc.to_string()),
javafx_bundled: false,
term_of_support: Some("lts".to_string()),
release_status: Some("ga".to_string()),
latest_build_available: Some(true),
};
let mut pkg1 = base_pkg.clone();
pkg1.id = "v1".to_string();
pkg1.distribution_version = Version::from_str("21.0.7").unwrap();
dist_cache.packages.push(pkg1);
let mut pkg2 = base_pkg.clone();
pkg2.id = "v2".to_string();
pkg2.distribution_version = Version::from_str("21.0.7.1").unwrap();
dist_cache.packages.push(pkg2);
let mut pkg3 = base_pkg.clone();
pkg3.id = "v3".to_string();
pkg3.distribution_version = Version::from_str("21.0.71").unwrap();
dist_cache.packages.push(pkg3);
}
let request = ParsedVersionRequest {
version: Some(Version::from_str("21.0.7").unwrap()),
distribution: None,
package_type: None,
latest: false,
};
let results = cache
.search(&request, VersionSearchType::DistributionVersion)
.unwrap();
assert_eq!(results.len(), 2);
assert!(
results
.iter()
.any(|r| r.package.distribution_version == Version::from_str("21.0.7").unwrap())
);
assert!(
results
.iter()
.any(|r| r.package.distribution_version == Version::from_str("21.0.7.1").unwrap())
);
assert!(
!results
.iter()
.any(|r| r.package.distribution_version == Version::from_str("21.0.71").unwrap())
);
}