aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests/command
AgeCommit message (Collapse)Author
2026-08-22test(show-command): run test_self againnsfisis
date() renders in the system default timezone, so the mismatch the ignore described is gone; the test passes with the local date ahead of or behind the UTC date. test_show_outdated_deps_sorting_by_age still fails that way, because ShowCommand::get_relative_time formats the release date in UTC while taking "today" from date(). Its ignore now says so on its own.
2026-08-22fix(package): dispatch get_links_for_type on Link::TYPE_* valuesnsfisis
PHP resolves a link kind through `$package->{'get'.ucfirst($linkType)}()`, so the argument is a `Link::$TYPES` value ("requires", "devRequires", ...). The helper matched the composer.json key names ("require", "require-dev", ...) instead, so every caller passing a `Link::TYPE_*` constant got an empty map back: `show <package>` printed none of its requires/provides/conflicts/ replaces sections, and `--format=json` carried none of those keys. The two callers that were passing composer.json keys now pass the matching `method`, as PHP does.
2026-08-22feat(cli): tell the user to run `shirabe`, not `composer`nsfisis
Messages that instruct the user to run a command named the Composer binary. Shirabe ships as its own binary, so the hints now name it. File names (composer.json, composer.lock), the "composer" repository type and prose about Composer itself are untouched. The installer and functional integration fixtures live in the Composer submodule and cannot be edited, so their expected output is normalized on load.
2026-08-19fix(input): thread a typed InputValue through the input layernsfisis
Options and arguments were stored and passed as PhpMixed even though Symfony only ever puts a string, a bool, a list of strings or null in one. get_option already narrowed to InputOptionValue at the boundary; this widens that enum into InputValue and pushes it through InputInterface, InputOption/InputArgument defaults, the Input storage, ArgvInput/ArrayInput/StringInput/CompletionInput, Command::add_option and add_argument, and the Composer-side wrappers. Two neighbouring string|int unions get types of their own: InputDefinition::{get_argument,has_argument} take an ArgumentName, and ArrayInput keys its parameters by ParameterName. has_parameter_option and get_parameter_option take the values they look for as &[&str], which is what PHP's `(array) $values` cast produced anyway. Two behaviours change along the way. Input::set_option on a negated option now negates with PHP's loose bool cast rather than treating a non-bool as false, matching `!$value`. ArrayInput::parse now resolves an integer key to an argument position instead of looking up an argument literally named "0". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18fix: propagate ported exceptions instead of flattening themnsfisis
`ProcessExecutor::execute_args` existed only to turn `execute`'s `anyhow::Result` into an exit code of 1, so every one of its ~87 call sites silently took the "command failed" branch on an error PHP would have thrown. It is gone; callers use `execute` and propagate with `?`. Where the enclosing function had no `Result` to propagate into, its signature grew one, up to and including `Git::get_version`, `Svn::binary_version`, `GitHub`/`GitLab`/`Bitbucket::authorize_oauth`, `InitCommand::get_git_config` and `DiagnoseCommand::check_git`. The VCS drivers had the same problem in the other direction: their `get_contents` returned `Result<Response, Box<TransportException>>`, a type too narrow for the PHP method, which lets any Throwable out of the `catch (TransportException $e)` block. Every non-transport error was therefore rewritten into a `TransportException` with code 0, which the callers switch on. They now return `anyhow::Result<Result<Response, Box<TransportException>>>`: the outer `Result` carries what PHP does not catch, the inner one the exception the drivers handle. That signature also restores `GitLabDriver::getContents`: the 400/401 `TransportException`s it raises to force authentication are thrown inside its own `try` block and handled by its own `catch`, but the port returned them straight to the caller, so the authentication flow behind them never ran. `impl_php_exception!` gains `From<Box<$ty>> for anyhow::Error` so a caught exception can be re-propagated with `?`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17refactor(preg): drop unused args of preg_match_all_offset_capture2()nsfisis
The sole caller, Preg::match_all_with_offsets5(), always passed flags=0 and offset=0, so PREG_UNMATCHED_AS_NULL is now unconditional and the subject is scanned from the beginning. That method is only reached from Preg::match_all_with_offsets(), so it is no longer public either. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16feat(php-shim): render human-facing timestamps in the local timezonensfisis
Split date() into date_utc() and date_local(), the latter resolving the system's local timezone through the tzfile crate ($TZ, then /etc/localtime, falling back to UTC when neither is readable). The timestamps Composer renders for humans -- the GitHub OAuth token note, the GitHub API rate limit reset time, the Perforce client spec fields and the "today" check of the show command -- now go through date_local(). PHP resolves its default timezone from the date.timezone ini setting, which Shirabe does not read, so date_default_timezone_get/set have no input left to model and are dropped from the shim and its callers. The resulting difference is recorded in docs/known-incompatibilities.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15feat(cli): report Shirabe's own identity instead of Composer'snsfisis
The binary called itself Composer everywhere: the application name, the logo, --version, about, and every warning that talks about the running program. Prompts to file a bug also pointed at Composer's issue tracker. Add SHIRABE_VERSION and SHIRABE_RELEASE_DATE next to the Composer version constants and report those, naming the Composer version this port tracks alongside them. Composer::VERSION and getVersion() are untouched, so the composer platform package, composer-runtime-api and the HTTP User-Agent keep the value plugins and package repositories expect. build.rs stamps the release date with the UTC date of the HEAD commit, the way Composer's Compiler fills in @release_date@ when building the phar. It now also fails the build when git cannot be read, instead of letting COMPOSER_DEV_WARNING_TIME fall back to the tagged-release value and suppress the outdated-build warning forever. Messages about the Composer ecosystem keep their wording. Two of them are pinned by upstream installer fixtures (Rule's "cannot be modified by Composer" and SolverProblemsException's "you can run Composer with") and stay as they are so those fixtures can keep being used verbatim. The e2e list comparison against upstream Composer now skips the banner, which cannot match by design, and compares everything below it as before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15feat(diagnose): audit the Composer runtime the executable carriesnsfisis
checkComposerAudit reported success instead of auditing anything, because the binary ships no vendor/composer/installed.json on disk. It reads the one in the embedded Composer PHP runtime now, and Composer's warning for a missing installed.json is back. Only that file leaves the bundle, into a temporary directory that goes away with the handle; the runtime is unpacked whole only for a worker that cannot read the bundle in place. Phar::extractTo's $files argument selects it, which the shim ignored so far. SHIRABE_COMPOSER_PHP_DIR moves into composer_runtime, so the worker and a reader on the Rust side resolve the runtime through the same branch. DiagnoseCommandTest::testCmdSuccess is ignored: packagist has advisories against composer/composer 2.9.7, the version Composer::VERSION reports, so diagnose exits 1 where the test expects 0. Upstream Composer 2.9.7 reports the same advisories. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12test: drop stale #[ignore]s and rewrite the run-script reasonsnsfisis
Measuring every ignored test with `cargo test --workspace --no-fail-fast -- --ignored` turned up 11 that pass today; their reasons named todo!()s and gaps that have since been implemented. The two run_script_command_test cases still fail, but not for the reason given: the user's Command class is imported and executed, and what is missing is only a way to observe it, since PhpCommandProxy::run forwards the run to the worker-side console application. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12docs(todo): retag TODO markers by root causensfisis
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10refactor(symfony): hide leaf modules behind their parent re-exportsnsfisis
Every parent module in the symfony-* crates already re-exported its leaf modules with `pub use`, so each item was reachable by two paths. Make the leaf modules private and route all callers through the single re-exported path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09refactor(symfony-console): extract symfony/console into the ↵nsfisis
shirabe-symfony-console crate Move `Symfony\Component\Console` out of shirabe-external-packages and into its own crate, so the path is `shirabe_symfony_console::application::Application` instead of `shirabe_external_packages::symfony::console::application::Application`. The `delegate_to_inner!` and `delegate_command_trait_impls_to_inner!` macros move with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09refactor(symfony-process): extract symfony/process into the ↵nsfisis
shirabe-symfony-process crate Move `Symfony\Component\Process` out of shirabe-external-packages and into its own crate, so the path is `shirabe_symfony_process::Process` instead of `shirabe_external_packages::symfony::process::Process`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07test: port the tests left as todo!() stubsnsfisis
Replace the todo!() bodies with real ports. Four autoload-generator tests now run for real; the rest stay #[ignore]d, but each ignore reason now names the concrete missing symbol instead of a vague subsystem. Production additions the ports need: the deprecated AuthHelper::addAuthenticationHeader wrapper, EventDispatcher::__set_dispatch_script_override as the seam for PHPUnit onlyMethods(['dispatchScript']), and a define() stub in the shim. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06chore: drop @param/@return tags that only restate Rust typesnsfisis
The ported docblocks copied @param and @return straight from the PHP source. When such a tag carries nothing but a type and an argument name, the Rust signature already states it, so the line is noise. Tags whose text adds prose beyond the type are kept. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06refactor(php-shim): take filesystem paths as impl AsRef<Path>nsfisis
The shim's filesystem entry points took `&str` even though each one resolves to a local path through `std::fs` or a syscall, so callers holding a `PathBuf` had to stringify it at the call site. They now take `impl AsRef<Path>`, the form `file_exists`, `is_dir`, `unlink` and the rest of the already-converted set use. `Phar`, `PharData` and `ZipArchive` keep their archive path as a `PathBuf`. `PharData::compress` names the compressed sibling by appending the suffix to the file name rather than formatting the path into a `String`. Arguments PHP resolves through a stream wrapper (`fopen`, `file_put_contents`, `include`) still take `&str`, as do the byte-string operations (`dirname`, `basename`, `pathinfo`) and archive-internal entry names, which are `/`-joined logical names rather than OS paths. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05test(plugin): pin a composer-normalize fixture and compare its listingnsfisis
The pinned plugin and its real dependency tree (nine packages, fetched by commit and verified by tree hash into the git-ignored ext/) install under both implementations, and the list/help renderings of the plugin-provided normalize command must match upstream byte for byte. The execution comparison is written but ignored: NormalizeCommand builds a second, in-process Composer instance, and the worker's proxy stubs reject native construction of the classes that path instantiates. Stale comments about the missing worker-side application are updated to the current facts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04docs: state code facts instead of porting-phase progressnsfisis
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04feat(plugin): remove subscribed listeners when a plugin is removednsfisis
Wire removePlugin to EventDispatcher::removeListener now that subscriber listeners carry the plugin's P-table handle: PHP's $candidate[0] === $listener identity check maps to phandle equality on Callable::PhpMethod. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04feat(plugin): dispatch plugin event subscribers through the RPC workernsfisis
Wires the addPlugin subscriber branch end to end: EventSubscriberInterface and Capable become fallible and dyn-compatible (their sole implementor is the PHP plugin proxy, which answers getSubscribedEvents over RPC), listeners register as Callable::PhpMethod and are invoked with a per-call event handle, and the R table now drops entries when a child-side stub destructs. The R table keeps its IndexMap with monotonically increasing handles, so released handles are never reused and no generation counter is needed. Upstream has no subscriber-plugin test, so the path is covered by a Shirabe-owned fixture exercising all three getSubscribedEvents shapes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03feat(event-dispatcher): run composer.json PHP scripts through the RPC workernsfisis
Implement the two script execution paths that previously stopped at todo!(): a Class::method listener is invoked as CallStaticMethod with the event crossing the boundary as a proxy-stub handle, and a Command-class listener runs inside a throwaway bare Symfony Application hosted by the worker via a generated snippet, its BufferedOutput written back through the dispatcher's IO. makeAutoloader is ported for real (canonical-package hash, setDevMode, buildPackageMap/parseAutoloads/createLoader), and the class_exists/is_callable/is_a/defined guards now query the worker, whose script autoloader resolves classes by asking the Rust-side ClassLoader over the reverse channel. EventInterface gains as_any (the IOInterface downcast pattern) so the concrete event type is reachable behind the trait object. Application::do_run now registers ScriptAliasCommand entries as typed commands, unblocking the run-script --list/alias tests; the dev-mode-to-generator test is ported with local mockall mocks. The remaining ignored tests carry re-verified reasons: the listener methods live on the PHPUnit test class itself (unloadable in the worker), or the test needs live import of a user PHP Command class into the Application. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02fix(repository): resolve remaining late-binding hazards from the auditnsfisis
Three fixes for the ComposerRepository/FilesystemRepository/PlatformRepository hazards where inner-composition delegation skipped PHP's late-bound virtual dispatch: - ComposerRepository::has_package now builds its packageMap through the late-bound getPackages() equivalent, so lazy-providers repos surface the LogicException and available-packages repos load their package list, as in PHP, instead of silently answering false from the raw array. - RepositoryInterface::get_repo_name returns anyhow::Result<String>: PHP's getRepoName() counts through the late-bound initialize(), which is fallible in file-reading subclasses. FilesystemRepository and PackageRepository now run that initialization instead of freezing the inner array repository to an empty state (which also made a later write() truncate installed.json). Supporting changes keep the initialization chain callable from &self: JsonFile::read takes &self (indent moved into a RefCell), FilesystemRepository dev_mode became a Cell, and WritableArrayRepository dev_package_names a RefCell. - PlatformRepository::new routes constructor packages through its own add_package so the override handling and full platform initialization run as they do via PHP's parent constructor; the inner find_package/add_package delegations inside add_package (and ComposerRepository::add_package) gained the same is_initialized guard, since the constructor path would otherwise freeze the repository. Same defect class as 7db937af, 97b5211a and 3e367f78. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02fix(json): propagate JsonFile::encode errors instead of unwrappingnsfisis
PHP's JsonFile::encode throws a RuntimeException when json_encode fails; the port swallowed that into an .unwrap() marked TODO(phase-c). Return anyhow::Result from encode/encode_with_options and propagate at every call site (print_table and list_repositories become Result-returning to carry it). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02fix(io): propagate ask/select errors instead of panickingnsfisis
IOInterface::ask/select now return anyhow::Result<PhpMixed>, and ConsoleIO::ask_question forwards QuestionHelper errors (validator failures, MissingInputException) instead of collapsing them with .expect(). In PHP these exceptions propagate from QuestionHelper through ConsoleIO to the caller, so callers such as UpdateCommand's interactive package selection must be able to observe them; the MissingInputException is wrapped with its concrete type preserved so Application's ExceptionInterface downcast keeps working. All call sites now propagate with `?` (Perforce::query_p4_user becomes Result-returning: PHP declares it void but exceptions still escape), and the previously ignored test_interactive_mode_throws_if_no_package_entered passes. ask_confirmation/ask_and_hide_answer still collapse errors; extending propagation to them is left as TODO(phase-c) pending a decision. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02fix(diagnose-command): pass the self-audit when installed.json is absentnsfisis
A native binary never ships vendor/composer/installed.json, so Composer's "non-standard Composer installation" warning fired on every diagnose run and forced exit 1. The self-audit itself stays: a Composer source snapshot is planned to be embedded together with the plugin API implementation, which will make it functional; until then the missing file reports success, marked with TODO(phase-c). Also un-ignore diagnose_command_test::test_cmd_success: the other half of its ignore reason ("requires real network access") is no blocker — the PHP original runs its live packagist/github checks unguarded too. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26feat(diagnose-command): query the real PHP runtime in one RPC callnsfisis
diagnose used to read hardcoded shim stubs, so it described a fictional runtime: OPENSSL_VERSION_NUMBER was always 0 and tripped the TLSv1.1/1.2 check, PHP_BINARY and OPENSSL_VERSION_TEXT were empty, and the extension, function and ini probes answered from a fixed table. The PHP worker gained a `diagnose` entry that returns every fact the command needs as one PHP array, cached in a OnceLock so the several call sites share a single round trip. Reading it back needed array support in the serialize() parser, which in turn lets get_loaded_extensions and get_all_ini_files return real lists instead of comma-joined strings. Also fixes the openssl_version message, which dropped strstr()'s before_needle argument during the port, and check_connectivity's allow_url_fopen test, which did not follow PHP string truthiness. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25fix(require-command): end input borrow before asking for confirmationnsfisis
The `fixed` option was read as a temporary inside the argument list of update_requirements_after_resolution(), so the Ref lived until the end of the enclosing let statement — i.e. across the whole call. When the resolved version looks like a feature branch, that call asks for confirmation, and ConsoleIO takes the same input RefCell mutably, panicking with "RefCell already borrowed". Hoisting the read into its own statement ends the borrow before the call. The expected output of the un-ignored test transcribed PHP's string concatenation operator (`[y,n]? '.'`, used to keep the trailing space visible) as a literal `.`; it now matches RequireCommandTest.php. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25fix(package-discovery): pass IO so platform requirement warnings surfacensfisis
findBestVersionForPackage is the only findBestCandidate() caller that PHP hands $this->getIO() to; the port passed None, so VersionSelector silently skipped every "Cannot use <pkg> as it requires <ext> which is missing from your platform" warning. require/update/create-project therefore dropped a candidate without telling the user why. Un-ignores require_command_test::test_require, whose first data-provider case asserts exactly that warning. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25refactor: replace redundant clones with movesnsfisis
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-24fix(event-dispatcher): invoke Closure listeners instead of always failing ↵nsfisis
is_callable RequireCommand registers an inline listener on InstallerEvents::PRE_OPERATIONS_EXEC to track dependency_resolution_completed, mirroring PHP's `function () use (&$dependencyResolutionCompleted) { ... }`. This is Composer's own code, not a Plugin subscriber, but it went through the shared non-string-callable path, which checked is_callable() against a hardcoded PhpMixed::Null and always failed, breaking every `require` that reaches the install step. Callable::Closure now carries the actual Rc<dyn Fn> instead of being a data-less placeholder, and is invoked directly (Closures are always callable in PHP). The ArrayCallable path used by future Plugin subscribers is untouched. Un-ignoring the two require_command_test cases that cited this bug reveals two separate, pre-existing issues (a missing ext-requirement warning message, and a RefCell re-entrancy panic in ConsoleIO::ask_question); their #[ignore] reasons are updated to describe the real current blocker instead of the now-fixed one.
2026-07-20test(ignore): document root causes for unannotated #[ignore] testsnsfisis
74 tests carried a bare #[ignore] with no explanation. Re-ran each: 25 now pass and had the attribute removed; the remaining 49 got a concise reason (todo!() stubs, regex-crate PCRE gaps, PhpMixed type mismatches, config bool-coercion bugs, missing skipped_load wiring in PoolBuilder, etc.) so future work can find and fix them by grep. No production code or test logic/assertions were changed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20fix(diagnose-command): stop holding a Config borrow across http callsnsfisis
execute() held &config.borrow() across check_http/check_composer_repo/ check_composer_audit, which reach HttpDownloader -> CurlDownloader:: download; that method does self.config.borrow_mut() on the same Config RefCell, panicking with "RefCell already borrowed" once the phpinfo panic that previously masked this was fixed. Switch those three helpers to take the Rc<RefCell<Config>> handle (matching check_version's existing pattern) and borrow only where a field is actually read, so no borrow spans the downstream network call. Un-ignore the now-passing run_diagnose smoke test and test_cmd_fail; test_cmd_success stays ignored, now for two separate reasons: it needs real network access (as the PHP original does), and shirabe_php_shim::OPENSSL_VERSION_NUMBER is a hardcoded stub (0) that always trips check_platform's TLSv1.1/1.2 support check regardless of the real linked OpenSSL, forcing a non-zero exit code.
2026-07-20feat(php-rpc): implement phpinfo() capture over RPCnsfisis
DiagnoseCommand::check_platform needed phpinfo() output but shirabe-php-shim's ob_start()/phpinfo()/ob_get_clean() are unmodeled todo!()s. Add a phpinfo dispatch entry to the PHP worker (capturing output the same way extension_info already does) and a get_phpinfo() wrapper, then switch check_platform to call it directly. This unblocks the phpinfo-related panic in diagnose; the ignored tests now hit a separate RefCell double-borrow bug in check_http, so their ignore reasons are updated to point at that instead.
2026-07-20test(fund-command): match PHP tester options for deterministic displaynsfisis
capture_stderr_separately routed the tester through ConsoleOutput, which detects color/hyperlink support from the real process STDOUT before the stream is swapped to the memory buffer. When STDOUT is a real tty, decoration leaks into the captured output. The PHP original never passes this option and get_error_output() was unused here, so drop it to match and keep the assertion deterministic.
2026-07-20test(show-command): ignore test_self over UTC-only date() timezone gapnsfisis
The shim date() renders in UTC only (no timezone database) while PHP's date() uses the system default timezone, so get_relative_time misses the "today" match and prints "this week" whenever the local date differs from the UTC date (daily 00:00-09:00 JST on this machine). Verified by running the test with and without TZ=UTC at 07:40 JST. Mark the gap with a TODO(phase-c) in the shim; fixing it needs a timezone database (a new crate), which is a user decision. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20fix(update-command): un-ignore test_interactive_tmp via assoc select choicesnsfisis
PHP's UpdateCommand::getPackagesInteractively passes $autocompleterValues keyed by package name to $io->select, so the selection resolves to package names. The port passed only the keys as a list, making select resolve to a numeric index that the update then treated as an unknown package. The old ignore reason (non-interactive terminal error) no longer applied. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20test(run-script-command): detail dev-mode test ignore blockersnsfisis
Re-verified the reason: besides the missing as_any seam on EventInterface (a cross-cutting trait change over every event type), the mocked dispatchScript call expectation is also inexpressible since dispatch_script is a concrete method with no call-recording seam. Record both blockers in the ignore string and TODO(phase-d) comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20test(diagnose-command): detail ignore reasons with the actual blockernsfisis
The tests fail before any network access: DiagnoseCommand::check_platform captures phpinfo() via ob_start()/ob_get_clean(), which are todo!() in shirabe-php-shim, so the command panics before producing output. Record that as the primary blocker alongside the live-network requirement. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20fix(validating-array-loader): un-ignore test_fund_command by fixing empty checknsfisis
validate_array judged emptiness via as_array(), which only matches PhpMixed::Array, so a non-empty PhpMixed::List (e.g. a funding array) was misjudged as empty and dropped, diverging from PHP's !count() check. Match both Array and List. The stale ignore reason on test_fund_command no longer applies: init_temp_composer injects packagist:false so no network is reached, and the downloader stack is reqwest-based (no curl shim todo!()). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20fix(process-executor): un-ignore 11 tests by implementing execute_async mock ↵nsfisis
support ProcessExecutor::execute_async's mock branch was an unimplemented todo!(), blocking every test whose code path calls it (feature-branch git diffing, system-unzip/7z fallback extraction). Implement it by: - Adding Process::__mock (mirroring the existing ZipArchive::__mock pattern) so execute_async can resolve with a fabricated, already- terminated Process instead of spawning a real subprocess. - Extracting the sync mock's expectation-matching logic into a shared ProcessExecutor::mock_match, wrapping the mock state in a RefCell so it works from execute_async's &self/&mut self receivers. - Setting error_output/capture_output from the mock branch, matching PHP's ProcessExecutorMock::executeAsync sharing doExecute with the sync path; execute_async now takes &mut self for this (safe, since the borrow only needs to live through the synchronous setup, not across the .await). - Turning a strict-mode expectation mismatch from a panic!() into a shirabe_php_shim::RuntimeException Err, mirroring PHPUnit's AssertionFailedError extending \RuntimeException: PHP call sites that catch (\RuntimeException $e) around a mocked git/hg/svn call (e.g. Git::get_mirror_default_branch, GitDriver::supports) treat a mismatch as an ordinary recoverable failure, and now so does the port. The RefMut is dropped before firing an expectation's optional callback so a re-entrant callback doesn't panic on double-borrow. Also fixes two real bugs found while porting test_private_repository_ no_interaction: GitHub::authorize_oauth and GitLab::authorize_oauth checked their domains config via PhpMixed::as_array(), which only matches the Array (map) variant, but github-domains/gitlab-domains default to PhpMixed::List, so the check always returned false and OAuth token lookup was silently skipped. Use the in_array shim instead, matching PHP's in_array() semantics. Also fix Git::run_command's "capture credentials from git remote -v" call, which used the panic- swallowing execute_args wrapper instead of a fallible execute(), so a mock mismatch there couldn't reach get_mirror_default_branch's catch. Un-ignores: - zip_downloader_test::test_system_unzip_only_{good,failed} - zip_downloader_test::test_non_windows_fallback_{good,failed} - event_dispatcher_test::test_dispatcher_outputs_error_on_failed_command - root_package_loader_test::test_feature_branch_pretty_version - version_guesser_test::test_guess_version_reads_and_respects_non_feature_branches_configuration_for_arbitrary_naming{,_regex} - version_guesser_test::test_remote_branches_are_selected - github_driver_test::test_private_repository_no_interaction (also adds the missing #[serial], since it seeds the shared Git::VERSION static that vcs_repository_test::test_load_versions depends on for real) - init_command_test::test_get_git_config, made deterministic by pointing HOME at a throwaway dir with its own .gitconfig instead of depending on the host's global git config Deduplicates the GitVersionGuard/RestoreEnv test-drop-guard idioms into tests/common/test_case.rs instead of reimplementing them per file. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19fix(root-package-loader): un-ignore 4 tests by fixing stability-flag isset() ↵nsfisis
port extract_stability_flags ported PHP's `isset($stabilityFlags[$name]) && $stabilityFlags[$name] > $stability` as `unwrap_or(i64::MAX) > stability`, so the check always short-circuited to "already more unstable" and no flag (e.g. from an explicit `*@dev` requirement) was ever recorded. Fixed using Option::is_some_and, a direct translation of PHP's isset() && ... check. Also fixes tests/common/test_case.rs's shared installation_manager() helper, which built a real InstallationManager::new instead of the __new_mock constructor (mirroring PHP's FactoryMock::createInstallationManager()), so it always had zero installers registered and wrote install-path: null into fixture installed.json files. Un-ignores test_reinstall_command, test_locally_modified_packages_from_source/ _from_dist, and test_package_still_present_error_when_no_install_flag_used — the first three were already passing (their #[ignore] reasons were stale), the last is fixed by the test_case.rs change above. Updates the #[ignore] reasons on installer_test.rs's three fixture-driven integration tests to reflect their current state: the install pipeline now runs end-to-end, but the ~189-fixture installer/ set still hits several independent, unrelated bugs/gaps that need case-by-case triage rather than a single fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19fix(show-command): un-ignore 10 tests by fixing show-warnings typing and ↵nsfisis
repo bugs Replace the PhpMixed-based `$showWarnings` hack in VersionSelector:: findBestCandidate with a typed ShowWarnings enum (Always / Predicate), letting ShowCommand::findLatestPackage pass its real closure instead of hardcoding `true`. Fix the --no-dev branch in ShowCommand::execute, which built `repos` from an empty package list instead of sharing the same InstalledRepository as `installed_repo`. Pass repository handles instead of pre-borrowed `&dyn RepositoryInterface` refs into get_package/ generate_package_tree/add_tree to stop a RefCell double-borrow panic on --all/--locked. Add the missing CompletePackage/RootPackage set_release_date setter so the outdated sorting-by-age test can set fixture dates. Resolve OutputFormatterStyleStack::pop's empty-style todo!() via clone_box(), and fix FileDownloader's cache-GC log call to pass the VERY_VERBOSE verbosity PHP uses. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19fix(search-command): un-ignore test_search by fixing JSON output and ↵nsfisis
abandoned propagation The json format branch ignored search results entirely and always wrote null. Encode results into the same name/description/abandoned/url shape Composer's array-backed repositories produce. Also fix ComposerRepository's RepositoryInterface::search adapter, which dropped the abandoned field from raw API results even when present.
2026-07-19fix(update-command): un-ignore test_update by fixing 3 real bugsnsfisis
test_update was skipped for a stale reason; running it uncovered three distinct bugs it was actually catching: - ApplicationTester::run never restored SHELL_VERBOSITY after Application::configureIO mutates it, so one dataset's -vv verbosity leaked into later runs sharing the process (Symfony's tester restores it in a finally block; the port dropped that). - Installer::do_install built its RepositorySet with a hardcoded empty temporary_constraints map instead of self.temporary_constraints, so --with never actually constrained the resolver. - BumpCommand was missing a <warning> tag pair around one of its output lines.
2026-07-19fix(test-harness): set COMPOSER_TESTS_ARE_RUNNING so interactive tests ↵nsfisis
actually run Without this, Application::do_run force-disabled interactivity whenever stdin wasn't a tty (as under cargo test), so ApplicationTester runs with set_inputs silently produced non-interactive default output instead of consuming the answers, masking real behavior as several stale #[ignore]s blaming already-implemented ProcessExecutor/Process todo!()s. Port composer/tests/bootstrap.php's env setup into a bootstrap() helper called from get_application_tester(), un-ignore the now-passing init/update command tests, and update init_command_test's expected schema-validation wording to match the jsonschema crate (already the accepted wording per 541a8b4f, not an unported gap).
2026-07-19fix(check-platform-reqs): restore raw Link column in text tablensfisis
PHP's printTable builds a 5-column row for text output, with the raw Link object (cast via __toString) as its own column separate from the formatted description string. The port had collapsed both into one column, dropping an empty column for successful checks and throwing off the rendered column widths/spacing versus real Composer output.
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-16test: update TODO reason for unported test casesnsfisis
No test logic changes.
2026-07-11chore: use fully-qualified name for Rc/RefCellnsfisis