aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart/tests
AgeCommit message (Collapse)Author
2026-05-02feat(installer): add trace recorder and topo install ordernsfisis
Adds TraceRecorderExecutor (Composer's InstallationManagerMock analog), which records every install/update/uninstall as a string matching Composer's *Operation::__toString output (after strip_tags) - the load-bearing assertion target for in-process fixture tests. Two changes were needed to make the recorder useful: - InstallerExecutor::uninstall_package gains a version parameter, and install_from_lock now looks up both the uninstall and the Update-from-version from installed.json. Previously the Update path passed the new version as a placeholder; the recorder needs the real old version to emit `Upgrading pkg (old => new)`. - compute_operations now topologically sorts the lock contents (deps before dependents) before computing actions, mirroring Composer's Transaction::calculateOperations. Without this, packages would install in alphabetical order and the trace would diverge from Composer's expectation. Also adds crates/mozart/tests/installer_in_process.rs with the in-process harness scaffold: parses the same .test fixtures, builds a tempdir, calls commands::install::run / update::run with an empty RepositorySet (no Packagist) and a TraceRecorderExecutor, then asserts exit code + EXPECT trace. One fixture wired up: suggest_replaced - the original CI failure that motivated this whole DI refactor. It now passes on the in-process path because the empty RepositorySet makes b/b unreachable just like Composer's `'packagist' => false` test config, and the resolver finds c/c (which replaces b/b) via the inline package repo's eager preload. Step F will migrate every fixture currently in installer.rs to the new harness; remaining divergences (alias handling, output ordering, replace trace shape, etc.) will surface as individual follow-ups. All 136 existing spawn-based fixtures + 114 mozart-registry tests + 541 mozart lib tests still green; clippy clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02refactor(registry): introduce Repository and InstallerExecutor traitsnsfisis
Sets up DI scaffolding for in-process installer E2E tests, mirroring how Composer's PHPUnit suite swaps Packagist (FactoryMock) and the install manager (InstallationManagerMock) without touching the network or filesystem. Additions: - Repository trait + RepositorySet (Composer's RepositoryInterface analog), with PackagistRepository, InlinePackageRepository, VcsRepository impls. - InstallerExecutor trait (Composer's InstallationManager analog) with FilesystemExecutor extracted from install_from_lock. install_from_lock now delegates per-package install/uninstall verbs to FilesystemExecutor; console output orchestration stays in the caller so existing --EXPECT-OUTPUT-shape assertions remain comparable. No behavior change - all 136 enabled installer fixtures still pass. Also tightens the installer_fixture\! ignore form to a single token (installer_fixture\!(name, ignore)) for readability. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02feat(resolver): real constraint intersection and replace-aware same-name ↵nsfisis
conflicts `Pool::what_provides` previously accepted any provide/replace candidate because `constraints_intersect` returned true unconditionally; the same-name conflict pass also looked only at canonical names, so a package replacing another at v1.0.0 was treated as a valid provider for a v2.0.0 require and could coexist with the replaced package. Implement interval-based `VersionConstraint::intersects` and index packages by their `replace` targets (matching Composer's `getNames(false)`) when generating same-name conflict rules. Greens 3 installer fixtures: conflict_against_replaced_by_dep_package_problem, provider_conflicts3, replaced_packages_should_not_be_installed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02fix(update): normalize locked version to 4-segment form on partial pinnsfisis
`apply_partial_update` and `apply_patch_only` both pinned non-listed packages back to the lock by copying `LockedPackage.version_normalized` verbatim, falling back to the raw pretty `version` when the field was missing. Lock files written by Composer always include the field, but hand-written fixtures (every `--LOCK--` block in the installer fixtures, in particular) typically only carry `version`. The 3-segment form ("1.0.0") then leaked into the resolved package, where `LockFileGenerationRequest::inline_lookup` compares against the 4-segment normalizer output ("1.0.0.0") and missed inline `type: package` entries — triggering a Packagist fetch (and proxy-blocked failure under the test harness) for a package that should never need one. Extract a single `locked_version_normalized` helper that runs the pretty version through `mozart_semver::Version::parse(...).to_string()` when the lock omits `version_normalized`, and use it from both call sites. Mirrors `packagist_to_pool_inputs` and `inline_lookup`, which already produce the 4-segment form. Unblocks 26 installer fixtures: the entire update-allow-list cluster (16, minus the alias subcase), five partial-update cases, and five others (full-update-minimal-changes, load-replaced-package-if-replacer-dropped, remove-deletes-unused-deps, remove-does-nothing-if-removal-requires-update-of-dep, update-changes-url). Scoreboard: 107 → 133 of 187 installer fixtures. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02feat(resolver): honor --ignore-platform-reqs and wildcard patternsnsfisis
The pool builder and rule-set generator only consulted an exact-match HashSet, so `--ignore-platform-reqs` (no value) and `--ignore-platform-req=ext-foo-*` fell through to the SAT layer and produced "no matching package found" for transitive platform deps. Track the bool flag separately and run each platform name through `mozart_core::matches_wildcard` against the configured patterns. Unblocks four installer fixtures: install-{ignore-platform-package-requirement-wildcard,ignore-platform-package-requirements} update-{ignore-platform-package-requirement-wildcard,ignore-platform-package-requirements}. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01feat(registry): support inline 'type: package' repositoriesnsfisis
Composer's PackageRepository lets composer.json embed full package metadata under repositories[].package, mirroring the on-disk Packagist response shape. The vast majority of installer fixtures under composer/tests/Composer/Test/Fixtures/installer (179 of 189) rely on this — they declare every package they need inline rather than hitting the network. Three pieces wire this into Mozart: 1. mozart-core::package::RawRepository: relax `url` to Option<String> (Composer enforces presence per repo type, not at JSON parse) and add `package: Option<Value>` to receive the inline definition, which can be a single object or an array. 2. mozart-registry::inline_package: a new module that walks `&[RawRepository]`, picks out type=package entries, and reshapes each `package` payload into a PackagistVersion (auto-computing version_normalized when omitted, matching Packagist's output). 3. resolver::resolve and lockfile::generate_lock_file: feed inline packages into the SAT pool builder and short-circuit the Packagist fetch when generating the lock entry for a resolved inline package. The package-name set is shared with the existing VCS-skip logic so the seed and transitive loops don't double-fetch. One additional install-time change: in install_from_lock, packages that have neither dist nor source are now skipped silently instead of bailing with "no dist or source information". This mirrors Composer's MetapackageInstaller (no installer for type=metapackage) and is also what Composer's own AllFunctionalTest exercises via InstallationManagerMock — most inline-package fixtures define synthetic packages with no download metadata, expecting the install operation to be recorded but not actually run. Net effect: installer fixture scoreboard jumps from 7/187 to 103/187. The 84 fixtures still ignored hit issues unrelated to inline-package plumbing — aliases, replace/provide chains, dev-reference handling, allow-list updates, etc. — and are tracked separately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01fix(install): preserve LockedPackage extra_fields in installed.jsonnsfisis
Composer's InstalledFilesystemRepository::write() dumps the full package via ArrayDumper, so flags like `abandoned` and `default-branch` (which Mozart parks in LockedPackage::extra_fields) should round-trip from composer.lock into vendor/composer/installed.json. locked_to_installed_entry was zeroing the destination's extra_fields, silently stripping these flags every time installed.json got rewritten. Carry the extra_fields map across verbatim. The install-forces-reinstall-if-abandon-changes installer fixture is already exit-0 green at the harness layer; this aligns the actual end-state with Composer's EXPECT-INSTALLED so a future EXPECT-INSTALLED comparison won't re-flag this gap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01fix(registry): accept composer.lock without content-hashnsfisis
Composer's `Locker` treats `content-hash` as optional with BC support (see Locker::isLocked() / isFresh() lines 142-147): if a lock predates the field — or, in the case of installer fixtures, deliberately omits it — Composer simply considers the lock "not fresh" against any composer.json. Mozart's deserializer was strict, rejecting the lock with `missing field content-hash` before any of the install-time checks could run. Default the field to empty via `#[serde(default)]`. With an empty hash, `is_fresh()` returns false (matching Composer's BC behavior, so the freshness warning still fires) and downstream code that overwrites `content_hash` continues to work unchanged. Closes the parsing barrier exercised by the updating-dev-from-lock-removes-old-deps installer fixture. Note: matching Composer's exact operations trace ("Upgrading a/devpackage …", alias-removal lines) requires a `compute_operations` that compares package source references — out of scope for this change and tracked in .ken/test_design.md §7.2 under "EXPECT (operations trace) 比較". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01feat(core): reject root composer.json that requires its own namensfisis
Mirrors Composer\Package\Loader\RootPackageLoader::load(): if the root package's "name" appears as a key in its own "require" or "require-dev" map, fail loudly before reaching the resolver. Without this, Mozart would silently let the request hit Packagist (which has no entry for the root's vendor/name) and report a misleading "could not be found" error. Wired into install::execute (when a lock file is present) and update::execute (the no-lock fallback path). Carries the same wording as Composer's RuntimeException so a future EXPECT-OUTPUT comparison will match. Also extends the installer test harness: when a fixture sets EXPECT-EXCEPTION but no EXPECT-EXIT-CODE, assert that Mozart exits non-zero. Full exception-class matching remains a follow-up (see .ken/test_design.md §7.2). Closes the gap exercised by the install-self-from-root installer fixture. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01feat(install): verify platform requirements before install_from_locknsfisis
Mirrors Composer's platform-requirements check during Installer::doInstall(): merge platform requires from the lock's platform/platform-dev fields with the root composer.json require/require-dev (composer.json wins on duplicate keys), then verify them against the detected runtime platform. If any are missing or unsatisfied, print the standard "Your lock file does not contain a compatible set of packages" message followed by Problem 1..N entries and exit with DEPENDENCY_RESOLUTION_FAILED (2) instead of silently proceeding to "Nothing to install" with exit 0. Closes the gap exercised by the outdated-lock-file-with-new-platform-reqs-fails installer fixture. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01feat(install): verify lock file satisfies composer.json requiresnsfisis
Mirrors Composer's Installer::doInstall() check: before installing from an existing composer.lock, walk every root require (and require-dev in dev mode) and confirm the lock contains a satisfying package. If any are missing or fail the constraint, print the standard bullet-list diagnostic and exit with LOCK_FILE_INVALID (4) instead of blindly attempting to install and failing later with a misleading "no dist or source information" error. Closes the gap exercised by the outdated-lock-file-fails-install installer fixture. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01feat(registry): accept v1 (bare array) installed.jsonnsfisis
Composer's FilesystemRepository::initialize branches on isset($data['packages']) — object form is v2, bare array is v1 — and treats dev-package-names/dev as optional. Mirror that in InstalledPackages::read so Mozart consumes shared .test fixtures (which use v1) without harness preprocessing, and so installs over v1-era vendor directories keep working. Drop the v1→v2 wrapper that was added to mozart-test-harness for the same reason. Removes #[ignore] from update_to_empty_from_locked (2/187 green). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01fix(core): default missing composer.json "name" to __root__nsfisis
Composer's RootPackageLoader assigns the root name "__root__" when composer.json omits the "name" field. Mozart was failing deserialization in that case, blocking any installer fixture with a nameless root manifest. Apply the same serde default and unignore update-to-empty-from-blank as the first green entry on the .test scoreboard.
2026-05-01test(test-harness): enumerate Composer installer .test fixturesnsfisis
Add per-fixture #[test] entries (187 total) via a small declarative macro that reads files directly from the composer submodule. For now each test only asserts the file parses; execution and EXPECT-* checks will be layered on as the harness gains comparison helpers.
2026-02-22fix(test): correct install command expectation when no lock file presentnsfisis
The install command succeeds with a warning when no lock file exists, rather than failing with a suggestion to run update.
2026-02-21test(cli): add end-to-end integration tests for CLI commandsnsfisis
Add 23 integration tests using assert_cmd and predicates covering about, validate, show, licenses, install, config, init, and dump-autoload commands with shared test helpers and fixture projects. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>