This document describes the testing approach for the SIPREC server, with a focus on session redundancy, environment configuration, and end-to-end testing.
The SIPREC server uses multiple levels of testing to ensure quality and correctness:
- Unit Tests: Test individual components in isolation (packages)
- Integration Tests: Test interactions between components
- End-to-End Tests: Test the complete system flow
- Environment Tests: Validate configuration and environment setup
- Redundancy Tests: Verify session resilience during failures
- Security Tests: Verify TLS and SRTP implementation
Before running tests, ensure you have:
- A properly configured
.env
file (see.env.example
for reference) - Go 1.22 or later installed
- Required test dependencies installed
Run all tests with:
make test
Unit tests are located in each package directory alongside the code they test. For example:
pkg/stt/*.go
→ Unit tests inpkg/stt/*_test.go
pkg/sip/*.go
→ Unit tests inpkg/sip/*_test.go
To run unit tests for a specific package:
make test-package PKG=./pkg/stt
When writing unit tests:
- Use table-driven tests for thorough coverage
- Mock external dependencies
- Test edge cases and error handling
- Keep tests fast and independent
Example unit test structure:
func TestMyFunction(t *testing.T) {
tests := []struct {
name string
input string
expected string
wantErr bool
}{
{"valid input", "test data", "expected result", false},
{"invalid input", "", "", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := MyFunction(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("MyFunction() error = %v, wantErr %v", err, tt.wantErr)
return
}
if result != tt.expected {
t.Errorf("MyFunction() = %v, want %v", result, tt.expected)
}
})
}
}
Integration tests verify that components work together correctly. These tests are located in the /test
directory.
To run integration tests:
go test ./test/...
- SIP Messaging: Tests for SIP message processing
- RTP Handling: Tests for RTP packet processing
- STT Integration: Tests for speech-to-text processing
- Storage: Tests for session storage and retrieval
Environment tests verify that the server can correctly load configuration from environment variables and .env files. These are crucial for ensuring the server starts correctly in different environments.
make env-test
Or run the environment check tool directly:
go run ./cmd/testenv/main.go
For validation of critical configurations:
go run ./cmd/testenv/main.go --validate
- LoadEnvironment: Finds and loads the .env file using multiple strategies
- GetEnvWithDefault: Retrieves environment variables with fallback defaults
- RunEnvironmentCheck: Performs full validation of environment configuration
- Directory Verification: Checks required directories exist and are accessible
The environment tests use a robust approach to configuration:
- Multiple Location Search: Tries multiple strategies to find .env file
- Path Resolution: Handles both relative and absolute paths
- Default Fallbacks: Provides sensible defaults when configuration is missing
- Configuration Validation: Validates that critical config values are reasonable
Session redundancy tests verify the server's ability to maintain call continuity during network failures or server restarts, as specified in RFC 7245.
make test-redundancy
Or run specific redundancy tests:
go test ./test/e2e -run TestSiprecRedundancyFlow
-
TestSiprecRedundancyFlow: Tests the complete session recovery flow
- Simulates initial call establishment
- Creates failover metadata according to RFC 7245
- Triggers network disconnect
- Verifies session recovery works correctly
- Checks call metadata continuity
-
TestConcurrentSessionsRedundancy: Tests redundancy with multiple concurrent sessions
- Creates multiple simultaneous recording sessions
- Simulates random session failures
- Verifies all sessions can be recovered correctly
- Ensures no cross-session interference
-
TestStreamContinuityAfterFailover: Tests RTP stream continuity during failover
- Establishes RTP streams with sequence numbers
- Simulates network failure during active streaming
- Verifies stream continuity after reconnection
- Validates no packet loss during recovery
- CreateFailoverMetadata: Generates RFC 7245 compliant metadata for session failover
- ParseFailoverMetadata: Extracts session information from rs-metadata
- RecoverSession: Creates a new session based on failover metadata
- ProcessStreamRecovery: Restores stream information during recovery
- ValidateSessionContinuity: Validates session integrity post-recovery
The redundancy tests verify:
- Session Identity Preservation: Original and recovered sessions maintain the same logical identity
- Participant Continuity: All participants are correctly preserved during failover
- Dialog Replacement: SIP dialog replacement occurs correctly per RFC 7245
- Media Continuity: RTP streams continue with proper sequencing
- Metadata Preservation: All required metadata is preserved across reconnections
Security tests verify the TLS and SRTP implementation to ensure secure communication for both signaling and media.
To test TLS functionality:
chmod +x test_tls.sh
./test_tls.sh
To test a complete secure session with both TLS and SRTP:
cd test_tls
go build -o mock_invite_tls mock_invite_tls.go
./mock_invite_tls
-
TLS Connection Tests:
- Verifies TLS server setup and certificate loading
- Tests TLS handshake and connection establishment
- Validates server identity through certificates
- Tests various TLS client connection patterns
-
SRTP Session Tests:
- Tests SRTP key generation and exchange
- Verifies proper SDP crypto attribute generation
- Tests SRTP packet encryption and decryption
- Validates SRTP session setup and teardown
-
Security Shutdown Tests:
- Ensures secure shutdown of TLS connections
- Verifies proper cleanup of security resources
- Tests graceful termination of encrypted sessions
- OpenSSL Client: Tests basic TLS connectivity
- Custom TLS Test Client: Tests SIP over TLS scenarios
- Mock SIP INVITE with SRTP: Tests media encryption setup
- TLS Connection Verification: Ensures ports are properly listening
- Certificate Validation: Always verify certificate validation works correctly
- Protocol Compliance: Ensure TLS and SRTP implementations follow relevant RFCs
- Key Testing: Test key generation, exchange, and management processes
- Connection Lifecycle: Test the complete lifecycle of secure connections
- Error Handling: Test how security components handle invalid data or attacks
End-to-end tests verify the complete SIPREC call flow from SIP signaling through RTP processing to speech recognition and transcription delivery. These tests are located in the /test/e2e
directory.
To run all end-to-end tests:
make test-e2e
To run a specific end-to-end test:
make test-e2e-case TEST=TestSimulatedSiprecFullFlow
The end-to-end tests follow this general flow:
- Setup: Initialize environment and components
- Call Establishment: Create SIP dialogs and sessions
- Media Exchange: Simulate RTP packet transmission
- Process Audio: Process audio through STT pipeline
- Generate Transcriptions: Produce and validate transcriptions
- Simulate Failures (for redundancy tests): Create network/server failures
- Recover Sessions: Test session recovery mechanisms
- Validate Results: Verify expected outcomes
- Teardown: Clean up resources
The end-to-end tests use several key components:
- Mock SIP Client: Simulates SIP signaling
- RTP Generator: Creates realistic RTP packets
- Mock STT Provider: Simulates transcription
- Session Manager: Tracks call sessions
- Failure Injector: Creates controlled failures for redundancy testing
-
Simple Transcription Flow:
- Focuses on the transcription pipeline
- Verifies text extraction from audio
-
Complete Call Flow:
- Tests the entire SIPREC signaling and media flow
- Verifies all components work together
-
Redundancy Flow:
- Tests session recovery after failures
- Verifies RFC 7245 compliance
-
Concurrent Call Handling:
- Tests system under multiple simultaneous calls
- Verifies resource isolation between calls
- Keep Tests Independent: Each test should run independently
- Use Timeouts: Always use context timeouts to prevent tests from hanging
- Verify Resources: Check that all resources are properly released
- Log Meaningful Information: Include detailed logs for debugging
- Assert Real Behavior: Verify correct behavior, not just function calls
- Control Test Environment: Use controlled test data and configurations
- Test Both Success and Failure Paths: Verify proper error handling
To generate a test coverage report:
make test-coverage
This will generate a coverage report in HTML format at coverage.html
.
- Core Functionality: Aim for >80% coverage
- Error Handling: Test all error paths
- Configuration: Test all configuration options
- Session Redundancy: 100% coverage of redundancy code
All tests are automatically run in the CI pipeline when changes are pushed. The pipeline:
- Runs environment validation
- Runs unit tests for all packages
- Runs integration tests
- Runs redundancy tests
- Runs end-to-end tests
- Generates coverage reports
- Timeouts: Increase context timeout duration for slow tests
- Port Conflicts: Use dynamic port allocation or ensure cleanup
- State Leakage: Ensure tests clean up resources between runs
- Race Conditions: Use the race detector (
-race
flag) - Environment Dependencies: Check that .env is properly loaded
- Verbose Logging: Use
t.Logf()
for detailed test logs - Race Detector: Run tests with
-race
flag - Packet Capture: Use Wireshark to inspect network traffic
- Memory Profiling: Use pprof to find memory leaks
- Environment Tool: Use the environment check tool to validate configuration
Future improvements to the testing framework include:
- Load Testing: Testing the system under high call volume
- Chaos Testing: Simulating network failures and component crashes
- Long-Running Tests: Testing stability over extended periods
- Real Device Testing: Testing with actual SIP phones and PBXs
- Security Testing: Comprehensive security testing for TLS, SRTP, authentication, and authorization
- Benchmark Tests: Performance testing for scaling
When contributing new tests:
- Follow existing test patterns and naming conventions
- Document the purpose and scenarios being tested
- Include both positive and negative test cases
- Ensure tests are deterministic and reliable
- Add appropriate test coverage for new features
- Verify tests pass in isolation and as part of the suite