This directory contains cross-service integration tests that verify message contracts between FizzBuzz services using real Kafka infrastructure.
Integration tests fill the gap between unit tests (mocked dependencies) and E2E tests (full Docker Compose stack):
| Layer | Scope | Speed | Infrastructure |
|---|---|---|---|
| Unit Tests | Single service | Fast (~1s) | Mocked |
| Integration Tests | Message contracts | Medium (~30s) | Testcontainers |
| E2E Tests | Full pipeline | Slow (~5min) | Docker Compose |
┌─────────────────────────────────────────────────────────────┐
│ Integration Test Layer │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Producer │───▶│ Redpanda │───▶│ Consumer │ │
│ │ (Test) │ │ (Container) │ │ (Test) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │
│ Message Contract │
│ Verification │
│ │
└─────────────────────────────────────────────────────────────┘
Tests use Testcontainers to spin up a Redpanda instance (Kafka-compatible) for each test run, ensuring isolation and reproducibility.
# From repository root
pnpm test:integration
# Or directly
pnpm --filter @fizzbuzz-enterprise/integration-tests test
# Watch mode (useful during development)
pnpm --filter @fizzbuzz-enterprise/integration-tests test:watchThe integration tests verify message contracts between all service boundaries:
| Contract | Topic | Producer | Consumer | Status |
|---|---|---|---|---|
NumberMessage |
numbers-topic |
number-ingestion | fizz, buzz | Verified |
FizzResult |
fizz-topic |
fizz-service | aggregator | Verified |
BuzzResult |
buzz-topic |
buzz-service | aggregator | Verified |
FizzBuzzResult |
fizzbuzz-topic |
aggregator | api-service | Verified |
tests/
├── README.md # This file
└── integration/
├── package.json # Test dependencies
├── vitest.config.ts # Vitest configuration
├── tsconfig.json # TypeScript configuration
├── setup/
│ └── global-setup.ts # Starts/stops Redpanda container
└── specs/
└── message-flow.test.ts # Contract verification tests
- Import fixtures from
@fizzbuzz-enterprise/test-utils:
import {
createNumberMessage,
createFizzResult,
type NumberMessage,
type FizzResult,
} from '@fizzbuzz-enterprise/test-utils';- Use the shared Kafka infrastructure:
describe('My Service Contract', () => {
it('should produce valid messages', async () => {
const producer = new TestProducer({ brokers: globalThis.__KAFKA_BROKERS__ });
await producer.connect();
const message = createNumberMessage({ number: 42 });
await producer.send('my-topic', message);
// Assert consumer receives expected format
// ...
await producer.disconnect();
});
});- Use the
consumeOnehelper for synchronized consumption:
const result = await consumeOne<MyMessage>({
topic: 'my-topic',
sendMessage: async () => {
await producer.send('my-topic', myMessage);
},
});
expect(result.value).toMatchObject({
// expected fields
});The @fizzbuzz-enterprise/test-utils package provides factory functions:
| Function | Creates |
|---|---|
createNumberMessage() |
Valid NumberMessage with defaults |
createFizzResult() |
Valid FizzResult with defaults |
createBuzzResult() |
Valid BuzzResult with defaults |
createFizzBuzzResult() |
Valid FizzBuzzResult with defaults |
generateCorrelationId() |
UUID v4 correlation ID |
All factories accept overrides:
const message = createNumberMessage({
number: 15,
correlationId: 'test-123',
});| Utility | Purpose |
|---|---|
TestProducer |
Send messages to Kafka topics |
TestConsumer |
Consume messages with filtering |
waitForMessages() |
Wait for N messages on a topic |
waitForCondition() |
Poll until condition is true |
waitForHealthy() |
Wait for HTTP health endpoint |
Integration tests run in GitHub Actions after unit tests pass:
test-integration:
needs: [test-fizz-service, test-buzz-service, ...]
timeout-minutes: 10Tests are triggered when relevant paths change:
tests/integration/**packages/test-utils/**packages/kafka-client/**- Service source directories
If tests fail with container startup errors:
# Increase Docker resources (memory/CPU)
# Or pull Redpanda image ahead of time:
docker pull redpandadata/redpanda:latestIf tests hang waiting for messages, ensure:
- Topic exists (created in global setup)
- Consumer group ID is unique per test
- Producer sends after consumer joins group (use
consumeOnehelper)
Enable verbose Kafka logging:
DEBUG=kafkajs:* pnpm test:integrationView container logs:
// In test file
console.log(await container.logs());