diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-25 16:44:29 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-26 00:20:05 +0900 |
| commit | f5f429dbae0a3e2d8224c0b1e4edcef54805d286 (patch) | |
| tree | 9f837baeeae6efa0ed926b181b8c273128d86c49 /crates/shirabe/tests | |
| parent | 3a0d9340810a8808d963135a884f50d08442ac67 (diff) | |
| download | php-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')
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!() } |
