aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests/package/version/version_selector_test.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/tests/package/version/version_selector_test.rs')
-rw-r--r--crates/shirabe/tests/package/version/version_selector_test.rs521
1 files changed, 495 insertions, 26 deletions
diff --git a/crates/shirabe/tests/package/version/version_selector_test.rs b/crates/shirabe/tests/package/version/version_selector_test.rs
index fa288cd..f48a6ce 100644
--- a/crates/shirabe/tests/package/version/version_selector_test.rs
+++ b/crates/shirabe/tests/package/version/version_selector_test.rs
@@ -1,77 +1,546 @@
//! ref: composer/tests/Composer/Test/Package/Version/VersionSelectorTest.php
-// VersionSelector ranks candidate packages whose versions/constraints are parsed through a
-// look-around regex the regex crate cannot compile; the setup also mocks a repository.
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use indexmap::IndexMap;
+use shirabe::filter::platform_requirement_filter::PlatformRequirementFilterFactory;
+use shirabe::io::BufferIO;
+use shirabe::io::IOInterface;
+use shirabe::package::BasePackageHandle;
+use shirabe::package::CompleteAliasPackageHandle;
+use shirabe::package::CompletePackageHandle;
+use shirabe::package::Link;
+use shirabe::package::PackageInterfaceHandle;
+use shirabe::package::version::VersionSelector;
+use shirabe::package::version::version_parser::VersionParser;
+use shirabe::repository::PlatformRepository;
+use shirabe::repository::RepositorySetInterface;
+use shirabe_php_shim::PhpMixed;
+use shirabe_semver::constraint::AnyConstraint;
+
+use shirabe_external_packages::symfony::console::output::output_interface;
+
+use crate::test_case::get_package;
+
+mockall::mock! {
+ RepositorySet {}
+ impl RepositorySetInterface for RepositorySet {
+ fn find_packages(
+ &self,
+ name: &str,
+ constraint: Option<AnyConstraint>,
+ flags: i64,
+ ) -> anyhow::Result<Vec<BasePackageHandle>>;
+ }
+}
+
+// `RepositorySetInterface` requires `Debug`; mockall does not generate it.
+impl std::fmt::Debug for MockRepositorySet {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str("MockRepositorySet")
+ }
+}
+
+fn into_seam(mock: MockRepositorySet) -> Rc<RefCell<dyn RepositorySetInterface>> {
+ Rc::new(RefCell::new(mock))
+}
+
+/// Mirrors PHPUnit `assertSame($expected, $best)`: object identity, not value equality.
+fn assert_same(
+ best: &Option<PackageInterfaceHandle>,
+ expected: &PackageInterfaceHandle,
+ msg: &str,
+) {
+ let best = best
+ .as_ref()
+ .unwrap_or_else(|| panic!("{msg}: expected Some(_), got None"));
+ assert!(best.ptr_eq(expected), "{msg}");
+}
+
+fn require_link(package_name: &str, target: &str, pretty_constraint: &str) -> Link {
+ let parser = VersionParser::new();
+ Link::new(
+ package_name.to_string(),
+ target.to_string(),
+ parser.parse_constraints(pretty_constraint).unwrap(),
+ Some(Link::TYPE_REQUIRE.to_string()),
+ pretty_constraint.to_string(),
+ )
+}
+
+fn find_best(
+ version_selector: &mut VersionSelector,
+ package_name: &str,
+ preferred_stability: &str,
+ platform_requirement_filter: Option<
+ std::rc::Rc<
+ dyn shirabe::filter::platform_requirement_filter::PlatformRequirementFilterInterface,
+ >,
+ >,
+ io: Option<Rc<RefCell<dyn IOInterface>>>,
+) -> Option<PackageInterfaceHandle> {
+ version_selector
+ .find_best_candidate(
+ package_name,
+ None,
+ preferred_stability,
+ platform_requirement_filter,
+ 0,
+ io,
+ PhpMixed::Bool(true),
+ )
+ .unwrap()
+}
+
#[test]
-#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"]
fn test_latest_version_is_returned() {
- todo!()
+ let package_name = "foo/bar";
+
+ let package1 = get_package("foo/bar", "1.2.1");
+ let package2 = get_package("foo/bar", "1.2.2");
+ let package3 = get_package("foo/bar", "1.2.0");
+ let packages = vec![package1.clone(), package2.clone(), package3.clone()];
+
+ let mut repository_set = MockRepositorySet::new();
+ repository_set
+ .expect_find_packages()
+ .times(1)
+ .returning_st(move |_, _, _| Ok(packages.clone()));
+
+ let mut version_selector = VersionSelector::new(into_seam(repository_set), None).unwrap();
+ let best = find_best(&mut version_selector, package_name, "stable", None, None);
+
+ // 1.2.2 should be returned because it's the latest of the returned versions
+ assert_same(&best, &package2, "Latest version should be 1.2.2");
}
#[test]
-#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"]
+#[ignore = "PlatformRepository initialization calls shirabe_php_shim::runtime::constant() which is still todo!(); unrelated to the RepositorySet seam"]
fn test_latest_version_is_returned_that_matches_php_requirements() {
- todo!()
+ let package_name = "foo/bar";
+
+ let mut overrides: IndexMap<String, PhpMixed> = IndexMap::new();
+ overrides.insert("php".to_string(), PhpMixed::String("5.5.0".to_string()));
+ let mut platform = PlatformRepository::new(vec![], overrides).unwrap();
+
+ let package0 = get_package("foo/bar", "0.9.0");
+ package0.__set_requires(IndexMap::from([(
+ "php".to_string(),
+ require_link(package_name, "php", ">=5.6"),
+ )]));
+ let package1 = get_package("foo/bar", "1.0.0");
+ package1.__set_requires(IndexMap::from([(
+ "php".to_string(),
+ require_link(package_name, "php", ">=5.4"),
+ )]));
+ let package2 = get_package("foo/bar", "2.0.0");
+ package2.__set_requires(IndexMap::from([(
+ "php".to_string(),
+ require_link(package_name, "php", ">=5.6"),
+ )]));
+ let package3 = get_package("foo/bar", "2.1.0");
+ package3.__set_requires(IndexMap::from([(
+ "php".to_string(),
+ require_link(package_name, "php", ">=5.6"),
+ )]));
+ let packages = vec![
+ package0.clone(),
+ package1.clone(),
+ package2.clone(),
+ package3.clone(),
+ ];
+
+ let mut repository_set = MockRepositorySet::new();
+ repository_set
+ .expect_find_packages()
+ .times(3)
+ .returning_st(move |_, _, _| Ok(packages.clone()));
+
+ let mut version_selector =
+ VersionSelector::new(into_seam(repository_set), Some(&mut platform)).unwrap();
+
+ let io = Rc::new(RefCell::new(
+ BufferIO::new(String::new(), output_interface::VERBOSITY_NORMAL, None).unwrap(),
+ ));
+ let io_dyn: Rc<RefCell<dyn IOInterface>> = io.clone();
+ let best = find_best(
+ &mut version_selector,
+ package_name,
+ "stable",
+ None,
+ Some(io_dyn),
+ );
+ assert_same(
+ &best,
+ &package1,
+ "Latest version supporting php 5.5 should be returned (1.0.0)",
+ );
+ assert_eq!(
+ "<warning>Cannot use foo/bar's latest version 2.1.0 as it requires php >=5.6 which is not satisfied by your platform.\n",
+ io.borrow().get_output()
+ );
+
+ let io = Rc::new(RefCell::new(
+ BufferIO::new(String::new(), output_interface::VERBOSITY_VERBOSE, None).unwrap(),
+ ));
+ let io_dyn: Rc<RefCell<dyn IOInterface>> = io.clone();
+ let best = find_best(
+ &mut version_selector,
+ package_name,
+ "stable",
+ None,
+ Some(io_dyn),
+ );
+ assert_same(
+ &best,
+ &package1,
+ "Latest version supporting php 5.5 should be returned (1.0.0)",
+ );
+ assert_eq!(
+ "<warning>Cannot use foo/bar's latest version 2.1.0 as it requires php >=5.6 which is not satisfied by your platform.\n\
+ <warning>Cannot use foo/bar 2.0.0 as it requires php >=5.6 which is not satisfied by your platform.\n",
+ io.borrow().get_output()
+ );
+
+ let best = find_best(
+ &mut version_selector,
+ package_name,
+ "stable",
+ Some(PlatformRequirementFilterFactory::ignore_all()),
+ None,
+ );
+ assert_same(
+ &best,
+ &package3,
+ "Latest version should be returned when ignoring platform reqs (2.1.0)",
+ );
}
#[test]
-#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"]
+#[ignore = "PlatformRepository initialization calls shirabe_php_shim::runtime::constant() which is still todo!(); unrelated to the RepositorySet seam"]
fn test_latest_version_is_returned_that_matches_ext_requirements() {
- todo!()
+ let package_name = "foo/bar";
+
+ let mut overrides: IndexMap<String, PhpMixed> = IndexMap::new();
+ overrides.insert("ext-zip".to_string(), PhpMixed::String("5.3.0".to_string()));
+ let mut platform = PlatformRepository::new(vec![], overrides).unwrap();
+
+ let package1 = get_package("foo/bar", "1.0.0");
+ package1.__set_requires(IndexMap::from([(
+ "ext-zip".to_string(),
+ require_link(package_name, "ext-zip", "^5.2"),
+ )]));
+ let package2 = get_package("foo/bar", "2.0.0");
+ package2.__set_requires(IndexMap::from([(
+ "ext-zip".to_string(),
+ require_link(package_name, "ext-zip", "^5.4"),
+ )]));
+ let packages = vec![package1.clone(), package2.clone()];
+
+ let mut repository_set = MockRepositorySet::new();
+ repository_set
+ .expect_find_packages()
+ .times(2)
+ .returning_st(move |_, _, _| Ok(packages.clone()));
+
+ let mut version_selector =
+ VersionSelector::new(into_seam(repository_set), Some(&mut platform)).unwrap();
+
+ let best = find_best(&mut version_selector, package_name, "stable", None, None);
+ assert_same(
+ &best,
+ &package1,
+ "Latest version supporting ext-zip 5.3.0 should be returned (1.0.0)",
+ );
+ let best = find_best(
+ &mut version_selector,
+ package_name,
+ "stable",
+ Some(PlatformRequirementFilterFactory::ignore_all()),
+ None,
+ );
+ assert_same(
+ &best,
+ &package2,
+ "Latest version should be returned when ignoring platform reqs (2.0.0)",
+ );
}
#[test]
-#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"]
+#[ignore = "PlatformRepository initialization calls shirabe_php_shim::runtime::constant() which is still todo!(); unrelated to the RepositorySet seam"]
fn test_latest_version_is_returned_that_matches_platform_ext() {
- todo!()
+ let package_name = "foo/bar";
+
+ let mut platform = PlatformRepository::new(vec![], IndexMap::new()).unwrap();
+
+ let package1 = get_package("foo/bar", "1.0.0");
+ let package2 = get_package("foo/bar", "2.0.0");
+ package2.__set_requires(IndexMap::from([(
+ "ext-barfoo".to_string(),
+ require_link(package_name, "ext-barfoo", "*"),
+ )]));
+ let packages = vec![package1.clone(), package2.clone()];
+
+ let mut repository_set = MockRepositorySet::new();
+ repository_set
+ .expect_find_packages()
+ .times(2)
+ .returning_st(move |_, _, _| Ok(packages.clone()));
+
+ let mut version_selector =
+ VersionSelector::new(into_seam(repository_set), Some(&mut platform)).unwrap();
+
+ let best = find_best(&mut version_selector, package_name, "stable", None, None);
+ assert_same(
+ &best,
+ &package1,
+ "Latest version not requiring ext-barfoo should be returned (1.0.0)",
+ );
+ let best = find_best(
+ &mut version_selector,
+ package_name,
+ "stable",
+ Some(PlatformRequirementFilterFactory::ignore_all()),
+ None,
+ );
+ assert_same(
+ &best,
+ &package2,
+ "Latest version should be returned when ignoring platform reqs (2.0.0)",
+ );
}
#[test]
-#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"]
+#[ignore = "PlatformRepository initialization calls shirabe_php_shim::runtime::constant() which is still todo!(); unrelated to the RepositorySet seam"]
fn test_latest_version_is_returned_that_matches_composer_requirements() {
- todo!()
+ let package_name = "foo/bar";
+
+ let mut overrides: IndexMap<String, PhpMixed> = IndexMap::new();
+ overrides.insert(
+ "composer-runtime-api".to_string(),
+ PhpMixed::String("1.0.0".to_string()),
+ );
+ let mut platform = PlatformRepository::new(vec![], overrides).unwrap();
+
+ let package1 = get_package("foo/bar", "1.0.0");
+ package1.__set_requires(IndexMap::from([(
+ "composer-runtime-api".to_string(),
+ require_link(package_name, "composer-runtime-api", "^1.0"),
+ )]));
+ let package2 = get_package("foo/bar", "1.1.0");
+ package2.__set_requires(IndexMap::from([(
+ "composer-runtime-api".to_string(),
+ require_link(package_name, "composer-runtime-api", "^2.0"),
+ )]));
+ let packages = vec![package1.clone(), package2.clone()];
+
+ let mut repository_set = MockRepositorySet::new();
+ repository_set
+ .expect_find_packages()
+ .times(2)
+ .returning_st(move |_, _, _| Ok(packages.clone()));
+
+ let mut version_selector =
+ VersionSelector::new(into_seam(repository_set), Some(&mut platform)).unwrap();
+
+ let best = find_best(&mut version_selector, package_name, "stable", None, None);
+ assert_same(
+ &best,
+ &package1,
+ "Latest version supporting composer 1 should be returned (1.0.0)",
+ );
+ let best = find_best(
+ &mut version_selector,
+ package_name,
+ "stable",
+ Some(PlatformRequirementFilterFactory::ignore_all()),
+ None,
+ );
+ assert_same(
+ &best,
+ &package2,
+ "Latest version should be returned when ignoring platform reqs (1.1.0)",
+ );
}
#[test]
-#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"]
fn test_most_stable_version_is_returned() {
- todo!()
+ let package_name = "foo/bar";
+
+ let package1 = get_package("foo/bar", "1.0.0");
+ let package2 = get_package("foo/bar", "1.1.0-beta");
+ let packages = vec![package1.clone(), package2.clone()];
+
+ let mut repository_set = MockRepositorySet::new();
+ repository_set
+ .expect_find_packages()
+ .times(1)
+ .returning_st(move |_, _, _| Ok(packages.clone()));
+
+ let mut version_selector = VersionSelector::new(into_seam(repository_set), None).unwrap();
+ let best = find_best(&mut version_selector, package_name, "stable", None, None);
+
+ assert_same(
+ &best,
+ &package1,
+ "Latest most stable version should be returned (1.0.0)",
+ );
}
#[test]
-#[ignore = "requires mocking RepositorySet::find_packages with willReturnOnConsecutiveCalls; RepositorySet is a concrete struct with no injectable/overridable find_packages"]
fn test_most_stable_version_is_returned_regardless_of_order() {
- todo!()
+ let package_name = "foo/bar";
+
+ let package1 = get_package("foo/bar", "2.x-dev");
+ let package2 = get_package("foo/bar", "2.0.0-beta3");
+ let packages = vec![package1.clone(), package2.clone()];
+ let reversed: Vec<PackageInterfaceHandle> = packages.iter().rev().cloned().collect();
+
+ let mut repository_set = MockRepositorySet::new();
+ let mut seq = mockall::Sequence::new();
+ repository_set
+ .expect_find_packages()
+ .times(1)
+ .in_sequence(&mut seq)
+ .returning_st(move |_, _, _| Ok(packages.clone()));
+ repository_set
+ .expect_find_packages()
+ .times(1)
+ .in_sequence(&mut seq)
+ .returning_st(move |_, _, _| Ok(reversed.clone()));
+
+ let mut version_selector = VersionSelector::new(into_seam(repository_set), None).unwrap();
+ let best = find_best(&mut version_selector, package_name, "stable", None, None);
+ assert_same(
+ &best,
+ &package2,
+ "Expecting 2.0.0-beta3, cause beta is more stable than dev",
+ );
+
+ let best = find_best(&mut version_selector, package_name, "stable", None, None);
+ assert_same(
+ &best,
+ &package2,
+ "Expecting 2.0.0-beta3, cause beta is more stable than dev",
+ );
}
#[test]
-#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"]
fn test_highest_version_is_returned() {
- todo!()
+ let package_name = "foo/bar";
+
+ let package1 = get_package("foo/bar", "1.0.0");
+ let package2 = get_package("foo/bar", "1.1.0-beta");
+ let packages = vec![package1.clone(), package2.clone()];
+
+ let mut repository_set = MockRepositorySet::new();
+ repository_set
+ .expect_find_packages()
+ .times(1)
+ .returning_st(move |_, _, _| Ok(packages.clone()));
+
+ let mut version_selector = VersionSelector::new(into_seam(repository_set), None).unwrap();
+ let best = find_best(&mut version_selector, package_name, "dev", None, None);
+
+ assert_same(
+ &best,
+ &package2,
+ "Latest version should be returned (1.1.0-beta)",
+ );
}
#[test]
-#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"]
fn test_highest_version_matching_stability_is_returned() {
- todo!()
+ let package_name = "foo/bar";
+
+ let package1 = get_package("foo/bar", "1.0.0");
+ let package2 = get_package("foo/bar", "1.1.0-beta");
+ let package3 = get_package("foo/bar", "1.2.0-alpha");
+ let packages = vec![package1.clone(), package2.clone(), package3.clone()];
+
+ let mut repository_set = MockRepositorySet::new();
+ repository_set
+ .expect_find_packages()
+ .times(1)
+ .returning_st(move |_, _, _| Ok(packages.clone()));
+
+ let mut version_selector = VersionSelector::new(into_seam(repository_set), None).unwrap();
+ let best = find_best(&mut version_selector, package_name, "beta", None, None);
+
+ assert_same(
+ &best,
+ &package2,
+ "Latest version should be returned (1.1.0-beta)",
+ );
}
#[test]
-#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"]
fn test_most_stable_unstable_version_is_returned() {
- todo!()
+ let package_name = "foo/bar";
+
+ let package2 = get_package("foo/bar", "1.1.0-beta");
+ let package3 = get_package("foo/bar", "1.2.0-alpha");
+ let packages = vec![package2.clone(), package3.clone()];
+
+ let mut repository_set = MockRepositorySet::new();
+ repository_set
+ .expect_find_packages()
+ .times(1)
+ .returning_st(move |_, _, _| Ok(packages.clone()));
+
+ let mut version_selector = VersionSelector::new(into_seam(repository_set), None).unwrap();
+ let best = find_best(&mut version_selector, package_name, "stable", None, None);
+
+ assert_same(
+ &best,
+ &package2,
+ "Latest version should be returned (1.1.0-beta)",
+ );
}
#[test]
-#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"]
fn test_default_branch_alias_is_never_returned() {
- todo!()
+ let package_name = "foo/bar";
+
+ let package = get_package("foo/bar", "1.1.0-beta");
+ let package2 = get_package("foo/bar", "dev-main");
+ let package2_complete = CompletePackageHandle::from_rc_unchecked(package2.as_rc().clone());
+ let package2_alias: PackageInterfaceHandle = CompleteAliasPackageHandle::new(
+ package2_complete,
+ VersionParser::DEFAULT_BRANCH_ALIAS.to_string(),
+ VersionParser::DEFAULT_BRANCH_ALIAS.to_string(),
+ )
+ .into();
+ let packages = vec![package.clone(), package2_alias.clone()];
+
+ let mut repository_set = MockRepositorySet::new();
+ repository_set
+ .expect_find_packages()
+ .times(1)
+ .returning_st(move |_, _, _| Ok(packages.clone()));
+
+ let mut version_selector = VersionSelector::new(into_seam(repository_set), None).unwrap();
+ let best = find_best(&mut version_selector, package_name, "dev", None, None);
+
+ assert_same(
+ &best,
+ &package2,
+ "Latest version should be returned (dev-main)",
+ );
}
#[test]
-#[ignore = "requires mocking RepositorySet::find_packages to return an empty list; RepositorySet is a concrete struct with no injectable/overridable find_packages"]
fn test_false_returned_on_no_packages() {
- todo!()
+ let mut repository_set = MockRepositorySet::new();
+ repository_set
+ .expect_find_packages()
+ .times(1)
+ .returning_st(|_, _, _| Ok(vec![]));
+
+ let mut version_selector = VersionSelector::new(into_seam(repository_set), None).unwrap();
+ let best = find_best(&mut version_selector, "foobaz", "stable", None, None);
+ assert!(best.is_none(), "No versions are available returns false");
}
#[test]