aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests/package
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-27 17:21:00 +0900
committernsfisis <nsfisis@gmail.com>2026-06-27 17:26:28 +0900
commite98823e599eb375b30037cc714710e3309d927d1 (patch)
treee1689ec164086798fc46aa6a58d1e914cf4873b2 /crates/shirabe/tests/package
parent20f620bdd0b5764ed2e9812dc0772f907b0d6f29 (diff)
downloadphp-shirabe-e98823e599eb375b30037cc714710e3309d927d1.tar.gz
php-shirabe-e98823e599eb375b30037cc714710e3309d927d1.tar.zst
php-shirabe-e98823e599eb375b30037cc714710e3309d927d1.zip
test: port Composer tests unblocked by mockall, add seams
Port 11 categories of previously-ignored Composer tests now reachable with the mockall crate: DownloadManager, VCS/Perforce/File downloaders, VersionSelector, PlatformRepository, Auditor, installer/FilesystemRepository, RootPackageLoader, util auth/http, commands, and Cache. Extract test seams additively on concrete structs as *Interface traits (Runtime, HhvmDetector, VersionGuesser, RepositorySet, Perforce, BinaryInstaller) plus mock-field seams (Cache, Filesystem); consumers take trait objects. Mocks are defined locally in the test crates via mockall::mock!, since automock-generated mocks are cfg(test)-gated and invisible across the integration-test boundary. dataProviders are ported in full; tests blocked by unported shims stay #[ignore] with documented reasons rather than reduced or weakened. Fix product bugs surfaced by the ports: - util/github: use the exception code, not the HTTP status, for 401/403 - advisory: serialize empty audit maps as [] to match PHP json_encode - repository/filesystem and downloader/file: fix RefCell double-borrow panics Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/tests/package')
-rw-r--r--crates/shirabe/tests/package/loader/root_package_loader_test.rs232
-rw-r--r--crates/shirabe/tests/package/version/version_selector_test.rs521
2 files changed, 715 insertions, 38 deletions
diff --git a/crates/shirabe/tests/package/loader/root_package_loader_test.rs b/crates/shirabe/tests/package/loader/root_package_loader_test.rs
index 23c8beb..7fa970a 100644
--- a/crates/shirabe/tests/package/loader/root_package_loader_test.rs
+++ b/crates/shirabe/tests/package/loader/root_package_loader_test.rs
@@ -4,21 +4,28 @@
// ProcessExecutor / VersionGuesser or require constraints whose parsing goes through a
// look-around regex the regex crate cannot compile.
-use std::cell::RefCell;
+use std::cell::{Cell, RefCell};
use std::rc::Rc;
use indexmap::IndexMap;
+use serial_test::serial;
use shirabe::config::Config;
use shirabe::io::IOInterface;
use shirabe::io::null_io::NullIO;
+use shirabe::package::RootPackage;
use shirabe::package::loader::RootPackageLoader;
-use shirabe::package::version::{VersionGuesser, VersionParser};
+use shirabe::package::version::{
+ VersionData, VersionGuesser, VersionGuesserInterface, VersionParser,
+};
use shirabe::package::{STABILITY_ALPHA, STABILITY_DEV, STABILITY_RC};
use shirabe::repository::RepositoryManager;
+use shirabe::util::Git as GitUtil;
use shirabe::util::http_downloader::HttpDownloader;
-use shirabe::util::process_executor::ProcessExecutor;
+use shirabe::util::process_executor::{MockExpectation, MockHandler, ProcessExecutor};
use shirabe_php_shim::PhpMixed;
+use crate::process_executor_mock::{cmd, cmd_full, get_process_executor_mock};
+
fn null_io() -> Rc<RefCell<dyn IOInterface>> {
Rc::new(RefCell::new(NullIO::new()))
}
@@ -35,6 +42,73 @@ fn http_downloader(
)))
}
+// `$config = new Config; $config->merge(['repositories' => ['packagist' => false]]);`
+fn make_config() -> Rc<RefCell<Config>> {
+ let config = Rc::new(RefCell::new(Config::new(true, None)));
+ let mut repositories: IndexMap<String, PhpMixed> = IndexMap::new();
+ repositories.insert("packagist".to_string(), PhpMixed::Bool(false));
+ let mut merge: IndexMap<String, PhpMixed> = IndexMap::new();
+ merge.insert("repositories".to_string(), PhpMixed::Array(repositories));
+ config.borrow_mut().merge(&merge, "test");
+ config
+}
+
+// Stands in for `getMockBuilder('Composer\Repository\RepositoryManager')->disableOriginalConstructor()`.
+// The loader only stores it and feeds it default repositories, so a real instance suffices.
+fn make_manager(
+ io: &Rc<RefCell<dyn IOInterface>>,
+ config: &Rc<RefCell<Config>>,
+) -> Rc<RefCell<RepositoryManager>> {
+ Rc::new(RefCell::new(RepositoryManager::new(
+ io.clone(),
+ config.clone(),
+ http_downloader(io, config),
+ None,
+ None,
+ )))
+}
+
+fn require_map(entries: &[(&str, &str)]) -> PhpMixed {
+ let mut m: IndexMap<String, PhpMixed> = IndexMap::new();
+ for (k, v) in entries {
+ m.insert(k.to_string(), PhpMixed::String(v.to_string()));
+ }
+ PhpMixed::Array(m)
+}
+
+// Resets the cached git `version` static on drop so a seeded value does not leak into other
+// tests in this binary (VersionGuesserTest seeds/resets the same static).
+struct GitVersionGuard;
+
+impl Drop for GitVersionGuard {
+ fn drop(&mut self) {
+ GitUtil::__reset_version();
+ }
+}
+
+// A test double for the concrete VersionGuesser, supplied through the VersionGuesserInterface seam.
+#[derive(Debug)]
+struct VersionGuesserMock {
+ version_data: VersionData,
+ guess_version_calls: Rc<Cell<u32>>,
+}
+
+impl VersionGuesserInterface for VersionGuesserMock {
+ fn guess_version(
+ &mut self,
+ _package_config: &IndexMap<String, PhpMixed>,
+ _path: &str,
+ ) -> anyhow::Result<Option<VersionData>> {
+ self.guess_version_calls
+ .set(self.guess_version_calls.get() + 1);
+ Ok(Some(self.version_data.clone()))
+ }
+
+ fn get_root_version_from_env(&self) -> anyhow::Result<String> {
+ unreachable!("COMPOSER_ROOT_VERSION is not set in this test")
+ }
+}
+
#[test]
#[ignore = "process_executor.enable_async() drives the async stream path, which calls stream_set_blocking (fcntl(2) todo!() in shirabe-php-shim::stream)"]
fn test_stability_flags_parsing() {
@@ -70,7 +144,8 @@ fn test_stability_flags_parsing() {
Some(io.clone()),
);
- let mut loader = RootPackageLoader::new(manager, config.clone(), None, Some(guesser), None);
+ let mut loader =
+ RootPackageLoader::new(manager, config.clone(), None, Some(Box::new(guesser)), None);
let mut data = IndexMap::new();
data.insert(
@@ -136,25 +211,158 @@ fn test_stability_flags_parsing() {
}
#[test]
-#[ignore = "requires getProcessExecutorMock with expects(['return' => 1]); no ProcessExecutorMock mocking infrastructure exists"]
+#[serial]
fn test_no_version_is_visible_in_pretty_version() {
- todo!()
+ GitUtil::__reset_version();
+ let _git_guard = GitVersionGuard;
+
+ let io = null_io();
+ let config = make_config();
+ let manager = make_manager(&io, &config);
+
+ let (process, _guard) = get_process_executor_mock(
+ vec![],
+ false,
+ MockHandler {
+ r#return: 1,
+ ..Default::default()
+ },
+ );
+ let guesser = VersionGuesser::new(config.clone(), process, VersionParser::new(), None);
+
+ let mut loader =
+ RootPackageLoader::new(manager, config.clone(), None, Some(Box::new(guesser)), None);
+
+ let package = loader
+ .load(IndexMap::new(), "Composer\\Package\\RootPackage", None)
+ .unwrap();
+ let package = package.as_root().unwrap();
+
+ assert_eq!("1.0.0.0", package.get_version());
+ assert_eq!(
+ RootPackage::DEFAULT_PRETTY_VERSION,
+ package.get_pretty_version()
+ );
}
#[test]
-#[ignore = "requires getMockBuilder VersionGuesser mock with guessVersion expectation; no VersionGuesser mocking infrastructure exists"]
+#[serial]
fn test_pretty_version_for_root_package_in_version_branch() {
- todo!()
+ // see #6845
+ let io = null_io();
+ let config = make_config();
+ let manager = make_manager(&io, &config);
+
+ let guess_version_calls = Rc::new(Cell::new(0u32));
+ let version_guesser = VersionGuesserMock {
+ version_data: VersionData {
+ version: Some("3.0.9999999.9999999-dev".to_string()),
+ commit: Some("aabbccddee".to_string()),
+ pretty_version: Some("3.0-dev".to_string()),
+ feature_version: None,
+ feature_pretty_version: None,
+ },
+ guess_version_calls: guess_version_calls.clone(),
+ };
+
+ let mut loader = RootPackageLoader::new(
+ manager,
+ config.clone(),
+ None,
+ Some(Box::new(version_guesser)),
+ None,
+ );
+
+ let package = loader
+ .load(IndexMap::new(), "Composer\\Package\\RootPackage", None)
+ .unwrap();
+
+ assert!(guess_version_calls.get() >= 1);
+ assert_eq!("3.0-dev", package.as_root().unwrap().get_pretty_version());
}
#[test]
-#[ignore = "requires getProcessExecutorMock with expects() git command expectations; no ProcessExecutorMock mocking infrastructure exists"]
+#[ignore = "feature-branch guessing calls ProcessExecutor::execute_async, whose mock path is todo!()"]
+#[serial]
fn test_feature_branch_pretty_version() {
- todo!()
+ // proc_open() is always available; the PHP markTestSkipped guard does not apply here.
+ GitUtil::__set_version(Some("2.52.0".to_string()));
+ let _git_guard = GitVersionGuard;
+
+ let io = null_io();
+ let config = make_config();
+ let manager = make_manager(&io, &config);
+
+ let expectations: Vec<MockExpectation> = vec![
+ cmd_full(
+ ["git", "branch", "-a", "--no-color", "--no-abbrev", "-v"],
+ 0,
+ "* latest-production 38137d2f6c70e775e137b2d8a7a7d3eaebf7c7e5 Commit message\n master 4f6ed96b0bc363d2aa4404c3412de1c011f67c66 Commit message\n",
+ "",
+ ),
+ cmd(["git", "rev-list", "master..latest-production"]),
+ ];
+ let (process, _guard) = get_process_executor_mock(expectations, true, MockHandler::default());
+ let guesser = VersionGuesser::new(config.clone(), process, VersionParser::new(), None);
+
+ let mut loader =
+ RootPackageLoader::new(manager, config.clone(), None, Some(Box::new(guesser)), None);
+
+ let mut data = IndexMap::new();
+ data.insert(
+ "require".to_string(),
+ require_map(&[("foo/bar", "self.version")]),
+ );
+
+ let package = loader
+ .load(data, "Composer\\Package\\RootPackage", None)
+ .unwrap();
+
+ assert_eq!(
+ "dev-master",
+ package.as_root().unwrap().get_pretty_version()
+ );
}
#[test]
-#[ignore = "requires getProcessExecutorMock with expects() git command expectations; no ProcessExecutorMock mocking infrastructure exists"]
+#[serial]
fn test_non_feature_branch_pretty_version() {
- todo!()
+ // proc_open() is always available; the PHP markTestSkipped guard does not apply here.
+ GitUtil::__set_version(Some("2.52.0".to_string()));
+ let _git_guard = GitVersionGuard;
+
+ let io = null_io();
+ let config = make_config();
+ let manager = make_manager(&io, &config);
+
+ let expectations: Vec<MockExpectation> = vec![cmd_full(
+ ["git", "branch", "-a", "--no-color", "--no-abbrev", "-v"],
+ 0,
+ "* latest-production 38137d2f6c70e775e137b2d8a7a7d3eaebf7c7e5 Commit message\n master 4f6ed96b0bc363d2aa4404c3412de1c011f67c66 Commit message\n",
+ "",
+ )];
+ let (process, _guard) = get_process_executor_mock(expectations, true, MockHandler::default());
+ let guesser = VersionGuesser::new(config.clone(), process, VersionParser::new(), None);
+
+ let mut loader =
+ RootPackageLoader::new(manager, config.clone(), None, Some(Box::new(guesser)), None);
+
+ let mut data = IndexMap::new();
+ data.insert(
+ "require".to_string(),
+ require_map(&[("foo/bar", "self.version")]),
+ );
+ data.insert(
+ "non-feature-branches".to_string(),
+ PhpMixed::List(vec![PhpMixed::String("latest-.*".to_string())]),
+ );
+
+ let package = loader
+ .load(data, "Composer\\Package\\RootPackage", None)
+ .unwrap();
+
+ assert_eq!(
+ "dev-latest-production",
+ package.as_root().unwrap().get_pretty_version()
+ );
}
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]