aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests
AgeCommit message (Collapse)Author
2026-07-18perf(installation-manager): run executeBatch operation chains concurrentlynsfisis
executeBatch now builds one future per operation — the PHP promise chain prepare -> install/update/uninstall -> cleanup -> repo->write, including the '<Op> of <pkg> failed' rejection handler covering the chain up to cleanup — and drives the whole batch through waitOnPromises()/Loop::wait, so archive extraction (the unzip subprocesses gated by ProcessExecutor's semaphore) finally overlaps across packages. Alias operations stay synchronous in the collection loop like PHP. The shared repository is threaded through the chains as RefCell<&mut dyn InstalledRepositoryInterface>: execute() wraps the incoming &mut once, and InstallerInterface::install/update/uninstall take the cell so implementations borrow it only in their synchronous head/tail, never across an await. InstallationManager's own install/update/uninstall/download/get_installer/get_install_path/ mark_for_notification move to &self (cache and notifiable_packages behind RefCell) so every chain can capture &self. WritableRepositoryInterface::write and the InstallationManagerInterface get_install_path it relies on lose their &mut manager requirement — the per-op repo->write inside the chains only reads install paths. Warm-cache create-project laravel/laravel: the package-operations phase (109 installs) drops from ~3.0-3.8s serial to ~1.9s, on par with real Composer (~2.1s) measured back-to-back; the resulting vendor tree, installed.json included, stays byte-identical to Composer's (diff -rq clean). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18perf(installation-manager): fan out package downloads via Loop::waitnsfisis
InstallationManager::downloadAndExecuteBatch now matches PHP: every update/install operation's installer->download() promise is collected and driven concurrently through waitOnPromises()/Loop::wait instead of being awaited one package at a time. Concurrency caps stay where PHP puts them (HttpDownloader 12, ProcessExecutor 10 via their semaphores). Error semantics follow PHP too: all downloads settle before the first rejection is rethrown, rather than aborting on the first failure. To let the collected futures and the cleanup closures own their installer beyond the loop iteration that created them, the installer registry becomes Vec<Rc<dyn InstallerInterface>> and get_installer hands out clones (PHP closures capture $installer the same way), with InstallerInterface methods taking &self across the six implementors — the only genuinely mutable state was LibraryInstaller.vendor_dir (canonicalized in place), now behind a RefCell. as_plugin_installer_mut/as_binary_presence_interface lose their &mut. The cleanup_promises entries are now the real thing: the PHP closure including the getInstallationSource() guard and the installer->cleanup($opType, $package, $initialPackage) call, replacing the no-op futures (drops one TODO(phase-b) and two TODO(phase-c)). Verified against the real network: create-project laravel/laravel produces a vendor tree byte-identical to real Composer's (diff -rq clean across all 109 packages including vendor/composer). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18refactor(downloader): take &self across the downloader hierarchynsfisis
Concurrent package operations call into the same downloader instances through Rc<RefCell<dyn DownloaderInterface>>; with &mut self methods every call holds a RefMut across its awaits, which panics with 'already mutably borrowed' the moment two operations overlap. This is groundwork for fanning out InstallationManager's download/install loops (same rework HttpDownloader/CurlDownloader already got). - DownloaderInterface/ChangeReportInterface/ArchiveDownloader/ VcsDownloader methods now take &self; as_change_report_interface returns &dyn instead of &mut dyn. - Implementors move their genuinely mutable state behind cells: FileDownloader.additional_cleanup_paths, the archive downloaders' cleanup_executed, ZipDownloader.zip_archive_object, VcsDownloaderBase.has_cleaned_changes, GitDownloader's stash/discard/ cache maps and GitUtil, SvnDownloader.cache_credentials, PerforceDownloader.perforce. FileDownloader.io gains a RefCell layer so get_local_changes can keep PHP's NullIO swap under &self. - ProcessExecutor::execute_async now returns a future that captures everything up front instead of borrowing the executor, and call sites build the future before awaiting, so no borrow on the shared executor is held while a subprocess runs. - Filesystem::remove_directory_async becomes remove_directory_async_via taking the Rc handle: the Filesystem is only borrowed for the sync head/tail, never across the rm subprocess await (sync borrow_mut users like rename/ensure_directory_exists would otherwise collide). - DownloadManager async call sites hold shared borrows only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18perf(regex): eliminate per-call clone overhead in preg_* dispatchnsfisis
regex::Regex::clone() does not share the underlying meta engine's search-cache pool, so every fresh clone pays a ~10us warmup cost on its first use. Two changes together eliminate this across nearly all preg_* call sites: - A php_regex! macro resolves PHP-style patterns to a per-call-site &'static regex::Regex (via regex-macro's LazyLock), applied at the majority of call sites throughout the codebase. - Call sites still passing dynamic pattern strings go through PATTERN_CACHE, which now stores Arc<(Regex, bool)> and hands out Arc::clone()s instead of cloning the Regex itself. PregPattern::resolve() returns a ResolvedPattern enum (Arc or 'static reference) rather than an owned Regex, so neither path ever clones the Regex proper. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18fix(require-command): avoid unsupported regex lookahead in dev-branch checknsfisis
The `regex` crate does not support negative lookahead, so the ported `^dev-(?!main$|master$|trunk$|latest$)` pattern panicked at runtime on any `require` invocation that reached version-selection. Replace it with equivalent hand-written string logic per docs/dev/regex-porting.md.
2026-07-17refactor(tests-async): unify duplicated tokio runtime bridgesnsfisis
11 downloader/installer integration-test files each redefined an identical current_thread `run()` helper to block on async code. Extract one shared multi_thread Runtime into tests/common/async_runtime.rs so concurrent #[test] threads can all block_on it, matching the direction item 7 (top-level Runtime) will take in production code.
2026-07-17refactor(http-downloader): drop the job table for a &self Semaphore corensfisis
Replaces Job/Request/JobHandle/id_gen/running_jobs/max_jobs with a tokio::sync::Semaphore permit held for the duration of each request. get/add/copy/add_copy are now &self (add/add_copy are also genuinely async); a shared execute()/dispatch() core replaces add_job/run_rfs_job/start_job/settle_job, returning the Response directly instead of deferring to wait()/count_active_jobs()/ get_response() (all removed — confirmed zero callers, same for the now-unused STATUS_* constants). get()/copy() stay synchronous rather than becoming async wrappers around add()/add_copy(), bridging via the existing sync_executor instead of the curl_runtime() introduced for CurlDownloader: their callers (~35 files reaching HttpDownloader) are mostly plain sync fns with no async boundary anywhere in the call chain, and forcing that propagation now would pull forward the dedicated async-propagation task. curl-eligible requests still route through curl_runtime() inside dispatch(), same as before — nesting sync_executor::block_on (no real reactor) around curl_runtime().block_on() (a real, separate Runtime) is safe; it's only nesting curl_runtime() inside itself that would panic. CurlDownloader no longer needs Rc<RefCell<>> wrapping despite the original design sketch: since item 2 made all of its methods &self, a plain Option<CurlDownloader> field works fine under HttpDownloader's own &self methods. get/add/copy/add_copy becoming &self (rather than &mut self) requires no changes at any of their ~35 calling files: RefMut/Ref both deref to a type that can call &self methods just fine. Verified manually against real network I/O (sandbox disabled): `shirabe show -a` (get()'s sync_executor-bridged path) and `shirabe create-project` (add_copy()'s genuinely async path via file_downloader.rs) both complete correctly with no hang.
2026-07-16refactor(remote-filesystem): return headers from copy/get_contentsnsfisis
Wrap RemoteFilesystem in Rc<RefCell<>> inside HttpDownloader, in prep for the upcoming &self conversion of add()/get(). copy()/get_contents() now bundle the response headers into their return value instead of requiring a follow-up get_last_headers() call, since two separate calls through a shared RefCell could otherwise race: nothing would guarantee the reader observes the headers from its own request rather than one clobbered by a concurrently borrowed call. get_last_headers() itself is left in place, mirroring RemoteFilesystem::getLastHeaders() in PHP.
2026-07-16fix(proxy-manager): correct singleton lifecycle and simplify to a plain Mutexnsfisis
reset() eagerly rebuilt the ProxyManager singleton immediately, capturing env vars before a caller could set them for the next request. PHP's reset() just nulls the static instance; getInstance() lazily constructs on next use. Match that so proxy env vars set after reset() are observed. get_instance() also ensured the singleton was constructed under its own lock, dropped that lock, and returned the bare Mutex; every caller then took a second, independent lock. A reset() landing in that gap would leave the caller observing None and panicking on .as_ref().unwrap(), a state the old eager-reconstructing reset() could not produce. Return the already-locked MutexGuard from get_instance() instead, so construction and use happen under one lock, and update all call sites accordingly. Holding that guard across a loop body then deadlocked in diagnose_command, since check_http_proxy transitively re-enters get_instance() via HttpDownloader -> CurlDownloader, and std::sync::Mutex is not reentrant. Re-acquire the lock fresh each iteration with a short-lived guard instead. Finally, Mutex::new is a const fn, so the OnceLock wrapper around it was unnecessary indirection; a bare static Mutex<Option<ProxyManager>> initializes to the same state without the get_or_init/get dance. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16refactor(locker-test): reuse test_case's installation_manager helpernsfisis
locker_test.rs defined its own installation_manager, identical to test_case.rs's (both build a bare InstallationManager over a mock HttpDownloader). Expose the shared one via pub(crate) and drop the duplicate. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16test(plugin): port capability-query tests, record remaining TODO(phase-d) ↵nsfisis
reasons Implement test_incapable_plugin_is_correctly_detected and test_querying_non_provided_capability_returns_null_safely against a real PluginManager, using hand-written PluginInterface/Capable stubs in place of PHPUnit's ad-hoc mocks. Wire the shared config_stub test helper into the plugin test binary, and derive Debug on the SetUp struct per this project's convention. The remaining plugin_installer_test.rs cases stay #[ignore]d: install/update/uninstall wiring in PluginInstaller and class instantiation in PluginManager::register_package are still TODO(plugin) stubs, so no plugin ever actually gets registered. Each now records its blocking TODO(plugin) site via // TODO(phase-d). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16test: update TODO reason for unported test casesnsfisis
No test logic changes.
2026-07-16test(downloader): record TODO(phase-d) reasons and port two git-downloader testsnsfisis
zip/git/file downloader tests that were already #[ignore]d for a documented reason lacked the required in-body // TODO(phase-d) comment; record it for each. Two git_downloader_test.rs cases, test_download_uses_various_protocols_and_sets_push_url_for_github and test_update_doesnt_throws_runtime_exception_if_git_command_fails_at_first_but_is_able_to_recover, had been left as todo!() with a TODO(phase-d) claiming ComposerMirror::process_git_url's github regex lacks PCRE delimiters and panics. That regex already has delimiters and does not panic (verified directly), and other tests in this same file already use Package::set_source_mirrors to give a real package a mirror-prefixed getSourceUrls() list, so the stated blocker no longer applies. Port both tests using that existing technique instead of recording a TODO(phase-d) for them. No other test logic changes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16test(util): port remaining todo!() tests in util test suitensfisis
Implement previously-todo!() tests in auth_helper_test.rs, process_executor_test.rs, remote_filesystem_test.rs, and stream_context_factory_test.rs by porting the corresponding PHPUnit test methods. Extend IOStub with writeRaw/setAuthentication call tracking and askAndValidate/getAuthentication overrides to model the PHPUnit mocks these tests rely on, deduping the resulting call-recording fields into a small generic CallRecorder<T> helper instead of repeating the same RefCell<Vec<T>> push/borrow().clone() boilerplate five times. testStoreAuthWithPromptInvalidAnswer and testPromptAuthIfNeededMultipleBitbucketDownloads had initially lost the ported PHPUnit mock's argument/call-count assertions (askAndValidate's exact prompt string, and hasAuthentication/getAuthentication's exactly(2) call counts), silently narrowing what the tests verify; IOStub now records these calls and the tests assert on them, matching upstream. Tests left unportable (PHP set_error_handler machinery, closures in data providers, network/subclass-mock dependencies, etc.) keep #[ignore] with a single // TODO(phase-d) reason recorded in the function body. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11chore: use fully-qualified name for Rc/RefCellnsfisis
2026-07-05feat(global-command): wire resetComposer and Application::run proxyingnsfisis
Application::find/reset_composer and the shared ApplicationHandle are now available, so GlobalCommand can reset the composer instance before building the sub-command input and proxy execution through the full Application::run dispatch, matching PHP's behavior. Un-ignores the tests that only depended on this wiring, and re-points the remaining ignores at their real (unrelated) blockers.
2026-07-05feat(validating-array-loader): implement LoaderInterfacensfisis
Convert errors/warnings/config to RefCell so load() can satisfy the trait's &self signature, matching upstream's `instanceof ValidatingArrayLoader` check in VcsRepository. This makes the InvalidPackageException downcast path in VcsRepository reachable for the first time instead of being permanently dead code.
2026-07-05feat(remote-filesystem): support file:// URLs in get_remote_contentsnsfisis
get_remote_contents was a full stub always returning None, so any file:// download raised a TransportException. Read local files directly for the file scheme, mirroring PHP's file_get_contents transparently handling the file:// stream wrapper. Also fixes file_get_contents5 to strip the file:// prefix like the 0-arg variant already did.
2026-07-04fix(event-dispatcher): avoid reentrant RefCell panicsnsfisis
Two related "already borrowed" panics reachable from AutoloadGenerator::dump() (which holds the local-repository, installation-manager, and config RefCells for the duration of its own statement, per the temporary-lifetime-extension pattern fixed separately in create_project_command.rs): - ensure_bin_dir_is_in_path called config.borrow_mut() to read "bin-dir", but Config::get only needs &self; use borrow() so it can coexist with an outer borrow instead of conflicting with it. - make_autoloader's real body needed composer_handle.borrow_mut() plus the same local-repository/installation-manager RefCells the caller already holds mutably, which cannot be made reentrant-safe without a larger restructuring. Since all 3 call sites already discard its return value, and its only effect (registering a Composer-generated ClassLoader for autoloading during event-listener PHP execution) is unobservable in this port — there's no embedded PHP interpreter to register it into, and class_exists for user-defined classes is a hardcoded-false shim so the caller's very next check always treats the class as unavailable regardless — make it a genuine no-op. This unblocks the post-autoload-dump event for any script listener naming a PHP class (e.g. Illuminate\Foundation\ComposerScripts), which every create-project/install run reaches once real packages get installed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04fix(pcre): restore missing regex delimiters in ported patternsnsfisis
Several Preg::*() call sites lost their PHP delimiter (and in one case the `i` modifier) during porting, since preg_*() expects the delimiter to be preserved in the caller's pattern literal and stripped internally. This made compile_php_pattern panic or silently misparse the pattern. Un-ignore the Version tests that were blocked by this bug.
2026-07-04test: ignore broken testsnsfisis
2026-07-02chore(lint): ban std::io::Read/Write, Any, Command use importsnsfisis
Extends no_banned_use to cover std::any::Any, std::io::Read/Write, and std::process::Command, and teaches the linter to allow `as _` imports so trait methods can still be brought into scope without binding the banned name. Fully qualifies all existing usages across the codebase.
2026-07-01test(repository-manager): port testFilterRepoWrappingnsfisis
PathRepository already implements RepositoryInterface and create_repository already handles the "path" class, so the prior ignore reason no longer applies; verify the FilterRepository/PathRepository wrapping via RepositoryInterfaceHandle::is/downcast_rc.
2026-07-01test(application): port two doRun script-command testsnsfisis
Port testNoPluginsDisablesPluginsWhenScriptCommandsExist and testScriptCommandTakesPriorityOverAbbreviatedBuiltinCommand. Both stay #[ignore]d because do_run panics at the script-registration todo!() (application.rs:2461) when composer.json has scripts. Add a test-only ApplicationHandle::__get_composer accessor so the first test's getPluginManager assertions can be expressed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01test(archive-manager): port testArchiveTar and testArchiveCustomFileNamensfisis
Wire a git downloader into the test's DownloadManager (mirroring Factory::createDownloadManager) so the archive path clones the package source as PHP does. Both remain #[ignore]'d on PharData tar archiving (new_with_format/build_from_iterator are todo!() in the php-shim). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01test(downloader): port ZipDownloader testErrorMessagesnsfisis
The faithful port drives a real HttpDownloader + Loop over a file:// dist URL, but RemoteFilesystem::get_remote_contents is a phase-c stub returning None, so the download fails with a "could not be downloaded" TransportException before reaching the ZipArchive "is not a zip archive" path. Kept #[ignore] with the corrected reason (the previous curl_multi_init note was wrong, file:// never uses curl). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01test(remote-filesystem): port get_contents, copy, bitbucket downloadnsfisis
These three tests exercise getContents/copy against a file:// URL (and a real network download for BitBucket). They remain #[ignore] because get_remote_contents has no stream/file layer yet (TODO(phase-c)) and returns None, so the calls raise a TransportException. The ignore reasons are updated to reflect the actual failure point. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01test(autoload): port 5 AutoloadGenerator testsnsfisis
Port testVendorDirExcludedFromWorkingDir, testUpLevelRelativePaths, testGeneratesPlatformCheck (all 12 data-provider rows), and both testAbsoluteSymlinkWith* tests from the Composer suite. testVendorDirExcludedFromWorkingDir passes. The other four expose behavioral gaps in shirabe (exclude-from-classmap with up-level/symlink paths, psr-4 symlink warnings, get_platform_check provider matching), so they keep their fully-ported bodies but are marked #[ignore] with the specific incompatibility rather than weakening expectations. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30refactor(git): replace is_callable with RunCommandOutput traitnsfisis
Model Git::runCommand's mixed $commandOutput parameter with the RunCommandOutput trait (one impl per PHP mode: discard, by-ref capture, callable handler), mirroring ProcessExecutor's IntoExecOutput. This moves the is_callable($commandOutput) value-inspection into the type system and drops the dependency on the shim's incomplete is_callable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29feat(audit): implement get_packages non-locked branchnsfisis
Port AuditCommand::getPackages's non-locked path: build an InstalledRepository from the local repository and return its packages, filtered by RootPackage requires when --no-dev is set. The prior TODO(phase-c) assumption (InstalledRepository::new vs get_local_repository type mismatch) no longer holds since both sides use RepositoryInterfaceHandle. Enables the two previously ignored audit command tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29feat(package): implement RootAliasPackage root getters via owned returnsnsfisis
It is faithful to PHP's by-value array/string semantics. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29fix(advisory): pass IO as shared handle to Auditor::auditnsfisis
Auditor::audit took io as &mut dyn IOInterface, forcing the audit and installer post-audit call sites to hold a borrow_mut() on the shared IO RefCell for the whole call. During advisory fetching the repositories write to their own clones of the same handle, so the borrow_mut() collided with their borrow() and panicked with 'RefCell already mutably borrowed' on 'audit --locked'. Take the Rc<RefCell<dyn IOInterface>> handle instead so writes borrow briefly and never overlap. Un-ignore the locked-audit regression test that this unblocks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29refactor(json): replace seld/jsonlint with serde_jsonnsfisis
Validate JSON syntax with serde_json's parse errors in JsonFile, and detect duplicate keys in ConfigValidator with a hand-written serde visitor, dropping the now-unused JsonParser/Lexer/DuplicateKeyException ports. ParsingException is kept as the thrown error type and downcast signal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28test(show): clarify ignore reason for empty platform sectionnsfisis
The platform: section is empty because PlatformRepository::get_packages routes to ArrayRepository::initialize instead of its own initialize, which needs the still-todo!() runtime::constant() shim. Record the real blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28fix(repository): flatten InstalledRepository and unwrap filter repos in shownsfisis
flattenRepositories must recurse into InstalledRepository (which extends CompositeRepository in PHP) and ShowCommand must unwrap FilterRepository when categorizing repos. Without this, installed/locked/platform packages all fell through to the "available" bucket, dropping the version column and per-section grouping. Un-ignores 10 show_command tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28fix(installer): execute install/update/uninstall ops in execute_batchnsfisis
execute_batch only awaited prepare(), leaving the install/update/ uninstall + cleanup + repo.write promise chain as a todo!() stub, so packages were downloaded to cache but never installed into the target directory. Wire the operation step (mirroring PHP's promise chain), propagate errors from the install/update/uninstall wrappers instead of swallowing them with .ok()?, and write the repo after each op. Un-ignore the create-project functional tests and the install/remove command tests this unblocks. The remaining --no-install case still fails on a separate install-path bug; its ignore reason is updated to match.
2026-06-28test(installer): port InstallerTest unit and integration harnessnsfisis
Port composer/tests/Composer/Test/InstallerTest.php. testInstaller (the provideInstaller cases) is fully ported and passes; the three integration tests port doTestIntegration in full (the .test fixture loader, FactoryMock, the in-process console Application with install/update commands, and the PHPUnit assertStringMatchesFormat matcher) and remain #[ignore]'d since the install pipeline is not yet executable end-to-end. Add test-only `__`-seams to the concrete types the test depends on, since their consumers (e.g. Locker takes the concrete InstallationManager) and the subclass-style mocks have no trait to mock: InstallationManager (recording mock + as_any), Factory (__create_mock), VersionGuesser, and InstalledFilesystemRepository. The production path (mock: false) is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28test(functional): port AllFunctionalTest integration harnessnsfisis
Implements the todo!() stubs in all_functional_test.rs by faithfully porting parseTestFile, cleanOutput and the inline %regex% EXPECT matcher, plus a subprocess runner that drives CARGO_BIN_EXE_shirabe (in place of PHP's composer.phar) over the reused Fixtures/functional .test files. The five fixtures are wired as individual #[serial] #[ignore] tests with per-fixture reasons: the two create-project cases need network + a real git clone, and the three update cases need the not-yet-implemented Plugin API. test_build_phar stays an ignored stub since Rust has no phar build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28refactor: add linternsfisis
2026-06-28test(json): drop JsonFormatterTest stubnsfisis
JsonFormatter is intentionally unported in crates/shirabe/src/json, so the ignored todo!() stub test serves no purpose. Remove the file and its module declaration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28test(downloader): cfg-gate XzDownloaderTest skip on Windows/32bitnsfisis
Replace the todo!() markTestSkipped placeholders in set_up with a compile-time cfg gate on the test function, mirroring PHP's setUp skips on Windows and 32bit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28test(command): port HomeCommandTest useCaseProvider casesnsfisis
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28test(command): port DiagnoseCommandTest casesnsfisis
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28test(tests): use mockall for hand-written interface mocksnsfisis
Replace hand-written mock/stub structs that re-implemented PHPUnit mock-builder behavior (record-and-verify, manual call counters, unreachable!() guards) with mockall::mock! locals across: - package/loader: MockLoader, VersionGuesserMock - command: ArchiveManager/RepositoryManager/EventDispatcher mocks - util: ConfigSource/AuthJson mocks (auth_helper, bitbucket, github, forgejo, gitlab) - repository/vcs: github_driver NullConfigSource - installer: CountingInstaller, RecordingBinaryInstaller, and the DownloadManager mock (formerly common/downloader_stub.rs, now deleted) - downloader: download_manager create_downloader_mock Verification (counts/args) now lives in mockall expectations checked on drop. installation_manager BinaryInstaller is left hand-written because its as_binary_presence_interface seam returns Some(&mut self), which mockall cannot express; io_stub and io_mock are left as-is. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28test(util/git): port interactive Bitbucket OAuth runCommand testnsfisis
Implement the four privateBitbucketWithOauthProvider cases that were stubbed as an ignored todo!(). Extend IOStub with per-question askAndHideAnswer responses and auth pre-seeding so the stateful OAuth flow can be driven without willReturnCallback, and inject a mock HttpDownloader plus no-op config sources to mirror PHPUnit's Config mock. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28test(repository): port testRepositoryWritesInstalledPhpnsfisis
Implement the previously-ignored test, which dumps installed.php via FilesystemRepository::write and byte-matches the Composer fixture. The package link setters needed by configureLinks are now reachable through the __set_provides/__set_replaces test hatches, so the PHP getPackage + configureLinks shape can be reproduced faithfully. Also switch both InstallationManager stubs in this file to a single mockall mock, replacing the hand-rolled impls and call counter. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28test(console): correct inaccurate test skip reasonsnsfisis
setCatchErrors is an old-Symfony compatibility shim that exists in neither Composer's PHP class nor Shirabe, and setAutoExit(false) is the default in Shirabe, so neither is a reason a test is unportable. Drop those mentions and point the application_test ignores at the real blocker: do_run's script-command registration is still a todo!(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28test(console): port DocumentationTest::testCommandnsfisis
Add __base_application accessor on ApplicationHandle so the test can build an ApplicationDescription the way console commands do. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28test(package/archiver): port testGitExcludesnsfisis
Process::fromShellCommandline and ZipArchive are now ported, so the git pipeline + zip reading have faithful equivalents. Kept #[ignore]d: the finder applies .gitattributes rules through BaseExcludeFilter::generate_pattern, whose (?=$|/) look-ahead regexes the regex crate cannot compile (same blocker as test_manual_excludes); also needs a git executable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28test(package/version): port testFindRecommendedRequireVersionnsfisis
Ported faithfully via PackageHandle::new + __set_extra (now available on PackageHandle). Kept #[ignore]d: date-based cases expose a shirabe_semver normalize divergence (v20121020 -> 20121020.0.0.0), not a test-port issue. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>