test(npm): merge edge cases and property test (S31 Step 13, part 3/3)
Stacked MRs (review/merge bottom-up)
- feat(npm): dist-tags merge, pass-1 validation (... (!2931 - merged) • David Fernandez • 19.5
- feat(npm): packument-merge core (S31 Step 13, p... (!2932 - merged) • David Fernandez • 19.5
- test(npm): merge edge cases and property test (... (!2933 - merged) • David Fernandez • 19.5
👈
Split into 3 stacked MRs to keep each within the review size limit (≈800 ideal). Each part targets the previous (part 1 → main), reviewed and merged bottom-up.
Part 3 of 3 of docs/plans/2026-08-21-s31-npm-virtual.md — Step 13: metadata merge and serve surface
Continues parts 1-2 of this stack — see part 1 for the full step scope and the parts this step's own plan text is split into. This part is test-only: it adds no new production code.
This part's scope: the remaining edge-case, fatal-vs-omission, and integrity-divergence tests over parts 1-2's merger, plus a property-based test proving the lowest-position-wins invariant holds under a generated arrival order and contributor count.
Spec coverage (docs/specs/S31-npm-virtual.md, "Packument merge" acceptance criteria — this step's part covers the criteria that don't depend on the collection/fan-out layer or the HTTP serve surface, both later, separate steps):
| Criterion | Test |
|---|---|
| Union of versions | TestMergePackuments_UnionOfVersions |
| Position-major ordering | TestMergePackuments_PositionMajorOrdering |
| Lowest position wins a version key, whole | TestMergePackuments_LowestPositionWinsVersionWhole, the property test |
dist-tags union, lowest position wins a tag |
TestMergePackuments_DistTagsUnion |
No latest is synthesized |
TestMergePackuments_NoLatestSynthesized |
| The two dist-tags surfaces agree | TestMergeDistTags_AgreesWithPackument |
time union, modified max, created min |
TestMergePackuments_TimeUnion, TestMergePackuments_TimeUnpublishedIsRelayedWhole |
_id/_rev, and the abbreviated variant's exact top-level shape |
TestMergePackuments_IDAndRev, TestMergePackuments_AbbreviatedTopLevelIsExactlyThreeFields, TestMergePackuments_AbbreviatedIgnoresMalformedTime |
| Integrity relayed per contributing position | TestMergePackuments_IntegrityRelayedPerPosition |
| Divergence is counted | TestMergePackuments_IntegrityDivergence |
| A malformed input cannot fail the read | TestMergePackuments_Pass1Omissions, TestMergePackuments_Pass1RejectsNonObjectShapes, TestMergePackuments_Pass1RejectsTrailingContent, TestMergePackuments_Pass1RejectsNonStringTimeValue, TestMergePackuments_Pass1RejectsNonStringDistTagValue, TestMergeDistTags_EmptyDocumentIsMalformedNotFatal (part 1), TestMergeDistTags_NonStringTagValueIsMalformedNotCoerced (part 1) |
| Determinism | TestMergePackuments_Determinism, the property test's rerun check |
Not covered by this step (owned by later, separate steps): re-pointing every dist.tarball (Part 3 of the plan's own Step 13 text — HTTP serve surface), the outage/unhealthy/denied/freshness/404/503 collection-mode criteria and bounded fan-out (Part 2 of the plan's own Step 13 text, currently blocked), Content-Length omission (HTTP serve surface).
Spec: https://gitlab.com/gitlab-org/ops/artifact-registry/-/blob/main/docs/specs/S31-npm-virtual.md, "Cross-upstream packument merge" and its acceptance criteria. Plan: https://gitlab.com/gitlab-org/ops/artifact-registry/-/blob/main/docs/plans/2026-08-21-s31-npm-virtual.md, Step 13.
Related to #894 (closed)