second first commit
This commit is contained in:
@@ -0,0 +1,270 @@
|
||||
# Testcontainers — Node.js / TypeScript
|
||||
|
||||
The `@floci/testcontainers` package integrates Floci with [Testcontainers for Node.js](https://node.testcontainers.org/). It works with any test runner that supports `async`/`await` — Jest, Vitest, Mocha, and others.
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
npm install --save-dev @floci/testcontainers
|
||||
```
|
||||
|
||||
```sh
|
||||
# yarn
|
||||
yarn add --dev @floci/testcontainers
|
||||
|
||||
# pnpm
|
||||
pnpm add -D @floci/testcontainers
|
||||
```
|
||||
|
||||
## Basic usage — Jest
|
||||
|
||||
```typescript
|
||||
import { FlociContainer } from "@floci/testcontainers";
|
||||
import { S3Client, CreateBucketCommand, ListBucketsCommand } from "@aws-sdk/client-s3";
|
||||
|
||||
describe("S3", () => {
|
||||
let floci: FlociContainer;
|
||||
|
||||
beforeAll(async () => {
|
||||
floci = await new FlociContainer().start();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await floci.stop();
|
||||
});
|
||||
|
||||
it("should create and list a bucket", async () => {
|
||||
const s3 = new S3Client({
|
||||
endpoint: floci.getEndpoint(),
|
||||
region: floci.getRegion(),
|
||||
credentials: {
|
||||
accessKeyId: floci.getAccessKey(),
|
||||
secretAccessKey: floci.getSecretKey(),
|
||||
},
|
||||
forcePathStyle: true,
|
||||
});
|
||||
|
||||
await s3.send(new CreateBucketCommand({ Bucket: "my-bucket" }));
|
||||
|
||||
const { Buckets } = await s3.send(new ListBucketsCommand({}));
|
||||
expect(Buckets?.some(b => b.Name === "my-bucket")).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## SQS example
|
||||
|
||||
```typescript
|
||||
import { FlociContainer } from "@floci/testcontainers";
|
||||
import {
|
||||
SQSClient,
|
||||
CreateQueueCommand,
|
||||
SendMessageCommand,
|
||||
ReceiveMessageCommand,
|
||||
} from "@aws-sdk/client-sqs";
|
||||
|
||||
describe("SQS", () => {
|
||||
let floci: FlociContainer;
|
||||
let sqs: SQSClient;
|
||||
|
||||
beforeAll(async () => {
|
||||
floci = await new FlociContainer().start();
|
||||
sqs = new SQSClient({
|
||||
endpoint: floci.getEndpoint(),
|
||||
region: floci.getRegion(),
|
||||
credentials: {
|
||||
accessKeyId: floci.getAccessKey(),
|
||||
secretAccessKey: floci.getSecretKey(),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await floci.stop();
|
||||
});
|
||||
|
||||
it("should send and receive a message", async () => {
|
||||
const { QueueUrl } = await sqs.send(
|
||||
new CreateQueueCommand({ QueueName: "orders" })
|
||||
);
|
||||
|
||||
await sqs.send(
|
||||
new SendMessageCommand({
|
||||
QueueUrl,
|
||||
MessageBody: JSON.stringify({ event: "order.placed" }),
|
||||
})
|
||||
);
|
||||
|
||||
const { Messages } = await sqs.send(
|
||||
new ReceiveMessageCommand({ QueueUrl, MaxNumberOfMessages: 1 })
|
||||
);
|
||||
|
||||
expect(Messages).toHaveLength(1);
|
||||
expect(JSON.parse(Messages![0].Body!).event).toBe("order.placed");
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## DynamoDB example
|
||||
|
||||
```typescript
|
||||
import { FlociContainer } from "@floci/testcontainers";
|
||||
import {
|
||||
DynamoDBClient,
|
||||
CreateTableCommand,
|
||||
PutItemCommand,
|
||||
GetItemCommand,
|
||||
} from "@aws-sdk/client-dynamodb";
|
||||
|
||||
describe("DynamoDB", () => {
|
||||
let floci: FlociContainer;
|
||||
let dynamo: DynamoDBClient;
|
||||
|
||||
beforeAll(async () => {
|
||||
floci = await new FlociContainer().start();
|
||||
dynamo = new DynamoDBClient({
|
||||
endpoint: floci.getEndpoint(),
|
||||
region: floci.getRegion(),
|
||||
credentials: {
|
||||
accessKeyId: floci.getAccessKey(),
|
||||
secretAccessKey: floci.getSecretKey(),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await floci.stop();
|
||||
});
|
||||
|
||||
it("should put and get an item", async () => {
|
||||
await dynamo.send(
|
||||
new CreateTableCommand({
|
||||
TableName: "Orders",
|
||||
AttributeDefinitions: [{ AttributeName: "id", AttributeType: "S" }],
|
||||
KeySchema: [{ AttributeName: "id", KeyType: "HASH" }],
|
||||
BillingMode: "PAY_PER_REQUEST",
|
||||
})
|
||||
);
|
||||
|
||||
await dynamo.send(
|
||||
new PutItemCommand({
|
||||
TableName: "Orders",
|
||||
Item: {
|
||||
id: { S: "order-1" },
|
||||
status: { S: "placed" },
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const { Item } = await dynamo.send(
|
||||
new GetItemCommand({
|
||||
TableName: "Orders",
|
||||
Key: { id: { S: "order-1" } },
|
||||
})
|
||||
);
|
||||
|
||||
expect(Item?.status?.S).toBe("placed");
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Vitest
|
||||
|
||||
The same pattern works with Vitest — replace `describe`/`it`/`expect` with their Vitest equivalents (the API is identical):
|
||||
|
||||
```typescript
|
||||
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
||||
import { FlociContainer } from "@floci/testcontainers";
|
||||
import { S3Client, CreateBucketCommand, ListBucketsCommand } from "@aws-sdk/client-s3";
|
||||
|
||||
describe("S3", () => {
|
||||
let floci: FlociContainer;
|
||||
|
||||
beforeAll(async () => {
|
||||
floci = await new FlociContainer().start();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await floci.stop();
|
||||
});
|
||||
|
||||
it("should create a bucket", async () => {
|
||||
const s3 = new S3Client({
|
||||
endpoint: floci.getEndpoint(),
|
||||
region: floci.getRegion(),
|
||||
credentials: {
|
||||
accessKeyId: floci.getAccessKey(),
|
||||
secretAccessKey: floci.getSecretKey(),
|
||||
},
|
||||
forcePathStyle: true,
|
||||
});
|
||||
|
||||
await s3.send(new CreateBucketCommand({ Bucket: "vitest-bucket" }));
|
||||
|
||||
const { Buckets } = await s3.send(new ListBucketsCommand({}));
|
||||
expect(Buckets?.some(b => b.Name === "vitest-bucket")).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Reusing the container across test files
|
||||
|
||||
Start the container once in a global setup file and expose the endpoint via an environment variable or a shared module so individual test files don't each start their own container.
|
||||
|
||||
=== "Jest — globalSetup"
|
||||
|
||||
```typescript
|
||||
// jest.global-setup.ts
|
||||
import { FlociContainer } from "@floci/testcontainers";
|
||||
|
||||
let floci: FlociContainer;
|
||||
|
||||
export async function setup() {
|
||||
floci = await new FlociContainer().start();
|
||||
process.env.FLOCI_ENDPOINT = floci.getEndpoint();
|
||||
}
|
||||
|
||||
export async function teardown() {
|
||||
await floci?.stop();
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
// jest.config.json
|
||||
{
|
||||
"globalSetup": "./jest.global-setup.ts"
|
||||
}
|
||||
```
|
||||
|
||||
=== "Vitest — globalSetup"
|
||||
|
||||
```typescript
|
||||
// vitest.global-setup.ts
|
||||
import { FlociContainer } from "@floci/testcontainers";
|
||||
|
||||
let floci: FlociContainer;
|
||||
|
||||
export async function setup() {
|
||||
floci = await new FlociContainer().start();
|
||||
process.env.FLOCI_ENDPOINT = floci.getEndpoint();
|
||||
}
|
||||
|
||||
export async function teardown() {
|
||||
await floci?.stop();
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// vitest.config.ts
|
||||
import { defineConfig } from "vitest/config";
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globalSetup: "./vitest.global-setup.ts",
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Source and changelog
|
||||
|
||||
[github.com/floci-io/testcontainers-floci-node](https://github.com/floci-io/testcontainers-floci-node)
|
||||
Reference in New Issue
Block a user