aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests/downloader/zip_downloader_test.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-25 15:37:21 +0900
committernsfisis <nsfisis@gmail.com>2026-06-26 00:20:05 +0900
commit3a883e6912e642a1bcfe68336007eae018207308 (patch)
tree2750519e1bbaaf8caa493bcf1eb15efb143280d9 /crates/shirabe/tests/downloader/zip_downloader_test.rs
parentd4cdccb8de8758bd46a12283f8df90e020327b99 (diff)
downloadphp-shirabe-3a883e6912e642a1bcfe68336007eae018207308.tar.gz
php-shirabe-3a883e6912e642a1bcfe68336007eae018207308.tar.zst
php-shirabe-3a883e6912e642a1bcfe68336007eae018207308.zip
test: port 35 auth/installer/io/zip/bitbucket tests; implement date_create
Port auth_helper (14), library_installer (8), console_io (7), zip_downloader (3), git_bitbucket_driver (3) tests. Implement date_create/strtotime for the ISO8601/ RFC3339/relative formats Composer uses (unknown input -> None, no silent guess). Fix production bugs: Question::is_assoc list-vs-assoc, auth_helper gitlab-domains list handling, LibraryInstaller RefCell double-borrow, ZipArchive::extract_to ErrorException propagation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/tests/downloader/zip_downloader_test.rs')
-rw-r--r--crates/shirabe/tests/downloader/zip_downloader_test.rs186
1 files changed, 146 insertions, 40 deletions
diff --git a/crates/shirabe/tests/downloader/zip_downloader_test.rs b/crates/shirabe/tests/downloader/zip_downloader_test.rs
index 0bffa89..055e98f 100644
--- a/crates/shirabe/tests/downloader/zip_downloader_test.rs
+++ b/crates/shirabe/tests/downloader/zip_downloader_test.rs
@@ -1,32 +1,79 @@
//! ref: composer/tests/Composer/Test/Downloader/ZipDownloaderTest.php
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use serial_test::serial;
+use shirabe::config::Config;
+use shirabe::downloader::ArchiveDownloader;
+use shirabe::downloader::zip_downloader::ZipDownloader;
+use shirabe::io::IOInterface;
+use shirabe::package::handle::{CompletePackageHandle, PackageInterfaceHandle};
+use shirabe::util::HttpDownloader;
+use shirabe::util::ProcessExecutor;
use shirabe::util::filesystem::Filesystem;
+use shirabe_php_shim::{ZipArchive, ZipArchiveMock};
+use shirabe_semver::VersionParser;
use tempfile::TempDir;
+use crate::io_stub::IOStub;
+
+fn run<F: std::future::Future>(future: F) -> F::Output {
+ tokio::runtime::Builder::new_current_thread()
+ .build()
+ .unwrap()
+ .block_on(future)
+}
+
struct SetUp {
test_dir: TempDir,
+ io: Rc<RefCell<dyn IOInterface>>,
+ config: Rc<RefCell<Config>>,
+ http_downloader: Rc<RefCell<HttpDownloader>>,
+ package: PackageInterfaceHandle,
filename: std::path::PathBuf,
}
+/// ref: ZipDownloaderTest::setUp.
+///
+/// The PHP test mocks IOInterface/Config/HttpDownloader/PackageInterface via PHPUnit. Here the
+/// IO/Config use the existing stubs, the HttpDownloader is built as a mock (no network is touched on
+/// the `extract` path exercised by the ported tests), and the package is a real CompletePackage whose
+/// `getName()` is `test/pkg`, matching the PHP mock's `->method('getName')->willReturn('test/pkg')`.
fn set_up() -> SetUp {
let test_dir = TempDir::new().unwrap();
- // The IO/Config/HttpDownloader/Package mocks are not ported; HttpDownloader construction
- // additionally reaches curl_multi_init (todo!()).
- let () = todo!();
- #[allow(unreachable_code)]
- {
- let filename = test_dir.path().join("composer-test.zip");
- std::fs::write(&filename, "zip").unwrap();
- SetUp { test_dir, filename }
+
+ let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(IOStub::new()));
+ let config = Rc::new(RefCell::new(Config::new(false, None)));
+ let dl_config = Rc::new(RefCell::new(Config::new(false, None)));
+ let http_downloader = Rc::new(RefCell::new(HttpDownloader::__new_mock(
+ io.clone(),
+ dl_config,
+ )));
+
+ let norm_version = VersionParser.normalize("1.0.0", None).unwrap();
+ let package: PackageInterfaceHandle =
+ CompletePackageHandle::new("test/pkg".to_string(), norm_version, "1.0.0".to_string())
+ .into();
+
+ let filename = test_dir.path().join("composer-test.zip");
+ std::fs::write(&filename, "zip").unwrap();
+
+ SetUp {
+ test_dir,
+ io,
+ config,
+ http_downloader,
+ package,
+ filename,
}
}
fn tear_down(test_dir: &std::path::Path) {
let mut fs = Filesystem::new(None);
fs.remove_directory(test_dir).unwrap();
- // setPrivateProperty('hasZipArchive', null) resets a ZipDownloader static via reflection;
- // the static is not reachable from a test here.
- todo!()
+ // setPrivateProperty('hasZipArchive', null)
+ ZipDownloader::__set_has_zip_archive(None);
}
struct TearDown {
@@ -45,76 +92,135 @@ impl Drop for TearDown {
}
}
-// These construct a ZipDownloader with a mocked IO/HttpDownloader/ProcessExecutor and rely
-// on ZipArchive extraction (todo!() in the php-shim) plus mocked unzip behaviour.
-#[ignore = "requires PHPUnit mocks of IOInterface/Config/PackageInterface; set_up() is todo!() and real HttpDownloader reaches curl_multi_init todo!(); no mocking framework"]
+fn make_downloader(set_up: &SetUp) -> ZipDownloader {
+ let filesystem = Rc::new(RefCell::new(Filesystem::new(None)));
+ let process = Rc::new(RefCell::new(ProcessExecutor::new(Some(set_up.io.clone()))));
+ ZipDownloader::new(
+ set_up.io.clone(),
+ set_up.config.clone(),
+ set_up.http_downloader.clone(),
+ None,
+ None,
+ filesystem,
+ process,
+ )
+}
+
+// The system-unzip / non-windows-fallback paths route through ProcessExecutor::execute_async, whose
+// mock branch is an unimplemented todo!() (no Process mock seam exists in the external-packages
+// crate). The PHP tests below mock Process/ProcessExecutor::executeAsync, which is not reproducible
+// here, so they remain ignored.
+//
+// testErrorMessages drives a real HttpDownloader + Loop (curl_multi_init todo!()), also out of reach.
+
+#[ignore = "drives a real HttpDownloader + Loop (download/install), which reaches curl_multi_init todo!()"]
#[test]
fn test_error_messages() {
let set_up = set_up();
let _tear_down = TearDown::new(set_up.test_dir.path().to_path_buf());
- let _ = (&set_up.test_dir, &set_up.filename);
+ let _ = (&set_up.io, &set_up.config, &set_up.http_downloader);
todo!()
}
-#[ignore = "requires setPrivateProperty reflection on hasZipArchive/zipArchiveObject, a MockedZipDownloader subclass, and a getMockBuilder('ZipArchive') mock; no mocking/reflection framework"]
#[test]
+#[serial]
fn test_zip_archive_only_failed() {
let set_up = set_up();
let _tear_down = TearDown::new(set_up.test_dir.path().to_path_buf());
- let _ = (&set_up.test_dir, &set_up.filename);
- todo!()
+
+ ZipDownloader::__set_has_zip_archive(Some(true));
+ let mut downloader = make_downloader(&set_up);
+ let zip_archive = ZipArchive::__mock(ZipArchiveMock {
+ open: Ok(()),
+ count: 0,
+ extract_to: Ok(false),
+ });
+ downloader.__set_zip_archive_object(Some(zip_archive));
+
+ let filename = set_up.filename.to_string_lossy().into_owned();
+ let result = run(downloader.extract(set_up.package.clone(), &filename, "vendor/dir"));
+
+ let e = result.expect_err("expected RuntimeException");
+ assert!(
+ e.to_string()
+ .contains("There was an error extracting the ZIP file"),
+ "got: {e}"
+ );
}
-#[ignore = "requires setPrivateProperty reflection on hasZipArchive/zipArchiveObject, a MockedZipDownloader subclass, and a getMockBuilder('ZipArchive') mock throwing ErrorException; no mocking/reflection framework"]
#[test]
+#[serial]
fn test_zip_archive_extract_only_failed() {
let set_up = set_up();
let _tear_down = TearDown::new(set_up.test_dir.path().to_path_buf());
- let _ = (&set_up.test_dir, &set_up.filename);
- todo!()
+
+ ZipDownloader::__set_has_zip_archive(Some(true));
+ let mut downloader = make_downloader(&set_up);
+ let zip_archive = ZipArchive::__mock(ZipArchiveMock {
+ open: Ok(()),
+ count: 0,
+ extract_to: Err("Not a directory".to_string()),
+ });
+ downloader.__set_zip_archive_object(Some(zip_archive));
+
+ let filename = set_up.filename.to_string_lossy().into_owned();
+ let result = run(downloader.extract(set_up.package.clone(), &filename, "vendor/dir"));
+
+ let e = result.expect_err("expected RuntimeException");
+ assert!(
+ e.to_string().contains(
+ "The archive for \"test/pkg\" may contain identical file names with different \
+ capitalization (which fails on case insensitive filesystems): Not a directory"
+ ),
+ "got: {e}"
+ );
}
-#[ignore = "requires setPrivateProperty reflection on hasZipArchive/zipArchiveObject, a MockedZipDownloader subclass, and a getMockBuilder('ZipArchive') mock; no mocking/reflection framework"]
#[test]
+#[serial]
fn test_zip_archive_only_good() {
let set_up = set_up();
let _tear_down = TearDown::new(set_up.test_dir.path().to_path_buf());
- let _ = (&set_up.test_dir, &set_up.filename);
- todo!()
+
+ ZipDownloader::__set_has_zip_archive(Some(true));
+ let mut downloader = make_downloader(&set_up);
+ let zip_archive = ZipArchive::__mock(ZipArchiveMock {
+ open: Ok(()),
+ count: 0,
+ extract_to: Ok(true),
+ });
+ downloader.__set_zip_archive_object(Some(zip_archive));
+
+ let filename = set_up.filename.to_string_lossy().into_owned();
+ let result = run(downloader.extract(set_up.package.clone(), &filename, "vendor/dir"));
+
+ result.expect("extract should succeed");
}
-#[ignore = "requires setPrivateProperty reflection on isWindows/hasZipArchive/unzipCommands, a MockedZipDownloader subclass, and getMockBuilder mocks of Process/ProcessExecutor::executeAsync; no mocking/reflection framework"]
+#[ignore = "routes through ProcessExecutor::execute_async whose mock branch is todo!() (no Process mock seam in external-packages)"]
#[test]
fn test_system_unzip_only_failed() {
- let set_up = set_up();
- let _tear_down = TearDown::new(set_up.test_dir.path().to_path_buf());
- let _ = (&set_up.test_dir, &set_up.filename);
+ let _ = set_up();
todo!()
}
-#[ignore = "requires setPrivateProperty reflection on isWindows/hasZipArchive/unzipCommands, a MockedZipDownloader subclass, and getMockBuilder mocks of Process/ProcessExecutor::executeAsync; no mocking/reflection framework"]
+#[ignore = "routes through ProcessExecutor::execute_async whose mock branch is todo!() (no Process mock seam in external-packages)"]
#[test]
fn test_system_unzip_only_good() {
- let set_up = set_up();
- let _tear_down = TearDown::new(set_up.test_dir.path().to_path_buf());
- let _ = (&set_up.test_dir, &set_up.filename);
+ let _ = set_up();
todo!()
}
-#[ignore = "requires setPrivateProperty reflection on isWindows/hasZipArchive/zipArchiveObject, a MockedZipDownloader subclass, and getMockBuilder mocks of Process/ProcessExecutor/ZipArchive; no mocking/reflection framework"]
+#[ignore = "routes through ProcessExecutor::execute_async whose mock branch is todo!() (no Process mock seam in external-packages)"]
#[test]
fn test_non_windows_fallback_good() {
- let set_up = set_up();
- let _tear_down = TearDown::new(set_up.test_dir.path().to_path_buf());
- let _ = (&set_up.test_dir, &set_up.filename);
+ let _ = set_up();
todo!()
}
-#[ignore = "requires setPrivateProperty reflection on isWindows/hasZipArchive/zipArchiveObject, a MockedZipDownloader subclass, and getMockBuilder mocks of Process/ProcessExecutor/ZipArchive; no mocking/reflection framework"]
+#[ignore = "routes through ProcessExecutor::execute_async whose mock branch is todo!() (no Process mock seam in external-packages)"]
#[test]
fn test_non_windows_fallback_failed() {
- let set_up = set_up();
- let _tear_down = TearDown::new(set_up.test_dir.path().to_path_buf());
- let _ = (&set_up.test_dir, &set_up.filename);
+ let _ = set_up();
todo!()
}