Skip to main content

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โ€‹

npm install

๐Ÿ“‹ 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โ€‹

  1. Run the tests: npm test
  2. Explore the code: Look at src/index.ts to understand the session service
  3. Modify tests: Try adding your own test cases
  4. Check coverage: npm run test:coverage
  5. Try other examples: Check examples 28 (Vitest) and 29 (Jest)

๐Ÿค Contributingโ€‹

When adding new tests:

  1. Follow the Arrange-Act-Assert pattern
  2. Use descriptive test names
  3. Focus on behavior, not implementation
  4. Keep tests simple and readable
  5. Test context structure and isolation

Remember

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.

Framework Integration

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 tested
  • tests/session-service.test.ts - The actual tests
  • tests/test-utils.ts - Helper functions for test setup

Common AI Mistakes to Avoidโ€‹

  1. Don't try to connect to real Redis - Use BeaconRedisMock instead
  2. Don't test Redis connection - Test business logic only
  3. Don't test Redis commands - Test context behavior instead
  4. 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!