test(e2e): npm, pnpm and yarn clients for the harness, and the 27 catalog rows they cover

What this adds

An npm client for the e2e harness, a pnpm and a yarn client beside it, and the 27 npm catalog rows they make assertable. The catalog had 56 automatable non-UI rows and no way to write any of them: e2e/internal/client held oras and oci only, and the pattern registry named no npm scenario.

Group Rows covered Left in the group
Setup and configuration 2 of 2
Authentication 3 of 3 ci-job-token (blocked on job-scoped credentials)
Publish 8 of 8
Discover 2 of 2
Consume 6 of 6
Lifecycle 6 of 8 2 counter rows, both blocked (below)

e2e-catalog check reports 0 disagreements; covered goes 5 → 32 across all catalogs, and the npm catalog's uncovered count falls by 27.

The client

e2e/internal/client/npm writes its own confinement rather than reusing client/oci's. npm resolves its user config and cache from npm_config_userconfig and npm_config_cache, reads nothing from DOCKER_CONFIG, and keeps its credential in a file the client writes rather than a header it passes. Its runner splits stdout from stderr, because this output is parsed and npm warns on stderr.

Versions asserts node against the .tool-versions pin and only reports npm's own: npm ships inside Node and nothing in this repository declares a version for it, so there is no pin to compare against. nodejs joins install-tools.sh's MISE_TOOLS, which had omitted it, and pnpm and yarn join it with pins of their own.

The two sibling clients, measured rather than assumed

Both were driven against a stub registry before a line of the tests was written.

pnpm reads the same .npmrc through npm_config_userconfig and takes no different flags for publish, so client/pnpm holds its pin and hands back the npm client set to invoke it.

yarn 4 needs its own package. It reads .yarnrc.yml and ignores .npmrc, resolves its cache and global folder from YARN_* variables, spells the publish yarn npm publish, and needs three commands rather than one: a directory with no lockfile is read as part of an enclosing project, and yarn npm publish refuses a workspace the lockfile does not resolve. It also rejects a plaintext registry outright (YN0081), so Configure writes unsafeHttpWhitelist with the registry host — only when the scheme is http, so an https rig carries no opt-in it never required. That carve-out has its own entry in the upstream-limitations index.

Both rows publish with the sibling and install with npm, comparing against the payload npm's own publish writes. Installing with the same tool would not show that the two agree, which is what "the same installable result as the npm CLI" claims.

Two rows this stops short of, both verified

lifecycle.unpublish-package-counters is blocked, not skipped. Both unpublish shapes it describes end with the npm_packages row soft-deleted — a whole-package unpublish marks it, and unpublishing a package's last active version cascades to it via cascadeEmptyPackageTx, which ADR 007 states and #988 records as missing from S11's text. ListNpmPackages and FindNpmPackageInRepository both filter soft_deleted_at IS NULL, so the tags_count reset the unpublish writes lands on a row no management-API read serves. The section intro names the missing capability.

discover.metadata-after-rebuild is now manual, with both reasons in the row. A rebuild leaves no signal a client can read — streamDocument frames the same ETag, Cache-Control, Content-Type and Content-Length whether it serves a pre-built cache row or builds inline — and its multi-page half needs 1001 versions in one package, since packumentReadPageSize is a Go constant no rig setting lowers.

lifecycle.management-delete-storage-counters stays uncovered and the section intro says why: two of its clauses are about a remote repository's deletes, and e2e/internal/registry cannot yet create a repository of kind: remote. It travels with the ## Remote repositories group, which needs the same capability.

Catalog corrections

The remote section claimed a remote repository "cannot be created through the management API yet, so the test environment seeds the npm_remote_repositories binding out of band". It can: api/openapi/v1.yaml documents a create taking kind: remote with a settings.url, and internal/datastore/repositories.go dispatches it to CreateNpmRemoteRepository.

Four remote cells paired an HTTP-observable outcome with a claim about rows and blobs nothing outside the database can see. Those clauses moved into the section intro as explanation, pointing at S15, so each cell states only what a client or the management API observes. ## Scope now records that the suite reads no database row and no object storage, and names the two delivery-mode-override rows that leaves uncovered.

Rig configuration, and what it hides

Three settings land in both test/caproni/values/ files, following the iam.cache_ttl: "0s" precedent already there:

  • npm.public_registry_url — unset, the registry renders every packument's dist.tarball root-relative, which npm resolves but no deployment serves.
  • npm.max_tarball_size: "512KB" and npm.max_versions_per_package: 5 — lowered from ADR-004's 5 GB and 25000, neither reachable in CI.

ADR-004 governs the shipped defaults, which are unchanged; these are rig settings. e2e/README.md's upstream-limitations index records what the lowered caps hide: the suite proves each cap is enforced, never that it is enforced at the value production ships. The index also gains npm's client-side duplicate-publish refusal, which is why republish-same-version covers both a CLI arm and a raw PUT.

Reviewable size

Past the 500 LOC guideline, and not split. By file group:

Group LOC Why it travels here
e2e/internal/client/{npm,pnpm,yarn} (7 files) ~1200 Each capability plus its unit tests; the no-untested-code guardrail keeps them together
e2e/bootstrap/npm (7 files) ~1150 One file per catalog group, which is how a reviewer reads one coherent set
patterns.go, harness wiring ~250 One registration per row, each naming its exemplar
Catalog, README, rig values, tool pins ~520 The Status flips and corrections the rows above require

Splitting the client from its scenarios would land a client no row uses; splitting the groups would land pattern registrations without their tests. The 13 commits are individually reviewable and each states what it asserts and why.

Not verified against a rig

Nothing here has run against a live caproni cluster. Every test(...) commit says so. The riskiest constructions are PublishEnvelope's key order and the coherence-mismatch construction, modeled on the envelope builder in internal/format/npm's publish pre-check integration tests. Its required key set is settled, though: streampub/walker.go:134 and :138 name name, versions and _attachments as the only three, so an absent dist-tags cannot divert a raw PUT to a missing-field error. The pnpm and yarn client behaviours were measured locally against a stub registry, including that yarn handles a path-bearing registry URL.

Merge order

Third in a stack. !2052 (mainsahmed/e2e-caproni-authz) merges first, then !2070 (merged) (sahmed/e2e-caproni-authzsahmed/e2e-oci-setup-scenarios), then this one. It targets sahmed/e2e-oci-setup-scenarios because it depends on that branch's registry.Client.DeleteRepository and its oci setup exemplar, which the two npm setup rows and their pattern registrations both cite.

This is test and documentation work with no behavior change, so no issue link is required per the conventions' maintenance exemption.

Edited by Suleimi Ahmed

Merge request reports

Loading
Loading