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

6.0 KiB

ADDED Requirements

Requirement: Layer 0 dep-graph isolation

The @suaveplan/testing-integration-minio package's dependencies MUST be exactly @suaveplan/testing-integration (workspace:^) and testcontainers (catalog:integration). No other @suaveplan/* runtime dep is permitted. devDependencies MAY include @suaveplan/testing for the package's own tests.

Scenario: Runtime closure clean

  • WHEN bunx turbo ls --filter=@suaveplan/testing-integration-minio... is run
  • THEN the only @suaveplan/* package in the runtime closure is @suaveplan/testing-integration

Requirement: Coexistence with Garage S3 fixture

The MinIO fixture MUST exist alongside @suaveplan/testing-integration-garage-s3 rather than replacing it. The two fixtures expose deliberately different surfaces: Garage masks several AWS-S3 quirks (admin-port health, layout-assignment init), while MinIO mirrors AWS-S3 surface much more closely. Tests that need AWS-API parity MUST target the MinIO fixture; tests covering the Garage init dance MUST target the Garage fixture.

Scenario: Both fixtures coexist

  • WHEN a test environment registers both MinioFixture and GarageS3Fixture
  • THEN both fixtures start in parallel with no shared port or env-var conflicts

Requirement: Image-pinning policy

The default image MUST be a pinned minio/minio:RELEASE.<date> tag (latest stable RELEASE at implementation time). The constants/index.ts file MUST carry a # TODO: replace with sha256:<digest> comment until the digest is recorded.

Scenario: Pinned RELEASE tag

  • WHEN the constants file is read
  • THEN the image string starts with minio/minio:RELEASE. AND a sha256 TODO line is present

Requirement: Env-bridge passthrough

The fixture MUST consult MINIO_S3_ENDPOINT, MINIO_S3_ACCESS_KEY_ID, MINIO_S3_SECRET_ACCESS_KEY, MINIO_S3_REGION, and MINIO_S3_BUCKET. When the first four are present and non-empty, start() MUST skip container creation and return a handle whose config points at the live MinIO. MINIO_S3_BUCKET is optional in passthrough mode.

Scenario: Live MinIO present

  • WHEN the four required env vars are set
  • THEN start() returns without invoking containerFactory AND the returned config's endpoint equals MINIO_S3_ENDPOINT

Scenario: Partial env triggers container

  • WHEN MINIO_S3_ENDPOINT is set but MINIO_S3_SECRET_ACCESS_KEY is empty
  • THEN start() falls back to container mode

Requirement: Healthcheck

The fixture MUST poll HTTP GET <endpoint>/minio/health/live and treat a 200 response as ready. The default healthTimeoutMs MUST be 60000. Callers MAY override.

Scenario: Healthcheck succeeds

  • WHEN /minio/health/live returns 200
  • THEN start() resolves

Scenario: Healthcheck times out

  • WHEN the endpoint never returns 200 within healthTimeoutMs
  • THEN start() rejects with code INTEGRATION.MINIO.HEALTHCHECK_TIMEOUT

Requirement: Random keys per run

In container mode, when accessKeyId/secretAccessKey are not supplied, the fixture MUST generate cryptographically random keys for each run (≥ 20 chars, alphanumeric) and pass them as MINIO_ROOT_USER / MINIO_ROOT_PASSWORD env vars to the container.

Scenario: Random keys

  • WHEN the fixture starts twice without explicit keys
  • THEN the two runs use different accessKeyId values

Requirement: Bucket pre-seeding

When bucket is supplied, the fixture MUST create the bucket via the MinIO admin API after the healthcheck passes, using the injectable fetchFn. The fixture MUST NOT resolve start() until the bucket exists (verified via HEAD <endpoint>/<bucket> returning 200).

Scenario: Bucket created

  • WHEN start({ bucket: 'app-uploads' }) resolves
  • THEN HEAD /app-uploads returns 200 against the returned endpoint

Requirement: Config shape

The exported MinioConfig interface MUST include accessKeyId, secretAccessKey, region, optional bucket?: string, optional console?: boolean, and the resolved endpoint. A sibling minio-fixture.schemas.ts MUST export a Zod MinioConfigSchema that validates the same shape.

Scenario: Schema parity

  • WHEN MinioConfigSchema.safeParse runs against a MinioConfig value
  • THEN valid configs parse OK AND missing required fields fail with ZodError

Requirement: Error-code prefix

All errors thrown from the fixture MUST carry a code matching ^INTEGRATION\.MINIO\.[A-Z_]+$.

Scenario: Coded error

  • WHEN any fixture method throws
  • THEN code starts with INTEGRATION.MINIO.

Requirement: Injectable fakes

The fixture constructor MUST accept containerFactory?: () => GenericContainer and fetchFn?: typeof fetch. Unit tests MUST drive 100% Istanbul branch coverage without Docker or network.

Scenario: Fake injection

  • WHEN the fixture is constructed with both fakes
  • THEN real Docker / real fetch are never called during the test

Requirement: Plugin scenarios — AWS-S3 parity

The fixture MUST support, end-to-end against a real container with an AWS S3 SDK client (@aws-sdk/client-s3), the following flows in its smoke test:

Scenario: PutObject and GetObject round-trip

  • WHEN an SDK client puts an object then gets it back
  • THEN the retrieved body bytes equal the put body bytes AND the ETag header is present

Scenario: Presigned GET URL works

  • WHEN a presigned GET URL is created via @aws-sdk/s3-request-presigner and fetched directly with no SDK signing
  • THEN the response is 200 with the object body

Scenario: Multipart upload works

  • WHEN an object larger than the part-size threshold (≥ 5 MB) is uploaded via CreateMultipartUpload + UploadPart + CompleteMultipartUpload
  • THEN the assembled object's body equals the concatenated source AND HeadObject reports the full content length

Scenario: Bucket policy attach

  • WHEN a PutBucketPolicy request attaches a public-read policy
  • THEN GetBucketPolicy returns the same JSON document