aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests/downloader/fossil_downloader_test.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-25 17:49:35 +0900
committernsfisis <nsfisis@gmail.com>2026-06-26 00:20:05 +0900
commitd4ec1dcb6fc04ff029ded35cc2fcc14f3bbc02ad (patch)
tree0a85bd085961fcdfd5570cafc3b44ba5eb8cb152 /crates/shirabe/tests/downloader/fossil_downloader_test.rs
parent22d0b327368e5f39de6f381046c081d08efdba15 (diff)
downloadphp-shirabe-d4ec1dcb6fc04ff029ded35cc2fcc14f3bbc02ad.tar.gz
php-shirabe-d4ec1dcb6fc04ff029ded35cc2fcc14f3bbc02ad.tar.zst
php-shirabe-d4ec1dcb6fc04ff029ded35cc2fcc14f3bbc02ad.zip
test: port 32 command/repository/downloader tests
Add create_installed_json/create_composer_lock test helpers. Port command (8), repository path/forgejo/perforce/vcs (11), and fossil/hg/download_manager (13) tests. Fix production porting bugs: root_package_loader/forgejo_url/version_bumper regex delimiters, repository_manager create_repository_by_class, array_loader isset, licenses_command RefCell borrow; implement disk_free_space and touch2/touch3 via libc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/tests/downloader/fossil_downloader_test.rs')
-rw-r--r--crates/shirabe/tests/downloader/fossil_downloader_test.rs181
1 files changed, 163 insertions, 18 deletions
diff --git a/crates/shirabe/tests/downloader/fossil_downloader_test.rs b/crates/shirabe/tests/downloader/fossil_downloader_test.rs
index 0c45d2c..e403229 100644
--- a/crates/shirabe/tests/downloader/fossil_downloader_test.rs
+++ b/crates/shirabe/tests/downloader/fossil_downloader_test.rs
@@ -1,8 +1,30 @@
//! 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<F: std::future::Future>(future: F) -> F::Output {
+ tokio::runtime::Builder::new_current_thread()
+ .build()
+ .unwrap()
+ .block_on(future)
+}
+
fn set_up() -> TempDir {
TempDir::new().unwrap()
}
@@ -30,48 +52,168 @@ impl Drop for TearDown {
}
}
-// Every case constructs a FossilDownloader with a mocked IO/Config and a mocked
-// ProcessExecutor to feed fossil command output; a real HttpDownloader reaches
-// curl_multi_init (todo!()), and ProcessExecutor mocking is not available.
+/// 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<Rc<RefCell<dyn IOInterface>>>,
+ config: Option<Config>,
+ process: Rc<RefCell<ProcessExecutor>>,
+) -> FossilDownloader {
+ let io =
+ io.unwrap_or_else(|| Rc::new(RefCell::new(IOStub::new())) as Rc<RefCell<dyn IOInterface>>);
+ // 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]
-#[ignore = "needs ProcessExecutorMock and PHPUnit mocks of IOInterface/PackageInterface/Filesystem for getDownloaderMock; not available"]
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 _ = &working_dir;
- todo!()
+
+ 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]
-#[ignore = "needs ProcessExecutorMock with expects() command-sequence assertions and PHPUnit PackageInterface mock; not available"]
fn test_install() {
let working_dir = set_up();
let _tear_down = TearDown::new(working_dir.path().to_path_buf());
- let _ = &working_dir;
- todo!()
+
+ 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]
-#[ignore = "needs ProcessExecutorMock and PHPUnit mocks of IOInterface/PackageInterface/Filesystem for getDownloaderMock; not available"]
fn test_updatefor_package_without_source_reference() {
let working_dir = set_up();
let _tear_down = TearDown::new(working_dir.path().to_path_buf());
- let _ = &working_dir;
- todo!()
+
+ 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]
-#[ignore = "needs ProcessExecutorMock with expects() command-sequence assertions and PHPUnit PackageInterface mock; not available"]
fn test_update() {
let working_dir = set_up();
let _tear_down = TearDown::new(working_dir.path().to_path_buf());
- let _ = &working_dir;
- todo!()
+
+ 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]
-#[ignore = "needs ProcessExecutorMock and a PHPUnit Filesystem mock asserting removeDirectoryAsync; not available"]
fn test_remove() {
let working_dir = set_up();
let _tear_down = TearDown::new(working_dir.path().to_path_buf());
@@ -80,10 +222,13 @@ fn test_remove() {
}
#[test]
-#[ignore = "needs ProcessExecutorMock and PHPUnit mocks of IOInterface/Filesystem for getDownloaderMock; not available"]
fn test_get_installation_source() {
let working_dir = set_up();
let _tear_down = TearDown::new(working_dir.path().to_path_buf());
let _ = &working_dir;
- todo!()
+
+ 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());
}