Skip to content

Commit 0f5d51a

Browse files
committed
Add s3mock module
1 parent 8cd3c6a commit 0f5d51a

File tree

9 files changed

+208
-0
lines changed

9 files changed

+208
-0
lines changed

docs/modules/s3mock.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# S3Mock
2+
3+
## Install
4+
5+
```bash
6+
npm install @testcontainers/s3mock --save-dev
7+
```
8+
9+
## Examples
10+
11+
These examples use the following libraries:
12+
13+
- [@aws-sdk/client-s3](https://www.npmjs.com/package/@aws-sdk/client-s3)
14+
15+
npm install @aws-sdk/client-s3
16+
17+
Choose an image from the [container registry](https://hub.docker.com/r/adobe/s3mock) and substitute `IMAGE`.
18+
19+
### Create a S3 bucket
20+
21+
<!--codeinclude-->
22+
[](../../packages/modules/s3mock/src/s3mock-container.test.ts) inside_block:s3mockCreateS3Bucket
23+
<!--/codeinclude-->

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ nav:
8585
- RabbitMQ: modules/rabbitmq.md
8686
- Redis: modules/redis.md
8787
- Redpanda: modules/redpanda.md
88+
- S3Mock: modules/s3mock.md
8889
- ScyllaDB: modules/scylladb.md
8990
- Selenium: modules/selenium.md
9091
- ToxiProxy: modules/toxiproxy.md

packages/modules/s3mock/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
FROM adobe/s3mock:4.9.1
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "@testcontainers/s3mock",
3+
"version": "11.7.2",
4+
"license": "MIT",
5+
"keywords": [
6+
"s3mock",
7+
"aws",
8+
"testing",
9+
"docker",
10+
"testcontainers"
11+
],
12+
"description": "S3Mock module for Testcontainers",
13+
"homepage": "https://github.com/testcontainers/testcontainers-node#readme",
14+
"repository": {
15+
"type": "git",
16+
"url": "git+https://github.com/testcontainers/testcontainers-node.git"
17+
},
18+
"bugs": {
19+
"url": "https://github.com/testcontainers/testcontainers-node/issues"
20+
},
21+
"main": "build/index.js",
22+
"files": [
23+
"build"
24+
],
25+
"publishConfig": {
26+
"access": "public"
27+
},
28+
"scripts": {
29+
"prepack": "shx cp ../../../README.md . && shx cp ../../../LICENSE .",
30+
"build": "tsc --project tsconfig.build.json"
31+
},
32+
"dependencies": {
33+
"testcontainers": "^11.7.2"
34+
},
35+
"devDependencies": {
36+
"@aws-sdk/client-s3": "^3.913.0"
37+
}
38+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { S3MockContainer, StartedS3MockContainer } from "./s3mock-container";
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { CreateBucketCommand, HeadBucketCommand, S3Client } from "@aws-sdk/client-s3";
2+
import { getImage } from "../../../testcontainers/src/utils/test-helper";
3+
import { S3MockContainer } from "./s3mock-container";
4+
5+
const IMAGE = getImage(__dirname);
6+
7+
describe("S3MockContainer", { timeout: 180_000 }, () => {
8+
it("should create a S3 bucket", async () => {
9+
// s3mockCreateS3Bucket {
10+
await using container = await new S3MockContainer(IMAGE).start();
11+
12+
const client = new S3Client({
13+
endpoint: container.getHttpConnectionUrl(),
14+
forcePathStyle: true,
15+
region: "auto",
16+
credentials: {
17+
secretAccessKey: container.getSecretAccessKey(),
18+
accessKeyId: container.getAccessKeyId(),
19+
},
20+
});
21+
22+
const input = { Bucket: "testcontainers" };
23+
const command = new CreateBucketCommand(input);
24+
25+
expect((await client.send(command)).$metadata.httpStatusCode).toEqual(200);
26+
expect((await client.send(new HeadBucketCommand(input))).$metadata.httpStatusCode).toEqual(200);
27+
// }
28+
});
29+
});
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { AbstractStartedContainer, GenericContainer, type StartedTestContainer, Wait } from "testcontainers";
2+
3+
const S3_HTTP_PORT = 9090;
4+
const S3_HTTPS_PORT = 3903;
5+
// https://github.com/adobe/S3Mock/issues/1250#issuecomment-1653387576
6+
const ACCESS_KEY_ID = "any-access-key-id";
7+
const SECRET_ACCESS_KEY = "any-secret-access-key";
8+
const REGION = "auto";
9+
10+
export class S3MockContainer extends GenericContainer {
11+
#accessKeyId: string = ACCESS_KEY_ID;
12+
#secretAccessKey: string = SECRET_ACCESS_KEY;
13+
14+
constructor(image: string) {
15+
super(image);
16+
this.withExposedPorts(S3_HTTP_PORT, S3_HTTPS_PORT);
17+
this.withEnvironment({
18+
COM_ADOBE_TESTING_S3MOCK_STORE_REGION: REGION,
19+
});
20+
21+
this.withWaitStrategy(Wait.forHttp("/favicon.ico", S3_HTTP_PORT));
22+
}
23+
24+
withAccessKeyId(accessKeyId: string): this {
25+
this.#accessKeyId = accessKeyId;
26+
return this;
27+
}
28+
withSecretAccessKey(secretAccessKey: string): this {
29+
this.#secretAccessKey = secretAccessKey;
30+
return this;
31+
}
32+
33+
override async start(): Promise<StartedS3MockContainer> {
34+
return new StartedS3MockContainer(
35+
await super.start(),
36+
this.#accessKeyId,
37+
this.#secretAccessKey,
38+
S3_HTTP_PORT,
39+
S3_HTTPS_PORT
40+
);
41+
}
42+
}
43+
44+
export class StartedS3MockContainer extends AbstractStartedContainer {
45+
readonly #accessKeyId: string;
46+
readonly #secretAccessKey: string;
47+
readonly #s3HttpPort: number;
48+
readonly #s3HttpsPort: number;
49+
50+
constructor(
51+
startedContainer: StartedTestContainer,
52+
accessKeyId: string,
53+
secretAccessKey: string,
54+
s3HttpPort: number,
55+
s3HttpsPort: number
56+
) {
57+
super(startedContainer);
58+
this.#accessKeyId = accessKeyId;
59+
this.#secretAccessKey = secretAccessKey;
60+
this.#s3HttpPort = s3HttpPort;
61+
this.#s3HttpsPort = s3HttpsPort;
62+
}
63+
64+
getHttpPort() {
65+
return this.startedTestContainer.getMappedPort(this.#s3HttpPort);
66+
}
67+
getHttpsPort() {
68+
return this.startedTestContainer.getMappedPort(this.#s3HttpsPort);
69+
}
70+
getAccessKeyId(): string {
71+
return this.#accessKeyId;
72+
}
73+
getSecretAccessKey(): string {
74+
return this.#secretAccessKey;
75+
}
76+
77+
getHttpConnectionUrl() {
78+
return `http://${this.getHost()}:${this.getHttpPort()}`;
79+
}
80+
getHttpsConnectionUrl() {
81+
return `https://${this.getHost()}:${this.getHttpsPort()}`;
82+
}
83+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"exclude": [
4+
"build",
5+
"src/**/*.test.ts"
6+
],
7+
"references": [
8+
{
9+
"path": "../../testcontainers"
10+
}
11+
]
12+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"extends": "../../../tsconfig.base.json",
3+
"compilerOptions": {
4+
"rootDir": "src",
5+
"outDir": "build",
6+
"paths": {
7+
"testcontainers": [
8+
"../../testcontainers/src"
9+
]
10+
}
11+
},
12+
"exclude": [
13+
"build"
14+
],
15+
"references": [
16+
{
17+
"path": "../../testcontainers"
18+
}
19+
]
20+
}

0 commit comments

Comments
 (0)