diff options
Diffstat (limited to 'crates/shirabe/tests/command')
| -rw-r--r-- | crates/shirabe/tests/command/archive_command_test.rs | 260 |
1 files changed, 119 insertions, 141 deletions
diff --git a/crates/shirabe/tests/command/archive_command_test.rs b/crates/shirabe/tests/command/archive_command_test.rs index c25ea3c..e7730b9 100644 --- a/crates/shirabe/tests/command/archive_command_test.rs +++ b/crates/shirabe/tests/command/archive_command_test.rs @@ -27,83 +27,81 @@ use shirabe_semver::VersionParser; use crate::config_stub::ConfigStubBuilder; -/// A recorded `ArchiveManager::archive()` call (PHPUnit `->with(...)->willReturn(...)`). -#[derive(Debug, Clone)] -struct ArchiveCall { - package_name: String, - format: String, - target_dir: String, - file_name: Option<String>, - ignore_filters: bool, -} - -/// Equivalent to a `getMockBuilder(ArchiveManager::class)` mock whose `archive` method is -/// stubbed to record its arguments and return a fixed path. -#[derive(Debug)] -struct ArchiveManagerMock { - calls: Rc<RefCell<Vec<ArchiveCall>>>, - return_value: String, -} - -impl ArchiveManagerInterface for ArchiveManagerMock { - fn archive( - &mut self, - package: CompletePackageInterfaceHandle, - format: String, - target_dir: String, - file_name: Option<String>, - ignore_filters: bool, - ) -> anyhow::Result<String> { - self.calls.borrow_mut().push(ArchiveCall { - package_name: package.get_name(), - format, - target_dir, - file_name, - ignore_filters, - }); - Ok(self.return_value.clone()) +// PHP mocks `Composer\Package\Archiver\ArchiveManager` with +// getMockBuilder(...)->disableOriginalConstructor(). +mockall::mock! { + #[derive(Debug)] + pub ArchiveManager {} + impl ArchiveManagerInterface for ArchiveManager { + fn archive( + &mut self, + package: CompletePackageInterfaceHandle, + format: String, + target_dir: String, + file_name: Option<String>, + ignore_filters: bool, + ) -> anyhow::Result<String>; } } -/// Equivalent to a `getMockBuilder(EventDispatcher::class)->disableOriginalConstructor()` mock -/// with no behavioral expectations: every dispatch is a no-op. -#[derive(Debug, Default)] -struct NoopEventDispatcher; - -impl EventDispatcherInterface for NoopEventDispatcher { - fn dispatch( - &mut self, - _event_name: Option<&str>, - _event: Option<&mut dyn EventInterface>, - ) -> anyhow::Result<i64> { - Ok(0) - } - - fn dispatch_script( - &mut self, - _event_name: &str, - _dev_mode: bool, - _additional_args: Vec<String>, - _flags: IndexMap<String, PhpMixed>, - ) -> anyhow::Result<i64> { - Ok(0) +// PHP mocks `Composer\Repository\RepositoryManager` with +// getMockBuilder(...)->disableOriginalConstructor(). +mockall::mock! { + #[derive(Debug)] + pub RepositoryManager {} + impl RepositoryManagerInterface for RepositoryManager { + fn get_local_repository(&self) -> RepositoryInterfaceHandle; + fn get_repositories(&self) -> &Vec<RepositoryInterfaceHandle>; + fn create_repository<'a>( + &self, + r#type: &str, + config: IndexMap<String, PhpMixed>, + name: Option<&'a str>, + ) -> anyhow::Result<RepositoryInterfaceHandle>; + fn add_repository(&mut self, repository: RepositoryInterfaceHandle); + fn set_local_repository(&mut self, repository: RepositoryInterfaceHandle); } +} - fn dispatch_installer_event( - &mut self, - _event_name: &str, - _dev_mode: bool, - _execute_operations: bool, - _transaction: Transaction, - ) -> anyhow::Result<i64> { - Ok(0) +// PHP mocks `Composer\EventDispatcher\EventDispatcher` with +// getMockBuilder(...)->disableOriginalConstructor() and no method stubs. +mockall::mock! { + #[derive(Debug)] + pub EventDispatcher {} + impl EventDispatcherInterface for EventDispatcher { + fn dispatch<'a, 'b>( + &mut self, + event_name: Option<&'a str>, + event: Option<&'b mut dyn EventInterface>, + ) -> anyhow::Result<i64>; + fn dispatch_script( + &mut self, + event_name: &str, + dev_mode: bool, + additional_args: Vec<String>, + flags: IndexMap<String, PhpMixed>, + ) -> anyhow::Result<i64>; + fn dispatch_installer_event( + &mut self, + event_name: &str, + dev_mode: bool, + execute_operations: bool, + transaction: Transaction, + ) -> anyhow::Result<i64>; + fn add_listener(&mut self, event_name: &str, listener: Callable, priority: i64); + fn has_event_listeners(&mut self, event: &dyn EventInterface) -> bool; } +} - fn add_listener(&mut self, _event_name: &str, _listener: Callable, _priority: i64) {} - - fn has_event_listeners(&mut self, _event: &dyn EventInterface) -> bool { - false - } +/// A `getMockBuilder(EventDispatcher::class)->disableOriginalConstructor()` mock with no +/// behavioral expectations: the dispatch methods ArchiveCommand calls are permissive no-ops. +fn noop_event_dispatcher() -> MockEventDispatcher { + let mut event_dispatcher = MockEventDispatcher::new(); + event_dispatcher.expect_dispatch().returning(|_, _| Ok(0)); + event_dispatcher + .expect_dispatch_script() + .returning(|_, _, _, _| Ok(0)); + event_dispatcher } fn root_package(name: &str, version: &str) -> RootPackageHandle { @@ -126,18 +124,30 @@ fn test_uses_config_from_composer_object() { .with("archive-format", PhpMixed::from("zip")) .build_shared(); - let calls = Rc::new(RefCell::new(Vec::new())); - let manager = ArchiveManagerMock { - calls: calls.clone(), - return_value: Platform::get_cwd(false).unwrap(), - }; + let cwd = Platform::get_cwd(false).unwrap(); + let mut manager = MockArchiveManager::new(); + // PHP: ->expects($this->once())->method('archive') + // ->with($package, 'zip', '.', null, false)->willReturn(Platform::getCwd()). + // PHP asserts archive() receives `$package` itself; we check the identity via the package name + // we set on the Composer. + manager + .expect_archive() + .times(1) + .withf(|package, format, target_dir, file_name, ignore_filters| { + package.get_name() == "archive/test" + && format == "zip" + && target_dir == "." + && file_name.is_none() + && !*ignore_filters + }) + .returning(move |_, _, _, _, _| Ok(cwd.clone())); let package = root_package("archive/test", "1.0.0"); let mut composer = Composer::new(); composer.set_config(config); composer.set_archive_manager(Rc::new(RefCell::new(manager))); - composer.set_event_dispatcher(Rc::new(RefCell::new(NoopEventDispatcher))); + composer.set_event_dispatcher(Rc::new(RefCell::new(noop_event_dispatcher()))); composer.set_package(package.into()); let composer = full_composer(composer); @@ -146,16 +156,6 @@ fn test_uses_config_from_composer_object() { command.set_composer(composer); command.run(input, output).unwrap(); - - let calls = calls.borrow(); - assert_eq!(1, calls.len()); - // PHP asserts archive() receives `$package` itself; we check the identity via the package name - // we set on the Composer. - assert_eq!("archive/test", calls[0].package_name); - assert_eq!("zip", calls[0].format); - assert_eq!(".", calls[0].target_dir); - assert_eq!(None, calls[0].file_name); - assert!(!calls[0].ignore_filters); } #[test] @@ -188,41 +188,6 @@ fn test_uses_config_from_factory_when_composer_is_not_defined() { assert!(!calls[0].had_composer); } -/// Equivalent to a `getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()` mock: -/// `getLocalRepository` returns the installed repository, `getRepositories` returns `[]`. -#[derive(Debug)] -struct RepositoryManagerMock { - local: RepositoryInterfaceHandle, - repositories: Vec<RepositoryInterfaceHandle>, -} - -impl RepositoryManagerInterface for RepositoryManagerMock { - fn get_local_repository(&self) -> RepositoryInterfaceHandle { - self.local.clone() - } - - fn get_repositories(&self) -> &Vec<RepositoryInterfaceHandle> { - &self.repositories - } - - fn create_repository( - &self, - _type: &str, - _config: IndexMap<String, PhpMixed>, - _name: Option<&str>, - ) -> anyhow::Result<RepositoryInterfaceHandle> { - unreachable!("ArchiveCommand does not create repositories") - } - - fn add_repository(&mut self, _repository: RepositoryInterfaceHandle) { - unreachable!("ArchiveCommand does not add repositories") - } - - fn set_local_repository(&mut self, _repository: RepositoryInterfaceHandle) { - unreachable!("ArchiveCommand does not set the local repository") - } -} - #[test] fn test_uses_config_from_composer_object_with_package_name() { let input: Rc<RefCell<dyn InputInterface>> = Rc::new(RefCell::new( @@ -239,26 +204,47 @@ fn test_uses_config_from_composer_object_with_package_name() { .with("archive-format", PhpMixed::from("zip")) .build_shared(); - let calls = Rc::new(RefCell::new(Vec::new())); - let manager = ArchiveManagerMock { - calls: calls.clone(), - return_value: Platform::get_cwd(false).unwrap(), - }; + let cwd = Platform::get_cwd(false).unwrap(); + let mut manager = MockArchiveManager::new(); + // PHP: ->expects($this->once())->method('archive') + // ->with($package, 'zip', '.', null, false)->willReturn(Platform::getCwd()). + manager + .expect_archive() + .times(1) + .withf(|package, format, target_dir, file_name, ignore_filters| { + package.get_name() == "foo/bar" + && format == "zip" + && target_dir == "." + && file_name.is_none() + && !*ignore_filters + }) + .returning(move |_, _, _, _, _| Ok(cwd.clone())); let package = root_package("foo/bar", "1.0.0"); + let mut repository_manager = MockRepositoryManager::new(); + // PHP: ->expects($this->once())->method('getLocalRepository')->willReturn($installedRepository). // The local repository resolves `foo/bar` (PHPUnit mocks InstalledRepositoryInterface::loadPackages). - let installed = - InstalledArrayRepository::new_with_packages(vec![package.clone().into()]).unwrap(); - let repository_manager = RepositoryManagerMock { - local: RepositoryInterfaceHandle::new(installed), - repositories: vec![], - }; + // Built inside the closure so it captures nothing (mockall requires `Send` closures). + repository_manager + .expect_get_local_repository() + .times(1) + .returning(|| { + let package = root_package("foo/bar", "1.0.0"); + let installed = + InstalledArrayRepository::new_with_packages(vec![package.into()]).unwrap(); + RepositoryInterfaceHandle::new(installed) + }); + // PHP: ->expects($this->once())->method('getRepositories')->willReturn([]). + repository_manager + .expect_get_repositories() + .times(1) + .return_const(Vec::new()); let mut composer = Composer::new(); composer.set_config(config); composer.set_archive_manager(Rc::new(RefCell::new(manager))); - composer.set_event_dispatcher(Rc::new(RefCell::new(NoopEventDispatcher))); + composer.set_event_dispatcher(Rc::new(RefCell::new(noop_event_dispatcher()))); composer.set_package(package.into()); composer.set_repository_manager(Rc::new(RefCell::new(repository_manager))); let composer = full_composer(composer); @@ -267,12 +253,4 @@ fn test_uses_config_from_composer_object_with_package_name() { command.set_composer(composer); command.run(input, output).unwrap(); - - let calls = calls.borrow(); - assert_eq!(1, calls.len()); - assert_eq!("foo/bar", calls[0].package_name); - assert_eq!("zip", calls[0].format); - assert_eq!(".", calls[0].target_dir); - assert_eq!(None, calls[0].file_name); - assert!(!calls[0].ignore_filters); } |
