diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-28 08:32:14 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-28 08:32:14 +0900 |
| commit | 7f84f914b29cf68d045080b430877f4b75367ada (patch) | |
| tree | 6c11c074b8df63319a41b2feb0438f67765a9cd9 /crates/shirabe/tests | |
| parent | 9cdc1263000206a42cd069d0125dda8932e0e80d (diff) | |
| download | php-shirabe-7f84f914b29cf68d045080b430877f4b75367ada.tar.gz php-shirabe-7f84f914b29cf68d045080b430877f4b75367ada.tar.zst php-shirabe-7f84f914b29cf68d045080b430877f4b75367ada.zip | |
test(repository): port testRepositoryWritesInstalledPhp
Implement the previously-ignored test, which dumps installed.php via
FilesystemRepository::write and byte-matches the Composer fixture. The
package link setters needed by configureLinks are now reachable through
the __set_provides/__set_replaces test hatches, so the PHP getPackage +
configureLinks shape can be reproduced faithfully.
Also switch both InstallationManager stubs in this file to a single
mockall mock, replacing the hand-rolled impls and call counter.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/tests')
| -rw-r--r-- | crates/shirabe/tests/repository/filesystem_repository_test.rs | 246 |
1 files changed, 186 insertions, 60 deletions
diff --git a/crates/shirabe/tests/repository/filesystem_repository_test.rs b/crates/shirabe/tests/repository/filesystem_repository_test.rs index 0d0e621..7459486 100644 --- a/crates/shirabe/tests/repository/filesystem_repository_test.rs +++ b/crates/shirabe/tests/repository/filesystem_repository_test.rs @@ -9,14 +9,16 @@ use shirabe::installed_versions::InstalledVersions; use shirabe::installer::{InstallationManagerInterface, InstallerInterface}; use shirabe::io::IOInterface; use shirabe::json::json_file::JsonFile; -use shirabe::package::PackageInterfaceHandle; +use shirabe::package::loader::ArrayLoader; +use shirabe::package::{Link, PackageInterfaceHandle, RootAliasPackageHandle, RootPackageHandle}; use shirabe::repository::InstalledRepositoryInterface; use shirabe::repository::RepositoryInterface; use shirabe::repository::filesystem_repository::FilesystemRepository; use shirabe::util::filesystem::Filesystem; use shirabe_php_shim::PhpMixed; +use shirabe_semver::VersionParser; -use crate::test_case::get_package; +use crate::test_case::{get_alias_package, get_package}; /// PHP mocks JsonFile::read()/exists(); without a mocking framework the canned read value is /// materialized as a real temp file whose decoded JSON reproduces the mock return value exactly. @@ -89,54 +91,32 @@ fn test_unexistent_repository_file() { assert_eq!(packages.len(), 0); } -/// Stub for `InstallationManagerInterface` whose `getInstallPath` returns a fixed path, mirroring -/// the PHPUnit mock built with `disableOriginalConstructor()`. Only `get_install_path` is reached by -/// `FilesystemRepository::write`; the call counter backs PHP's `expects($this->exactly(2))`. -#[derive(Debug)] -struct InstallPathStub { - calls: Rc<RefCell<i64>>, - fixed_path: String, -} - -impl InstallationManagerInterface for InstallPathStub { - fn add_installer(&mut self, _installer: Box<dyn InstallerInterface>) { - unimplemented!() - } - fn remove_installer(&mut self, _installer: &dyn InstallerInterface) { - unimplemented!() - } - fn disable_plugins(&mut self) { - unimplemented!() - } - fn is_package_installed( - &mut self, - _repo: &dyn InstalledRepositoryInterface, - _package: PackageInterfaceHandle, - ) -> anyhow::Result<bool> { - unimplemented!() - } - fn ensure_binaries_presence(&mut self, _package: PackageInterfaceHandle) { - unimplemented!() - } - fn execute( - &mut self, - _repo: &mut dyn InstalledRepositoryInterface, - _operations: Vec<Rc<dyn OperationInterface>>, - _dev_mode: bool, - _run_scripts: bool, - _download_only: bool, - ) -> anyhow::Result<()> { - unimplemented!() - } - fn get_install_path(&mut self, _package: PackageInterfaceHandle) -> Option<String> { - *self.calls.borrow_mut() += 1; - Some(self.fixed_path.clone()) - } - fn set_output_progress(&mut self, _output_progress: bool) { - unimplemented!() - } - fn notify_installs(&mut self, _io: Rc<RefCell<dyn IOInterface>>) { - unimplemented!() +mockall::mock! { + /// Mocks the PHPUnit `InstallationManager` mock built with `disableOriginalConstructor()`. Only + /// `get_install_path` is reached by `FilesystemRepository::write`. + #[derive(Debug)] + pub InstallationManager {} + impl InstallationManagerInterface for InstallationManager { + fn add_installer(&mut self, installer: Box<dyn InstallerInterface>); + fn remove_installer(&mut self, installer: &dyn InstallerInterface); + fn disable_plugins(&mut self); + fn is_package_installed( + &mut self, + repo: &dyn InstalledRepositoryInterface, + package: PackageInterfaceHandle, + ) -> anyhow::Result<bool>; + fn ensure_binaries_presence(&mut self, package: PackageInterfaceHandle); + fn execute( + &mut self, + repo: &mut dyn InstalledRepositoryInterface, + operations: Vec<Rc<dyn OperationInterface>>, + dev_mode: bool, + run_scripts: bool, + download_only: bool, + ) -> anyhow::Result<()>; + fn get_install_path(&mut self, package: PackageInterfaceHandle) -> Option<String>; + fn set_output_progress(&mut self, output_progress: bool); + fn notify_installs(&mut self, io: Rc<RefCell<dyn IOInterface>>); } } @@ -162,11 +142,11 @@ fn test_repository_write() { let json = JsonFile::new(json_path.clone(), None, None).unwrap(); let mut repository = FilesystemRepository::new(json, false, None, None).unwrap(); - let calls = Rc::new(RefCell::new(0i64)); - let mut im = InstallPathStub { - calls: calls.clone(), - fixed_path: format!("{}/vendor/woop/woop", repo_dir), - }; + let mut im = MockInstallationManager::new(); + let fixed_path = format!("{}/vendor/woop/woop", repo_dir); + im.expect_get_install_path() + .times(2) + .returning(move |_| Some(fixed_path.clone())); repository.set_dev_package_names(vec!["mypkg2".to_string()]); repository @@ -177,9 +157,6 @@ fn test_repository_write() { .unwrap(); repository.write(true, &mut im).unwrap(); - // PHP asserts getInstallPath is called exactly twice (once per installed package). - assert_eq!(*calls.borrow(), 2); - let written = std::fs::read_to_string(&json_path).unwrap(); let actual: serde_json::Value = serde_json::from_str(&written).unwrap(); let expected = serde_json::json!({ @@ -195,10 +172,159 @@ fn test_repository_write() { fs.remove_directory(&repo_dir).ok(); } +/// chdir()s into a fresh temp dir on construction and restores the previous cwd on drop, mirroring +/// PHP's `getUniqueTmpDirectory()` + `chdir($dir)`. The cwd is restored before the temp tree is +/// removed so the directory (the cwd itself) can be deleted cleanly. +struct CwdGuard { + temp: tempfile::TempDir, + prev_cwd: std::path::PathBuf, +} + +impl CwdGuard { + fn new() -> Self { + let temp = tempfile::TempDir::new().unwrap(); + let prev_cwd = std::env::current_dir().unwrap(); + std::env::set_current_dir(temp.path()).unwrap(); + Self { temp, prev_cwd } + } + + fn path(&self) -> std::path::PathBuf { + self.temp.path().to_path_buf() + } +} + +impl Drop for CwdGuard { + fn drop(&mut self) { + let _ = std::env::set_current_dir(&self.prev_cwd); + } +} + +/// ref: TestCase::configureLinks, inlined for the single package being configured. +fn configure_links( + name: &str, + pretty_version: &str, + description: &str, + links: &[(&str, &str)], +) -> IndexMap<String, Link> { + let mut map: IndexMap<String, PhpMixed> = IndexMap::new(); + for (target, constraint) in links { + map.insert( + (*target).to_string(), + PhpMixed::String((*constraint).to_string()), + ); + } + ArrayLoader::new(None, false) + .parse_links(name, pretty_version, description, map) + .unwrap() +} + #[test] -#[ignore = "needs get_root_package + configure_links test helpers (not present in tests/common; project_test_port_link_setters describes them via ArrayLoader::load_packages but this branch lacks them) plus exact byte-match of the generated installed.php fixture under a chdir"] fn test_repository_writes_installed_php() { - todo!() + let guard = CwdGuard::new(); + let dir = guard.path(); + let dir = dir.to_str().unwrap(); + + let json = JsonFile::new(format!("{}/installed.json", dir), None, None).unwrap(); + + let root_package = RootPackageHandle::new( + "__root__".to_string(), + VersionParser.normalize("dev-master", None).unwrap(), + "dev-master".to_string(), + ); + root_package.set_source_reference(Some("sourceref-by-default".to_string())); + root_package.set_dist_reference(Some("distref".to_string())); + root_package.set_provides(configure_links( + "__root__", + "dev-master", + Link::TYPE_PROVIDE, + &[("foo/impl", "2.0")], + )); + let root_package = RootAliasPackageHandle::new( + root_package, + VersionParser.normalize("1.10.x-dev", None).unwrap(), + "1.10.x-dev".to_string(), + ); + + let mut repository = + FilesystemRepository::new(json, true, Some(root_package.into()), None).unwrap(); + repository.set_dev_package_names(vec!["c/c".to_string()]); + + let pkg = get_package("a/provider", "1.1"); + pkg.__set_provides(configure_links( + "a/provider", + "1.1", + Link::TYPE_PROVIDE, + &[("foo/impl", "^1.1"), ("foo/impl2", "2.0")], + )); + pkg.set_dist_reference(Some("distref-as-no-source".to_string())); + repository.add_package(pkg).unwrap(); + + let pkg = get_package("a/provider2", "1.2"); + pkg.__set_provides(configure_links( + "a/provider2", + "1.2", + Link::TYPE_PROVIDE, + &[("foo/impl", "self.version"), ("foo/impl2", "2.0")], + )); + pkg.set_source_reference(Some("sourceref".to_string())); + pkg.set_dist_reference(Some("distref-as-installed-from-dist".to_string())); + pkg.set_installation_source(Some("dist".to_string())); + repository.add_package(pkg.clone()).unwrap(); + + repository + .add_package(get_alias_package(&pkg, "1.4")) + .unwrap(); + + let pkg = get_package("b/replacer", "2.2"); + pkg.__set_replaces(configure_links( + "b/replacer", + "2.2", + Link::TYPE_REPLACE, + &[("foo/impl2", "self.version"), ("foo/replaced", "^3.0")], + )); + repository.add_package(pkg).unwrap(); + + let pkg = get_package("c/c", "3.0"); + pkg.set_dist_reference(Some( + "{${passthru('bash -i')}} Foo\\Bar\n\ttab\u{0b}verticaltab\0".to_string(), + )); + repository.add_package(pkg).unwrap(); + + let pkg = get_package("meta/package", "3.0"); + pkg.__set_type("metapackage".to_string()); + repository.add_package(pkg).unwrap(); + + let mut im = MockInstallationManager::new(); + let cwd = dir.to_string(); + im.expect_get_install_path().returning(move |package| { + // check for empty paths handling + if package.get_type() == "metapackage" { + return Some(String::new()); + } + if package.get_name() == "c/c" { + // check for absolute paths + return Some("/foo/bar/ven\\do{}r/c/c${}".to_string()); + } + if package.get_name() == "a/provider" { + return Some("vendor/{${passthru('bash -i')}}".to_string()); + } + // check for cwd + if package.as_root().is_some() { + return Some(cwd.clone()); + } + // check for relative paths + Some(format!("vendor/{}", package.get_name())) + }); + + repository.write(true, &mut im).unwrap(); + + let expected = std::fs::read_to_string(format!( + "{}/../../composer/tests/Composer/Test/Repository/Fixtures/installed.php", + env!("CARGO_MANIFEST_DIR") + )) + .unwrap(); + let actual = std::fs::read_to_string(format!("{}/installed.php", dir)).unwrap(); + assert_eq!(expected, actual); } #[ignore] |
