## ADDED Requirements ### Requirement: Verdaccio image and pinning `@suaveplan/testing-integration-verdaccio` SHALL ship a `VerdaccioFixture` ServiceFixture backed by `verdaccio/verdaccio:6@sha256:`. The image MUST be pinned by both tag and sha256 digest and MUST be tracked by `scripts/verify-image-pinning.ts`. #### Scenario: Image is pinned - **WHEN** the fixture's image config is read - **THEN** the value matches `^verdaccio/verdaccio:6@sha256:[a-f0-9]{64}$` ### Requirement: Environment-variable bridge The fixture SHALL expose its endpoint and credentials to the test environment via the variables `NPM_REGISTRY_URL` and `NPM_TOKEN`. `NPM_REGISTRY_URL` MUST be the fully-qualified `http://:/` form. `NPM_TOKEN` MUST be a Verdaccio-compatible bearer token generated at fixture startup. #### Scenario: Env-bridge populated after start - **WHEN** `VerdaccioFixture.setup()` resolves - **THEN** `process.env.NPM_REGISTRY_URL` is a parseable URL with `protocol === 'http:'` AND `process.env.NPM_TOKEN` has length ≥ 32 ### Requirement: Healthcheck via /-/ping Readiness SHALL be determined by a polling HTTP `GET /-/ping` against the fixture's endpoint. The poller MUST respect `HealthPoller` defaults from `@suaveplan/testing-integration` and SHALL accept an injectable `fetch` implementation for unit tests. #### Scenario: Ping returns 200 → ready - **WHEN** `GET /-/ping` returns HTTP 200 - **THEN** `HealthPoller` resolves and the fixture is considered ready #### Scenario: Ping never succeeds → timeout - **WHEN** `/-/ping` never returns 200 within the configured `healthTimeoutMs` - **THEN** `setup()` rejects with `INTEGRATION.VERDACCIO.HEALTH_TIMEOUT` ### Requirement: Configuration shape The fixture SHALL accept a config object of shape `{ storage?: 'memory'|'tmpfs'; users?: Array<{ username: string; password: string }>; packages?: Array<{ name: string; access?: string; publish?: string }>; uplinks?: Record }`. `storage` defaults to `'memory'`. The fixture SHALL translate this object into Verdaccio's native YAML config at startup. Invalid config MUST be rejected via Zod with a `INTEGRATION.VERDACCIO.CONFIG_INVALID` error. #### Scenario: Memory storage default - **WHEN** the fixture is started without a `storage` field - **THEN** Verdaccio runs with in-memory storage AND no host filesystem path is mounted #### Scenario: tmpfs storage - **WHEN** the fixture is started with `storage: 'tmpfs'` - **THEN** the container's storage path is backed by a tmpfs mount AND publish state survives within the fixture lifetime but not across restarts #### Scenario: User and package config translated - **WHEN** `users: [{ username: 'ci', password: 'p' }]` and `packages: [{ name: '@suaveplan/*', access: '$authenticated', publish: 'ci' }]` are supplied - **THEN** the rendered Verdaccio YAML contains an htpasswd entry for `ci` AND a `packages` block matching the input ### Requirement: Error code prefix Every error thrown by `@suaveplan/testing-integration-verdaccio` SHALL extend the package's structured error class and carry a `code` of the form `INTEGRATION.VERDACCIO.` (uppercase ASCII, dots as separators). #### Scenario: Coded error on health timeout - **WHEN** the fixture's healthcheck never succeeds - **THEN** the thrown error has `code === 'INTEGRATION.VERDACCIO.HEALTH_TIMEOUT'` #### Scenario: Coded error on invalid config - **WHEN** an invalid `users` array is supplied - **THEN** the thrown error has `code === 'INTEGRATION.VERDACCIO.CONFIG_INVALID'` AND `cause` is the underlying `ZodError` ### Requirement: Publish flow integration test The fixture SHALL be exercisable end-to-end against `bun scripts/publish.ts --package @suaveplan/` so the genesis publish path is testable in CI before any real publish to the production Verdaccio at `https://npmjs.fq.io/`. A reference integration test SHALL ship in the package demonstrating: fixture startup, env-bridge wiring, `bun scripts/publish.ts` invocation against the fixture, and verification of the published tarball via `npm view --registry $NPM_REGISTRY_URL`. #### Scenario: Publish round-trip - **WHEN** the integration test runs `bun scripts/publish.ts --package ` against the fixture - **THEN** `npm view @0.0.0 --registry $NPM_REGISTRY_URL` returns the package metadata AND the tarball digest matches the locally built artefact