Testing Redis Context Patterns
This example demonstrates how to test SyntropyLog applications that use Redis for context management and correlation using declarative testing patterns.
๐ฏ Key Conceptsโ
1. Test WHAT the system produces, not HOW Redis works internallyโ
Instead of testing if Redis is connected or if data is stored, we test:
- โ Context behavior: Does the service maintain correlation IDs correctly?
- โ Session operations: Do create/read/update/delete operations work?
- โ Error handling: Does it handle invalid data properly?
- โ Context isolation: Are different operations isolated?
2. Declarative tests that read like specificationsโ
// Instead of: "Test that Redis connection is established"
// We write: "Test that session creation returns expected structure"
it('should create session with correlation and transaction IDs', async () => {
const userId = 'user-123';
const sessionData = { role: 'admin' };
const result = await sessionService.createSession(userId, sessionData);
expect(result).toHaveProperty('sessionId');
expect(result).toHaveProperty('correlationId');
expect(result).toHaveProperty('transactionId');
});
3. Use BeaconRedisMock for in-memory testingโ
SyntropyLog provides BeaconRedisMock
that handles everything in-memory:
- โ No external Redis needed - everything runs in memory
- โ Fast and reliable - no network dependencies
- โ Full Redis API support - all Redis commands work
- โ Automatic cleanup - no data persistence between tests
We test:
- โ Context structure and propagation
- โ Session operation results
- โ Error handling and validation
- โ Context isolation between operations
๐ Quick Startโ
- Install Dependencies
- Run Tests
- Watch Mode
- Coverage
npm install
npm test
npm run test:watch
npm run test:coverage
๐ Prerequisitesโ
- Node.js 18+ (using nvm:
source ~/.nvm/nvm.sh
) - npm or yarn
- Basic knowledge of Vitest and TypeScript
- Understanding of Redis context patterns
๐ง Setupโ
1. Install Dependenciesโ
npm install syntropylog vitest typescript
2. Configure Vitestโ
Create vitest.config.ts
:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'node',
globals: true,
},
});
3. Configure TypeScriptโ
Create tsconfig.json
:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"types": ["vitest/globals", "node"]
},
"include": ["src/**/*", "tests/**/*"],
"exclude": ["node_modules", "dist"]
}
๐งช Testing Patternsโ
Pattern 1: Context Structure Testingโ
Test that context operations return the expected structure:
it('should create session with correlation and transaction IDs', async () => {
const result = await sessionService.createSession('user-123', { role: 'admin' });
expect(result).toHaveProperty('sessionId');
expect(result).toHaveProperty('correlationId');
expect(result).toHaveProperty('transactionId');
expect(typeof result.correlationId).toBe('string');
});
Pattern 2: Context Isolation Testingโ
Test that different operations maintain separate context:
it('should generate unique session IDs for different sessions', async () => {
const session1 = await sessionService.createSession('user-123', { role: 'admin' });
const session2 = await sessionService.createSession('user-456', { role: 'user' });
expect(session1.sessionId).not.toBe(session2.sessionId);
expect(session1.correlationId).not.toBe(session2.correlationId);
});
Pattern 3: Session Operation Testingโ
Test that session operations behave correctly:
it('should handle non-existent sessions gracefully', async () => {
const result = await sessionService.getSession('non-existent');
expect(result).toBeNull();
});
it('should handle invalid update data', async () => {
await expect(sessionService.updateSession('session-123', { role: 'invalid-role' }))
.rejects.toThrow('Invalid role specified');
});
Pattern 4: Redis Context Persistence Testingโ
Test that context data persists across operations:
it('should persist and retrieve context data across operations', async () => {
// Create session
const session = await sessionService.createSession('user-123', { role: 'admin' });
// Retrieve session
const retrieved = await sessionService.getSession(session.sessionId);
expect(retrieved).toMatchObject({
userId: 'user-123',
role: 'admin',
sessionId: session.sessionId
});
});
๐ Understanding BeaconRedisMockโ
The BeaconRedisMock
is a complete in-memory simulation of Redis that provides:
What the Mock Simulatesโ
- Redis Commands: All standard Redis operations (GET, SET, DEL, etc.)
- Data Persistence: In-memory storage that persists during test execution
- Context Management: Correlation IDs, transaction IDs, and session data
- Error Handling: Simulates Redis errors and edge cases
Why Use BeaconRedisMock?โ
- โ No External Redis: No need for Redis server or Docker containers
- โ Fast Execution: Everything runs in memory
- โ Reliable: No network issues or connection problems
- โ Isolated: Each test gets a fresh mock instance
- โ Full API Support: All Redis commands work as expected
โ What's Being Testedโ
What We Testโ
- Context Structure: Correlation IDs, transaction IDs, session IDs
- Session Operations: Create, read, update, delete operations
- Data Persistence: Context data across operations
- Error Handling: Invalid data, missing sessions, edge cases
- Context Isolation: Different operations maintain separate context
What We Don't Testโ
- Redis Connection: Network connectivity, authentication
- Redis Performance: Memory usage, response times
- Redis Configuration: Server settings, cluster configuration
- External Dependencies: Network calls, database connections
๐ซ Common Pitfallsโ
1. Testing Redis Instead of Business Logicโ
โ Don't do this:
it('should connect to Redis successfully', async () => {
// Testing Redis connection - external dependency
});
โ Do this instead:
it('should create session with proper context', async () => {
const result = await sessionService.createSession('user-123', { role: 'admin' });
expect(result).toHaveProperty('correlationId');
});
2. Testing Implementation Detailsโ
โ Don't do this:
it('should call Redis SET command', async () => {
// Testing internal Redis calls - implementation detail
});
โ Do this instead:
it('should persist session data', async () => {
const session = await sessionService.createSession('user-123', { role: 'admin' });
const retrieved = await sessionService.getSession(session.sessionId);
expect(retrieved.role).toBe('admin');
});
3. Testing External Dependenciesโ
โ Don't do this:
it('should handle Redis network errors', async () => {
// Testing network issues - external dependency
});
โ Do this instead:
it('should handle invalid session data', async () => {
await expect(sessionService.createSession('user-123', null))
.rejects.toThrow('Session data is required');
});
๐ฏ Next Stepsโ
- Run the tests:
npm test
- Explore the code: Look at
src/index.ts
to understand the session service - Modify tests: Try adding your own test cases
- Check coverage:
npm run test:coverage
- Try other examples: Check examples 28 (Vitest) and 29 (Jest)
๐ค Contributingโ
When adding new tests:
- Follow the Arrange-Act-Assert pattern
- Use descriptive test names
- Focus on behavior, not implementation
- Keep tests simple and readable
- Test context structure and isolation
๐ Related Examplesโ
The goal is to test business logic and context behavior, not Redis internals. Let the mock handle the complexity, and focus your tests on what matters most: your application's behavior.
This example works seamlessly with the published syntropylog
package. The BeaconRedisMock
is included in the testing module and provides full Redis simulation.
๐จ For AI Assistants (and other helpful bots)โ
If you're an AI assistant or helpful bot working on this codebase, here's what you need to know:
What This Example Doesโ
- Shows how to test SyntropyLog applications that use Redis for context
- Uses BeaconRedisMock for in-memory Redis simulation
- Focuses on testing context behavior, not Redis internals
Key Files to Understandโ
src/index.ts
- The SessionService being testedtests/session-service.test.ts
- The actual teststests/test-utils.ts
- Helper functions for test setup
Common AI Mistakes to Avoidโ
- Don't try to connect to real Redis - Use BeaconRedisMock instead
- Don't test Redis connection - Test business logic only
- Don't test Redis commands - Test context behavior instead
- Don't test network errors - Test data validation instead
When Modifying This Exampleโ
- Keep tests simple and focused on context behavior
- Use the existing patterns as templates
- Don't add unnecessary complexity
- Follow the Arrange-Act-Assert pattern
If Tests Failโ
- Check that you're using BeaconRedisMock correctly
- Ensure you're not trying to access real Redis
- Verify that the service is properly injected with the mock
- Look at the existing working tests as reference
Remember: This is about testing context behavior, not Redis. Keep it simple!