test(npm): dist-tags integration suite (S11 Step 10, MR 5/5)

📦 What

S11 Step 10 (dist-tags GET endpoint) shipped as a five-MR stack, in dependency order:

  • !622 (merged) - dist-tags handler primitives + in-package unit tests.
  • !623 (merged) - inline-build handler (resolve -> 404 -> build {tag:version} -> serve).
  • !624 (merged) - kind=1 npm_metadata_files cache fast-path + conditional-GET 304-without-blob-read.
  • !625 (merged) - inline-build singleflight (collapse concurrent cache misses).
  • !626 (merged) - integration suite (real datastore finders + stub BlobStore). <- this MR

All four parents (!622 (merged)-!625 (merged)) have merged; this MR now targets main. As the final slice it also closes out Step 10 in the npm-hosted plan (the Step -> MR mapping table).

Slice 5 - the integration suite driving npm.DistTagsHandler against the real datastore read finders (NpmPackageStore, NpmMetadataFileStore, NpmTagStore, NpmVersionStore, RepositoryStore) over testcontainers PostgreSQL, plus an in-memory stub BlobStore seeded with the cache-blob bytes. The handler is constructed directly with the two-id Resolution injected into context (the route is on the 501 placeholder; see below).

  • AC 34 - a missing npm_packages row and a soft-deleted one both return 404 package_not_found.
  • Cache-served - a fresh kind=1 npm_metadata_files row serves the blob with the strong ETag and public Cache-Control.
  • Inline-build - a cache miss with tags renders {tag:version}; a version with no tag row renders {} (no synthesized latest); a private repo serves private, max-age=0; an expired kind=1 row falls through to the inline build (the stale blob is bypassed, not served).
  • A content-addressed fixture distinctness guard pins that distinct dist-tags bodies seed distinct cache digests.

Spec coverage

Behaviour Test
AC 34 - missing and soft-deleted package -> 404 package_not_found TestDistTagsIntegration_PackageNotFound
Cache-served 200: strong ETag + public Cache-Control (real metadata-file finder) TestDistTagsIntegration_CacheServed
Inline-build {tag:version} and {} (no synthesized latest), private Cache-Control, expired-row fall-through TestDistTagsIntegration_InlineBuild
Content-addressed cache fixtures stay distinct TestDistTagsIntegration_DistinctCacheDigests
AC 58 - read-endpoint auth (401/403/404-on-private) S08-owned: auth middleware runs before npm code; the handler has no auth surface. Not exercised here.

⚠️ Known gap - handler not yet wired into the route table

DistTagsHandler is implemented and tested, but nothing mounts it: internal/format/npm/handler.go still serves the 501 Not Implemented placeholder for the dist-tags route, so the integration tests construct the handler directly with an injected Resolution rather than routing through the mux. This is an unassigned plan-level gap (the npm hosted plan creates handler.go with placeholders in Step 8 and never assigns the placeholder->real-handler swap to a handler step); it surfaces no later than Step 23 (real npm-CLI conformance) and is closed via a plan amendment, not bolted onto this stack. Deliberately out of scope.

🧪 Testing

  • go test ./internal/format/npm/... - pass
  • golangci-lint run ./internal/format/npm/... - 0 issues
  • go vet -tags integration ./internal/format/npm/... - clean
  • Integration tests (//go:build integration) run in the merged-results pipeline.

Related to #122 (closed)

Edited by Dzmitry (Dima) Meshcharakou

Merge request reports

Loading
Loading