Initial Commit
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
## 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
|
||||
Reference in New Issue
Block a user