aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-25 16:44:29 +0900
committernsfisis <nsfisis@gmail.com>2026-06-26 00:20:05 +0900
commitf5f429dbae0a3e2d8224c0b1e4edcef54805d286 (patch)
tree9f837baeeae6efa0ed926b181b8c273128d86c49 /crates/shirabe/tests
parent3a0d9340810a8808d963135a884f50d08442ac67 (diff)
downloadphp-shirabe-f5f429dbae0a3e2d8224c0b1e4edcef54805d286.tar.gz
php-shirabe-f5f429dbae0a3e2d8224c0b1e4edcef54805d286.tar.zst
php-shirabe-f5f429dbae0a3e2d8224c0b1e4edcef54805d286.zip
feat(http): reimplement CurlDownloader on reqwest; port 15 more tests
Replace the libcurl-shim CurlDownloader with a reqwest+tokio implementation per the .ken sketch, resolving the construction panic that blocked command tests (mock path via __new_mock is untouched). Port remote_filesystem (7), hg/svn driver (4), zip_archiver/git_exclude_filter (4) tests. Fix hg/svn/git_exclude regex-delimiter and svn result-propagation porting bugs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/tests')
-rw-r--r--crates/shirabe/tests/command/bump_command_test.rs9
-rw-r--r--crates/shirabe/tests/installer/binary_installer_test.rs11
-rw-r--r--crates/shirabe/tests/package/archiver/git_exclude_filter_test.rs1
-rw-r--r--crates/shirabe/tests/package/archiver/zip_archiver_test.rs7
-rw-r--r--crates/shirabe/tests/repository/vcs/hg_driver_test.rs105
-rw-r--r--crates/shirabe/tests/repository/vcs/svn_driver_test.rs66
-rw-r--r--crates/shirabe/tests/util/remote_filesystem_test.rs207
7 files changed, 344 insertions, 62 deletions
diff --git a/crates/shirabe/tests/command/bump_command_test.rs b/crates/shirabe/tests/command/bump_command_test.rs
index 8866990..96c8baf 100644
--- a/crates/shirabe/tests/command/bump_command_test.rs
+++ b/crates/shirabe/tests/command/bump_command_test.rs
@@ -41,11 +41,10 @@ fn test_bump_fails_on_non_existing_composer_file() {
#[test]
#[serial]
-#[ignore = "BumpCommand::initialize constructs a full Composer (Factory::create_http_downloader \
- -> CurlDownloader::new -> curl_multi_init), and the curl subsystem in shirabe-php-shim \
- is still todo!(). Only reachable once the HTTP/curl layer is ported. The companion \
- test_bump_fails_on_non_existing_composer_file covers the same error-output capture path \
- without reaching curl (the missing composer.json makes Composer construction a no-op)."]
+#[ignore = "CurlDownloader::new no longer panics (HTTP layer is now reqwest-based), but this path \
+ still reaches an unrelated todo!() in shirabe-php-shim stream_set_blocking (stream.rs), \
+ which requires fcntl(2). The companion test_bump_fails_on_non_existing_composer_file \
+ covers the same error-output capture path without reaching that shim."]
fn test_bump_fails_on_write_error_to_composer_file() {
if shirabe_php_shim::function_exists("posix_getuid") && shirabe_php_shim::posix_getuid() == 0 {
// ref: $this->markTestSkipped('Cannot run as root');
diff --git a/crates/shirabe/tests/installer/binary_installer_test.rs b/crates/shirabe/tests/installer/binary_installer_test.rs
index 7bd5eea..a3e3c24 100644
--- a/crates/shirabe/tests/installer/binary_installer_test.rs
+++ b/crates/shirabe/tests/installer/binary_installer_test.rs
@@ -20,11 +20,14 @@ impl Drop for TearDown {
}
}
-// This installs a PHP binary and then executes it via ProcessExecutor, asserting the
-// program's output. It needs a real PHP runtime, the binary-proxy generation, and a
-// mocked Package's getBinaries(), none of which are available here.
+// This installs a PHP binary (via NullIO + Package::__set_binaries + a real tempdir) and then
+// executes it through ProcessExecutor, asserting its output. Setup and binary-proxy generation
+// all work; the remaining blocker is real subprocess I/O: ProcessExecutor -> symfony Process
+// drives its pipe reads through `stream_select`/`stream_set_blocking`, both of which are still
+// `todo!()` in shirabe-php-shim (they require select(2)/fcntl(2) over the child pipe fds, which
+// the shim does not yet expose). Un-ignore once that pipe-reading layer is implemented.
#[test]
-#[ignore = "needs PHPUnit getMockBuilder mocks of IOInterface and Package::getBinaries() plus a real PHP runtime to execute the installed binary and assert its output"]
+#[ignore = "ProcessExecutor cannot read a real child's output yet: shirabe_php_shim::{stream_select, stream_set_blocking} are todo!() (need select(2)/fcntl(2) over child pipe fds)"]
fn test_install_and_exec_binary_with_full_compat() {
todo!()
}
diff --git a/crates/shirabe/tests/package/archiver/git_exclude_filter_test.rs b/crates/shirabe/tests/package/archiver/git_exclude_filter_test.rs
index 80f9f31..ab1d609 100644
--- a/crates/shirabe/tests/package/archiver/git_exclude_filter_test.rs
+++ b/crates/shirabe/tests/package/archiver/git_exclude_filter_test.rs
@@ -3,7 +3,6 @@
use shirabe::package::archiver::git_exclude_filter::GitExcludeFilter;
#[test]
-#[ignore]
fn test_pattern_escape() {
for (ignore, expected) in provide_patterns() {
let filter = GitExcludeFilter::new("/".to_string());
diff --git a/crates/shirabe/tests/package/archiver/zip_archiver_test.rs b/crates/shirabe/tests/package/archiver/zip_archiver_test.rs
index 7785976..b94beb6 100644
--- a/crates/shirabe/tests/package/archiver/zip_archiver_test.rs
+++ b/crates/shirabe/tests/package/archiver/zip_archiver_test.rs
@@ -1,6 +1,7 @@
//! ref: composer/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php
use indexmap::IndexMap;
+use serial_test::serial;
use shirabe::package::archiver::{ArchiverInterface, ZipArchiver};
use shirabe::package::handle::CompletePackageHandle;
use shirabe::util::Platform;
@@ -116,8 +117,8 @@ impl ArchiverTestCase {
}
}
-#[ignore = "ZipArchiver::archive and the ZipArchive reader (open/get_from_name/...) are todo!() in the php-shim"]
#[test]
+#[serial]
fn test_simple_files() {
let mut test_case = ArchiverTestCase::set_up();
@@ -137,8 +138,8 @@ fn test_simple_files() {
test_case.assert_zip_archive(files);
}
-#[ignore = "ZipArchiver::archive and the ZipArchive reader (open/get_from_name/...) are todo!() in the php-shim"]
#[test]
+#[serial]
fn test_gitignore_exclude_negation() {
for include in ["!/docs", "!/docs/"] {
let mut test_case = ArchiverTestCase::set_up();
@@ -154,8 +155,8 @@ fn test_gitignore_exclude_negation() {
}
}
-#[ignore = "ZipArchiver::archive and the ZipArchive reader (open/get_from_name/...) are todo!() in the php-shim"]
#[test]
+#[serial]
fn test_folder_with_backslashes() {
if Platform::is_windows() {
// markTestSkipped('Folder names cannot contain backslashes on Windows.')
diff --git a/crates/shirabe/tests/repository/vcs/hg_driver_test.rs b/crates/shirabe/tests/repository/vcs/hg_driver_test.rs
index 7b6f1e6..f00cb9d 100644
--- a/crates/shirabe/tests/repository/vcs/hg_driver_test.rs
+++ b/crates/shirabe/tests/repository/vcs/hg_driver_test.rs
@@ -9,18 +9,21 @@ use shirabe::io::IOInterface;
use shirabe::io::null_io::NullIO;
use shirabe::repository::vcs::HgDriver;
use shirabe::util::filesystem::Filesystem;
-use shirabe_php_shim::PhpMixed;
+use shirabe::util::http_downloader::HttpDownloaderMockHandler;
+use shirabe::util::process_executor::MockHandler;
+use shirabe_php_shim::{PhpMixed, RuntimeException};
use tempfile::TempDir;
+use crate::http_downloader_mock::{HttpDownloaderMockGuard, get_http_downloader_mock};
+use crate::io_stub::IOStub;
+use crate::process_executor_mock::{ProcessExecutorMockGuard, cmd_full, get_process_executor_mock};
+
struct SetUp {
home: TempDir,
config: Config,
- // The IOInterface mock is not ported.
- io: (),
}
fn set_up() -> SetUp {
- let io = ();
let home = TempDir::new().unwrap();
let mut config = Config::new(true, None);
let mut top: IndexMap<String, PhpMixed> = IndexMap::new();
@@ -32,7 +35,7 @@ fn set_up() -> SetUp {
top.insert("config".to_string(), PhpMixed::Array(config_section));
config.merge(&top, Config::SOURCE_UNKNOWN);
- SetUp { home, config, io }
+ SetUp { home, config }
}
fn tear_down(home: &std::path::Path) {
@@ -76,32 +79,94 @@ fn test_supports() {
}
}
-// The remaining cases construct an HgDriver, which requires an HttpDownloader
-// (curl_multi_init is todo!() in the php-shim) and a mocked ProcessExecutor to feed
-// hg command output, neither of which is available here.
#[test]
-#[ignore = "requires getProcessExecutorMock with expects() hg branches/bookmarks command-sequence assertions and a getMockBuilder HttpDownloader mock; no ProcessExecutorMock/HttpDownloader mocking infrastructure exists"]
fn test_get_branches_filter_invalid_branch_names() {
- let SetUp { home, config, io } = set_up();
+ let SetUp { home, config } = set_up();
let _tear_down = TearDown::new(home.path().to_path_buf());
- let _ = (&config, &io);
- todo!()
+
+ let config = Rc::new(RefCell::new(config));
+ let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(IOStub::new()));
+
+ let (http_downloader, _http_guard): (_, HttpDownloaderMockGuard) =
+ get_http_downloader_mock(vec![], false, HttpDownloaderMockHandler::default());
+
+ let stdout = "default 1:dbf6c8acb640\n--help 1:dbf6c8acb640";
+ let stdout1 = "help 1:dbf6c8acb641\n--help 1:dbf6c8acb641\n";
+
+ let (process, _process_guard): (_, ProcessExecutorMockGuard) = get_process_executor_mock(
+ vec![
+ cmd_full(["hg", "branches"], 0, stdout, ""),
+ cmd_full(["hg", "bookmarks"], 0, stdout1, ""),
+ ],
+ false,
+ MockHandler::default(),
+ );
+
+ let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new();
+ repo_config.insert(
+ "url".to_string(),
+ PhpMixed::String("https://example.org/acme.git".to_string()),
+ );
+
+ let mut driver = HgDriver::new(repo_config, io, config, http_downloader, process);
+
+ let branches = driver.get_branches().unwrap();
+ let mut expected: IndexMap<String, String> = IndexMap::new();
+ expected.insert("help".to_string(), "dbf6c8acb641".to_string());
+ expected.insert("default".to_string(), "dbf6c8acb640".to_string());
+ assert_eq!(expected, branches);
}
#[test]
-#[ignore = "requires getProcessExecutorMock and a getMockBuilder HttpDownloader mock to construct HgDriver; no ProcessExecutorMock/HttpDownloader mocking infrastructure exists"]
fn test_file_get_content_invalid_identifier() {
- let SetUp { home, config, io } = set_up();
+ let SetUp { home, config } = set_up();
let _tear_down = TearDown::new(home.path().to_path_buf());
- let _ = (&config, &io);
- todo!()
+
+ let config = Rc::new(RefCell::new(config));
+ let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(IOStub::new()));
+
+ let (http_downloader, _http_guard): (_, HttpDownloaderMockGuard) =
+ get_http_downloader_mock(vec![], false, HttpDownloaderMockHandler::default());
+
+ let (process, _process_guard): (_, ProcessExecutorMockGuard) =
+ get_process_executor_mock(vec![], false, MockHandler::default());
+
+ let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new();
+ repo_config.insert(
+ "url".to_string(),
+ PhpMixed::String("https://example.org/acme.git".to_string()),
+ );
+
+ let driver = HgDriver::new(repo_config, io, config, http_downloader, process);
+
+ assert_eq!(None, driver.get_file_content("file.txt", "h").unwrap());
+
+ let err = driver.get_file_content("file.txt", "-h").unwrap_err();
+ assert!(err.downcast_ref::<RuntimeException>().is_some());
}
#[test]
-#[ignore = "requires getProcessExecutorMock and a getMockBuilder HttpDownloader mock to construct HgDriver; no ProcessExecutorMock/HttpDownloader mocking infrastructure exists"]
fn test_get_change_date_invalid_identifier() {
- let SetUp { home, config, io } = set_up();
+ let SetUp { home, config } = set_up();
let _tear_down = TearDown::new(home.path().to_path_buf());
- let _ = (&config, &io);
- todo!()
+
+ let config = Rc::new(RefCell::new(config));
+ let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(IOStub::new()));
+
+ let (http_downloader, _http_guard): (_, HttpDownloaderMockGuard) =
+ get_http_downloader_mock(vec![], false, HttpDownloaderMockHandler::default());
+
+ let (process, _process_guard): (_, ProcessExecutorMockGuard) =
+ get_process_executor_mock(vec![], false, MockHandler::default());
+
+ let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new();
+ repo_config.insert(
+ "url".to_string(),
+ PhpMixed::String("https://example.org/acme.git".to_string()),
+ );
+
+ let driver = HgDriver::new(repo_config, io, config, http_downloader, process);
+
+ let err = driver.get_change_date("-r foo").unwrap_err();
+ assert!(err.downcast_ref::<RuntimeException>().is_some());
}
diff --git a/crates/shirabe/tests/repository/vcs/svn_driver_test.rs b/crates/shirabe/tests/repository/vcs/svn_driver_test.rs
index 5ec9097..a1e79fc 100644
--- a/crates/shirabe/tests/repository/vcs/svn_driver_test.rs
+++ b/crates/shirabe/tests/repository/vcs/svn_driver_test.rs
@@ -9,9 +9,15 @@ use shirabe::io::IOInterface;
use shirabe::io::null_io::NullIO;
use shirabe::repository::vcs::SvnDriver;
use shirabe::util::filesystem::Filesystem;
-use shirabe_php_shim::PhpMixed;
+use shirabe::util::http_downloader::HttpDownloaderMockHandler;
+use shirabe::util::process_executor::MockHandler;
+use shirabe_php_shim::{PhpMixed, RuntimeException};
use tempfile::TempDir;
+use crate::http_downloader_mock::{HttpDownloaderMockGuard, get_http_downloader_mock};
+use crate::io_stub::IOStub;
+use crate::process_executor_mock::{ProcessExecutorMockGuard, cmd_full, get_process_executor_mock};
+
struct SetUp {
home: TempDir,
config: Config,
@@ -75,13 +81,61 @@ fn test_support() {
}
}
-// Constructs an SvnDriver and runs an svn command via a mocked ProcessExecutor; mocking is
-// not available here.
-#[ignore = "requires ProcessExecutor mock (getProcessExecutorMock/expects) and IOInterface/HttpDownloader mocks, none available"]
#[test]
fn test_wrong_credentials_in_url() {
let SetUp { home, config } = set_up();
let _tear_down = TearDown::new(home.path().to_path_buf());
- let _ = &config;
- todo!()
+
+ let config = Rc::new(RefCell::new(config));
+ let console: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(IOStub::new()));
+
+ let (http_downloader, _http_guard): (_, HttpDownloaderMockGuard) =
+ get_http_downloader_mock(vec![], false, HttpDownloaderMockHandler::default());
+
+ let mut output = String::from("svn: OPTIONS of 'https://corp.svn.local/repo':");
+ output.push_str(" authorization failed: Could not authenticate to server:");
+ output.push_str(" rejected Basic challenge (https://corp.svn.local/)");
+
+ let authed_command = [
+ "svn",
+ "ls",
+ "--verbose",
+ "--non-interactive",
+ "--username",
+ "till",
+ "--password",
+ "secret",
+ "--",
+ "https://till:secret@corp.svn.local/repo/trunk",
+ ];
+
+ let (process, _process_guard): (_, ProcessExecutorMockGuard) = get_process_executor_mock(
+ vec![
+ cmd_full(authed_command, 1, "", output.clone()),
+ cmd_full(authed_command, 1, "", output.clone()),
+ cmd_full(authed_command, 1, "", output.clone()),
+ cmd_full(authed_command, 1, "", output.clone()),
+ cmd_full(authed_command, 1, "", output.clone()),
+ cmd_full(authed_command, 1, "", output.clone()),
+ cmd_full(["svn", "--version"], 0, "1.2.3", ""),
+ ],
+ true,
+ MockHandler::default(),
+ );
+
+ let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new();
+ repo_config.insert(
+ "url".to_string(),
+ PhpMixed::String("https://till:secret@corp.svn.local/repo".to_string()),
+ );
+
+ let mut svn = SvnDriver::new(repo_config, console, config, http_downloader, process);
+ let err = svn.initialize().unwrap_err();
+ let runtime = err
+ .downcast_ref::<RuntimeException>()
+ .expect("expected RuntimeException");
+ assert_eq!(
+ "Repository https://till:secret@corp.svn.local/repo could not be processed, wrong credentials provided (svn: OPTIONS of 'https://corp.svn.local/repo': authorization failed: Could not authenticate to server: rejected Basic challenge (https://corp.svn.local/))",
+ runtime.message
+ );
}
diff --git a/crates/shirabe/tests/util/remote_filesystem_test.rs b/crates/shirabe/tests/util/remote_filesystem_test.rs
index b39a2dd..a5d9d66 100644
--- a/crates/shirabe/tests/util/remote_filesystem_test.rs
+++ b/crates/shirabe/tests/util/remote_filesystem_test.rs
@@ -1,82 +1,243 @@
//! ref: composer/tests/Composer/Test/Util/RemoteFilesystemTest.php
-// These mock IO/Config/HttpDownloader and use reflection to drive RemoteFilesystem option
-// building and downloads; mocking/reflection are not available and a real HttpDownloader
-// reaches curl_multi_init (todo!()).
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use indexmap::IndexMap;
+use shirabe::io::IOInterface;
+use shirabe::util::RemoteFilesystem;
+use shirabe_php_shim::{PhpMixed, STREAM_NOTIFY_FILE_SIZE_IS, STREAM_NOTIFY_PROGRESS, strpos};
+
+use crate::config_stub::ConfigStubBuilder;
+use crate::io_stub::IOStub;
+
+// Mirrors RemoteFilesystemTest::getConfigMock: get('github-domains') and
+// get('gitlab-domains') return [], everything else returns null. add_authentication_options
+// reads gitlab-domains, so seed it as an empty list.
+fn config_mock() -> Rc<RefCell<shirabe::config::Config>> {
+ ConfigStubBuilder::new()
+ .with("github-domains", PhpMixed::List(vec![]))
+ .with("gitlab-domains", PhpMixed::List(vec![]))
+ .build_shared()
+}
+
+// Mirrors RemoteFilesystemTest::callGetOptionsForUrl: build a RemoteFilesystem, set the
+// private file_url, then invoke the private get_options_for_url with the given args.
+fn call_get_options_for_url(
+ io: Rc<RefCell<dyn IOInterface>>,
+ origin_url: &str,
+ additional_options: IndexMap<String, PhpMixed>,
+ options: IndexMap<String, PhpMixed>,
+ file_url: &str,
+) -> IndexMap<String, PhpMixed> {
+ let mut fs = RemoteFilesystem::new(io, config_mock(), options, false, None);
+ fs.__set_file_url(file_url);
+ fs.__get_options_for_url(origin_url, additional_options)
+}
+
+fn http_header_list(res: &IndexMap<String, PhpMixed>) -> Option<Vec<String>> {
+ res.get("http")
+ .and_then(|v| v.as_array())
+ .and_then(|http| http.get("header"))
+ .and_then(|v| v.as_list())
+ .map(|list| {
+ list.iter()
+ .map(|v| v.as_string().unwrap_or("").to_string())
+ .collect()
+ })
+}
+
#[test]
-#[ignore = "requires a mocked IOInterface (expects(once())->hasAuthentication) and ReflectionMethod to invoke private get_options_for_url plus ReflectionProperty on private file_url; no mocking/reflection infrastructure exists"]
fn test_get_options_for_url() {
- todo!()
+ let io: Rc<RefCell<dyn IOInterface>> =
+ Rc::new(RefCell::new(IOStub::new().with_has_authentication(false)));
+
+ let res = call_get_options_for_url(
+ io,
+ "http://example.org",
+ IndexMap::new(),
+ IndexMap::new(),
+ "",
+ );
+
+ assert!(
+ http_header_list(&res).is_some(),
+ "getOptions must return an array with headers"
+ );
}
#[test]
-#[ignore = "requires a mocked IOInterface (expects(once())->hasAuthentication/getAuthentication) and ReflectionMethod to invoke private get_options_for_url plus ReflectionProperty on private file_url; no mocking/reflection infrastructure exists"]
fn test_get_options_for_url_with_authorization() {
- todo!()
+ let mut auth: IndexMap<String, Option<String>> = IndexMap::new();
+ auth.insert("username".to_string(), Some("login".to_string()));
+ auth.insert("password".to_string(), Some("password".to_string()));
+ let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(
+ IOStub::new()
+ .with_has_authentication(true)
+ .with_get_authentication(auth),
+ ));
+
+ let options = call_get_options_for_url(
+ io,
+ "http://example.org",
+ IndexMap::new(),
+ IndexMap::new(),
+ "",
+ );
+
+ let mut found = false;
+ for header in http_header_list(&options).unwrap_or_default() {
+ if strpos(&header, "Authorization: Basic") == Some(0) {
+ found = true;
+ }
+ }
+ assert!(found, "getOptions must have an Authorization header");
}
#[test]
-#[ignore = "requires a mocked IOInterface (expects(once())->hasAuthentication/getAuthentication) and ReflectionMethod to invoke private get_options_for_url plus ReflectionProperty on private file_url; no mocking/reflection infrastructure exists"]
fn test_get_options_for_url_with_stream_options() {
- todo!()
+ let mut auth: IndexMap<String, Option<String>> = IndexMap::new();
+ auth.insert("username".to_string(), None);
+ auth.insert("password".to_string(), None);
+ let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(
+ IOStub::new()
+ .with_has_authentication(true)
+ .with_get_authentication(auth),
+ ));
+
+ let mut ssl: IndexMap<String, PhpMixed> = IndexMap::new();
+ ssl.insert("allow_self_signed".to_string(), PhpMixed::Bool(true));
+ let mut stream_options: IndexMap<String, PhpMixed> = IndexMap::new();
+ stream_options.insert("ssl".to_string(), PhpMixed::Array(ssl));
+
+ let res = call_get_options_for_url(
+ io,
+ "https://example.org",
+ IndexMap::new(),
+ stream_options,
+ "",
+ );
+
+ let allow_self_signed = res
+ .get("ssl")
+ .and_then(|v| v.as_array())
+ .and_then(|ssl| ssl.get("allow_self_signed"))
+ .and_then(|v| v.as_bool());
+ assert_eq!(
+ allow_self_signed,
+ Some(true),
+ "getOptions must return an array with a allow_self_signed set to true"
+ );
}
#[test]
-#[ignore = "requires a mocked IOInterface (expects(once())->hasAuthentication/getAuthentication) and ReflectionMethod to invoke private get_options_for_url plus ReflectionProperty on private file_url; no mocking/reflection infrastructure exists"]
fn test_get_options_for_url_with_call_options_keeps_header() {
- todo!()
+ let mut auth: IndexMap<String, Option<String>> = IndexMap::new();
+ auth.insert("username".to_string(), None);
+ auth.insert("password".to_string(), None);
+ let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(
+ IOStub::new()
+ .with_has_authentication(true)
+ .with_get_authentication(auth),
+ ));
+
+ let mut http: IndexMap<String, PhpMixed> = IndexMap::new();
+ http.insert(
+ "header".to_string(),
+ PhpMixed::String("Foo: bar".to_string()),
+ );
+ let mut additional_options: IndexMap<String, PhpMixed> = IndexMap::new();
+ additional_options.insert("http".to_string(), PhpMixed::Array(http));
+
+ let res = call_get_options_for_url(
+ io,
+ "https://example.org",
+ additional_options,
+ IndexMap::new(),
+ "",
+ );
+
+ let headers = http_header_list(&res);
+ assert!(
+ headers.is_some(),
+ "getOptions must return an array with a http.header key"
+ );
+ let headers = headers.unwrap();
+
+ let found = headers.iter().any(|header| header == "Foo: bar");
+ assert!(found, "getOptions must have a Foo: bar header");
+ assert!(headers.len() > 1);
}
#[test]
-#[ignore = "requires a mocked IOInterface and ReflectionMethod to invoke private callback_get plus ReflectionProperty to read private bytes_max; no mocking/reflection infrastructure exists"]
fn test_callback_get_file_size() {
- todo!()
+ let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(IOStub::new()));
+ let mut fs = RemoteFilesystem::new(io, config_mock(), IndexMap::new(), false, None);
+ fs.__callback_get(STREAM_NOTIFY_FILE_SIZE_IS, 0, Some(String::new()), 0, 0, 20)
+ .unwrap();
+ assert_eq!(20, fs.__bytes_max());
}
#[test]
-#[ignore = "requires a mocked IOInterface (expects(once())->overwriteError) and ReflectionProperty to set private bytes_max/progress and read private last_progress; no mocking/reflection infrastructure exists"]
fn test_callback_get_notify_progress() {
- todo!()
+ let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(IOStub::new()));
+ let mut fs = RemoteFilesystem::new(io, config_mock(), IndexMap::new(), false, None);
+ fs.__set_bytes_max(20);
+ fs.__set_progress(true);
+
+ fs.__callback_get(STREAM_NOTIFY_PROGRESS, 0, Some(String::new()), 0, 10, 20)
+ .unwrap();
+ assert_eq!(Some(50), fs.__last_progress());
}
#[test]
-#[ignore = "requires a mocked IOInterface and ReflectionMethod to invoke private callback_get; no mocking/reflection infrastructure exists"]
fn test_callback_get_passes_through404() {
- todo!()
+ let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(IOStub::new()));
+ let mut fs = RemoteFilesystem::new(io, config_mock(), IndexMap::new(), false, None);
+
+ fs.__callback_get(
+ shirabe_php_shim::STREAM_NOTIFY_FAILURE,
+ 0,
+ Some("HTTP/1.1 404 Not Found".to_string()),
+ 404,
+ 0,
+ 0,
+ )
+ .unwrap();
}
#[test]
-#[ignore = "requires a mocked IOInterface and a real get_contents which bottoms at curl_multi_init (todo!()); no mocking infrastructure exists"]
+#[ignore = "real get_contents bottoms out at curl_multi_init (todo!()) in StreamContextFactory; no network/stream layer is modeled"]
fn test_get_contents() {
todo!()
}
#[test]
-#[ignore = "requires a mocked IOInterface and a real copy which bottoms at curl_multi_init (todo!()); no mocking infrastructure exists"]
+#[ignore = "real copy bottoms out at curl_multi_init (todo!()) in StreamContextFactory; no network/stream layer is modeled"]
fn test_copy() {
todo!()
}
#[test]
-#[ignore = "requires a MockObject subclass of RemoteFilesystem overriding private get_remote_contents; no mocking infrastructure exists"]
+#[ignore = "requires a MockObject subclass of RemoteFilesystem overriding private get_remote_contents; no subclass-mocking infrastructure exists"]
fn test_copy_with_no_retry_on_failure() {
todo!()
}
#[test]
-#[ignore = "requires MockObject subclasses overriding RemoteFilesystem::get_remote_contents and AuthHelper::prompt_auth_if_needed; no mocking infrastructure exists"]
+#[ignore = "requires MockObject subclasses overriding RemoteFilesystem::get_remote_contents and AuthHelper::prompt_auth_if_needed; no subclass-mocking infrastructure exists"]
fn test_copy_with_success_on_retry() {
todo!()
}
#[test]
-#[ignore = "requires a mocked IOInterface and ReflectionMethod to invoke private get_options_for_url plus ReflectionProperty on private file_url; no mocking/reflection infrastructure exists"]
+#[ignore = "get_tls_defaults validates the (nonexistent) cafile and errors; constructor swallows it, so no ssl defaults are produced. Faithful porting needs CaBundle::validate_ca_file semantics for a missing file"]
fn test_get_options_for_url_creates_secure_tls_defaults() {
todo!()
}
#[test]
-#[ignore = "requires a mocked ConsoleIO (getMockBuilder disableOriginalConstructor) and a real getContents network download reaching curl_multi_init (todo!()); no mocking infrastructure exists"]
+#[ignore = "real getContents network download reaches curl_multi_init (todo!()); no network layer is modeled"]
fn test_bit_bucket_public_download() {
todo!()
}