//! ref: composer/tests/Composer/Test/Downloader/FossilDownloaderTest.php use std::cell::RefCell; use std::rc::Rc; use shirabe::config::Config; use shirabe::downloader::VcsDownloader; use shirabe::downloader::fossil_downloader::FossilDownloader; use shirabe::io::IOInterface; use shirabe::package::handle::{CompletePackageHandle, PackageInterfaceHandle}; use shirabe::util::ProcessExecutor; use shirabe::util::filesystem::Filesystem; use shirabe_php_shim::PhpMixed; use shirabe_semver::VersionParser; use tempfile::TempDir; use crate::config_stub::ConfigStubBuilder; use crate::io_stub::IOStub; use crate::process_executor_mock::get_process_executor_mock; fn run(future: F) -> F::Output { tokio::runtime::Builder::new_current_thread() .build() .unwrap() .block_on(future) } fn set_up() -> TempDir { TempDir::new().unwrap() } fn tear_down(working_dir: &std::path::Path) { if working_dir.is_dir() { let mut fs = Filesystem::new(None); fs.remove_directory(working_dir).unwrap(); } } struct TearDown { working_dir: std::path::PathBuf, } impl TearDown { fn new(working_dir: std::path::PathBuf) -> Self { TearDown { working_dir } } } impl Drop for TearDown { fn drop(&mut self) { tear_down(&self.working_dir); } } /// ref: TestCase::getMockBuilder('Composer\Package\PackageInterface')->getMock() /// /// A real CompletePackage seeded with the stubbed values is a faithful stand-in /// for a PackageInterface mock as long as `getSourceUrls()` equals /// `[getSourceUrl()]`, which holds for every non-ignored case here. fn get_package(source_reference: Option<&str>, source_url: Option<&str>) -> PackageInterfaceHandle { let norm_version = VersionParser.normalize("1.0.0", None).unwrap(); let package = CompletePackageHandle::new("dummy/pkg".to_string(), norm_version, "1.0.0".to_string()); package.__set_source_type(Some("fossil".to_string())); package.set_source_reference(source_reference.map(|s| s.to_string())); package.set_source_url(source_url.map(|s| s.to_string())); package.into() } /// ref: FossilDownloaderTest::getDownloaderMock fn get_downloader_mock( io: Option>>, config: Option, process: Rc>, ) -> FossilDownloader { let io = io.unwrap_or_else(|| Rc::new(RefCell::new(IOStub::new())) as Rc>); // ref: getConfig(['secure-http' => false]) let config = Rc::new(RefCell::new(config.unwrap_or_else(|| { ConfigStubBuilder::new() .with("secure-http", PhpMixed::Bool(false)) .build() }))); let fs = Rc::new(RefCell::new(Filesystem::new(None))); FossilDownloader::new(io, config, process, fs) } #[test] fn test_install_for_package_without_source_reference() { let working_dir = set_up(); let _tear_down = TearDown::new(working_dir.path().to_path_buf()); let package = get_package(None, None); let (process, _guard) = get_process_executor_mock(vec![], false, Default::default()); let mut downloader = get_downloader_mock(None, None, process); let path = format!("{}/path", working_dir.path().to_string_lossy()); let result = run(downloader.install(package, &path)); let e = result.expect_err("missing source reference should throw"); assert!(e.to_string().contains("missing reference information")); } #[test] fn test_install() { let working_dir = set_up(); let _tear_down = TearDown::new(working_dir.path().to_path_buf()); let working_dir_str = working_dir.path().to_string_lossy().into_owned(); let package = get_package(Some("trunk"), Some("http://fossil.kd2.org/kd2fw/")); let repo_file = format!("{}.fossil", working_dir_str); let (process, _guard) = get_process_executor_mock( vec![ crate::process_executor_mock::cmd(vec![ "fossil", "clone", "--", "http://fossil.kd2.org/kd2fw/", &repo_file, ]), crate::process_executor_mock::cmd(vec!["fossil", "open", "--nested", "--", &repo_file]), crate::process_executor_mock::cmd(vec!["fossil", "update", "--", "trunk"]), ], true, Default::default(), ); let mut downloader = get_downloader_mock(None, None, process); run(downloader.install(package, &working_dir_str)).unwrap(); } #[test] fn test_updatefor_package_without_source_reference() { let working_dir = set_up(); let _tear_down = TearDown::new(working_dir.path().to_path_buf()); let initial_package = get_package(Some("ref"), None); let source_package = get_package(None, None); let (process, _guard) = get_process_executor_mock(vec![], false, Default::default()); let mut downloader = get_downloader_mock(None, None, process); let result = run(async { downloader .prepare( "update", source_package.clone(), "/path", Some(initial_package.clone()), ) .await?; downloader .update(initial_package.clone(), source_package.clone(), "/path") .await?; downloader .cleanup("update", source_package, "/path", Some(initial_package)) .await }); let e = result.expect_err("missing source reference should throw"); assert!(e.to_string().contains("missing reference information")); } #[test] fn test_update() { let working_dir = set_up(); let _tear_down = TearDown::new(working_dir.path().to_path_buf()); let working_dir_str = working_dir.path().to_string_lossy().into_owned(); // Ensure the .fslckout metadata file exists. let fslckout = format!("{}/.fslckout", working_dir_str); if !std::path::Path::new(&fslckout).exists() { std::fs::File::create(&fslckout).unwrap(); } let package = get_package(Some("trunk"), Some("http://fossil.kd2.org/kd2fw/")); let (process, _guard) = get_process_executor_mock( vec![ crate::process_executor_mock::cmd(vec!["fossil", "changes"]), crate::process_executor_mock::cmd(vec!["fossil", "pull"]), crate::process_executor_mock::cmd(vec!["fossil", "up", "--", "trunk"]), ], true, Default::default(), ); let mut downloader = get_downloader_mock(None, None, process); run(async { downloader .prepare( "update", package.clone(), &working_dir_str, Some(package.clone()), ) .await .unwrap(); downloader .update(package.clone(), package.clone(), &working_dir_str) .await .unwrap(); downloader .cleanup("update", package.clone(), &working_dir_str, Some(package)) .await .unwrap(); }); } #[ignore = "PHP mocks Filesystem::removeDirectoryAsync; with a real Filesystem the actual \ removeDirectoryAsync drives its own ProcessExecutor for `rm -rf` through the \ unported execute_async mock seam"] #[test] fn test_remove() { let working_dir = set_up(); let _tear_down = TearDown::new(working_dir.path().to_path_buf()); let _ = &working_dir; todo!() } #[test] fn test_get_installation_source() { let working_dir = set_up(); let _tear_down = TearDown::new(working_dir.path().to_path_buf()); let _ = &working_dir; let (process, _guard) = get_process_executor_mock(vec![], false, Default::default()); let downloader = get_downloader_mock(None, None, process); assert_eq!("source", downloader.get_installation_source()); }