Files
2026-05-27 23:29:53 +01:00

4.3 KiB
Raw Permalink Blame History

ADDED Requirements

Requirement: Garage v2 image policy

The @suaveplan/testing-integration-garage-s3 plugin SHALL pin to dxflrs/garage:v2.0.0 (name + minor version). A follow-up operational change tracked at docs/superpowers/specs/2026-04-25-test-suite-audit.md §5 will pin to sha256:<digest>.

Scenario: Image tag pinned

  • WHEN the container factory is invoked without injection
  • THEN the resulting container uses image dxflrs/garage:v2.0.0

Requirement: Dual-port exposure

The fixture SHALL expose two TCP ports from the container: the S3 API port on container port 3900 and the admin port on container port 3902. Both MUST be mapped to host ports and recorded in config.s3Endpoint and config.adminEndpoint.

Scenario: Both endpoints recorded

  • WHEN the container has started
  • THEN config.s3Endpoint and config.adminEndpoint are populated with reachable host URLs

Requirement: Env-bridge contract

The plugin SHALL declare and forward exactly four environment variables: GARAGE_S3_ENDPOINT, GARAGE_S3_ACCESS_KEY_ID, GARAGE_S3_SECRET_ACCESS_KEY, GARAGE_S3_BUCKET. When ALL four are set on the parent process at construction time, the fixture MUST enter env-bridge passthrough mode.

Scenario: env() returns the full four-key set

  • WHEN the fixture has started
  • THEN fixture.env() returns a record with exactly the four declared keys

Requirement: Seven-step initialisation sequence

In container mode start() SHALL execute the following sequence against the admin port, in order, before declaring the fixture healthy:

  1. status — read cluster status to discover the node id
  2. layout assign — assign capacity to the discovered node
  3. layout apply — commit the layout version
  4. bucket create — create the configured bucket
  5. key create — create an access/secret key pair
  6. bucket allow — grant the key full permissions on the bucket
  7. key info — read back the key to confirm and capture the secret

Any step failure MUST abort the sequence and reject start() with a coded error naming the failing step.

Scenario: Successful seven-step init

  • WHEN start() is invoked against a fresh container
  • THEN all seven admin operations execute in the order above before start() resolves

Scenario: Layout-apply failure aborts

  • WHEN the third step (layout apply) fails
  • THEN start() rejects with code === "INTEGRATION.GARAGE_S3.LAYOUT_APPLY_FAILED" AND steps 4–7 are not attempted

Requirement: Healthcheck contract — admin port

isHealthy() SHALL probe the admin port (3902) with an HTTP request and resolve true only when the admin endpoint responds. The S3 port (3900) alone is NOT sufficient evidence of health.

Scenario: Admin port is the source of truth

  • WHEN the S3 port is open but the admin port has not yet started
  • THEN isHealthy() resolves false

Requirement: Configurable timeouts

The fixture options SHALL accept healthIntervalMs?: number and healthTimeoutMs?: number overrides forwarded to HealthPoller. Defaults follow HealthPoller's defaults (500 ms / 120 s).

Scenario: Override applied

  • WHEN the fixture is constructed with { healthTimeoutMs: 30000 }
  • THEN HealthPoller rejects after 30 s when the container never becomes healthy

Requirement: Injectable factories

The fixture constructor SHALL accept optional containerFactory, execFn, and fetchFn parameters for the container, admin-CLI exec, and admin-port HTTP calls respectively. Defaults invoke testcontainers, the container's exec API, and the global fetch.

Scenario: Injected execFn drives the seven-step sequence

  • WHEN execFn is replaced with a stub that records every command
  • THEN the recorded commands match the seven-step order

Requirement: Error-code prefix

Errors thrown from the plugin SHALL carry codes matching ^INTEGRATION\.GARAGE_S3\.[A-Z_]+$. At minimum the package SHALL define: START_FAILED, STOP_FAILED, STATUS_FAILED, LAYOUT_ASSIGN_FAILED, LAYOUT_APPLY_FAILED, BUCKET_CREATE_FAILED, KEY_CREATE_FAILED, BUCKET_ALLOW_FAILED, KEY_INFO_FAILED.

Scenario: Each step has its own coded error

  • WHEN bucket create fails
  • THEN the rejection's code === "INTEGRATION.GARAGE_S3.BUCKET_CREATE_FAILED" AND context.step === "bucket-create"