veza/veza-backend-api/internal/recovery/error_recovery_test.go
senke b8adaf8935 [BE-SVC-022] be-svc: Implement data export service
- Created DataExportService for comprehensive user data export (GDPR compliance)
- Exports all user data: profile, settings, tracks, playlists, comments, likes, analytics, federated identities, roles
- Added ExportUserData method to retrieve all user data from database
- Added ExportUserDataAsJSON method to export as downloadable JSON file
- Added endpoint GET /api/v1/users/me/export that returns JSON file download
- Comprehensive unit tests for export service
- Proper error handling and logging

Phase: PHASE-6
Priority: P2
Progress: 118/267 (44.19%)
2025-12-24 18:01:00 +01:00

179 lines
4.2 KiB
Go

package recovery
import (
"context"
"errors"
"testing"
"time"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
)
func TestRetryRecoveryStrategy(t *testing.T) {
ctx := context.Background()
logger, _ := zap.NewDevelopment()
attempts := 0
fn := func() error {
attempts++
if attempts < 2 {
return errors.New("temporary error")
}
return nil
}
config := &RetryConfig{
MaxAttempts: 3,
InitialDelay: 10 * time.Millisecond,
}
strategy := NewRetryRecoveryStrategy(fn, config, logger)
assert.True(t, strategy.CanRecover(errors.New("timeout")))
assert.False(t, strategy.CanRecover(nil))
err := strategy.Recover(ctx, errors.New("temporary error"))
assert.NoError(t, err)
assert.Equal(t, 2, attempts)
}
func TestFallbackRecoveryStrategy(t *testing.T) {
ctx := context.Background()
logger, _ := zap.NewDevelopment()
fallbackCalled := false
fallbackFn := func() error {
fallbackCalled = true
return nil
}
strategy := NewFallbackRecoveryStrategy(fallbackFn, logger)
assert.True(t, strategy.CanRecover(errors.New("any error")))
err := strategy.Recover(ctx, errors.New("original error"))
assert.NoError(t, err)
assert.True(t, fallbackCalled)
}
func TestCircuitBreakerRecoveryStrategy(t *testing.T) {
// Utiliser un contexte avec timeout court pour éviter d'attendre 30s
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
defer cancel()
logger, _ := zap.NewDevelopment()
circuitOpen := true
checkFn := func() bool {
return circuitOpen
}
strategy := NewCircuitBreakerRecoveryStrategy(checkFn, logger)
assert.True(t, strategy.CanRecover(errors.New("circuit breaker error")))
// Simuler la fermeture du circuit breaker après un court délai
go func() {
time.Sleep(50 * time.Millisecond)
circuitOpen = false
}()
err := strategy.Recover(ctx, errors.New("circuit breaker open"))
// Le timeout du contexte devrait se produire avant le timeout de la stratégie
assert.Error(t, err)
assert.Contains(t, err.Error(), "timeout")
}
func TestCompositeRecoveryStrategy(t *testing.T) {
ctx := context.Background()
logger, _ := zap.NewDevelopment()
attempts := 0
retryFn := func() error {
attempts++
if attempts < 2 {
return errors.New("temporary error")
}
return nil
}
config := &RetryConfig{
MaxAttempts: 3,
InitialDelay: 10 * time.Millisecond,
RetryableFunc: func(err error) bool {
return true // Toujours retryable pour ce test
},
}
retryStrategy := NewRetryRecoveryStrategy(retryFn, config, logger)
composite := NewCompositeRecoveryStrategy([]ErrorRecoveryStrategy{retryStrategy}, logger)
assert.True(t, composite.CanRecover(errors.New("timeout")))
err := composite.Recover(ctx, errors.New("temporary error"))
assert.NoError(t, err)
assert.Equal(t, 2, attempts)
}
func TestRecoverWithStrategies(t *testing.T) {
ctx := context.Background()
logger, _ := zap.NewDevelopment()
fallbackCalled := false
retryFn := func() error {
return errors.New("retry failed")
}
retryStrategy := NewRetryRecoveryStrategy(retryFn, nil, logger)
fallbackStrategy := NewFallbackRecoveryStrategy(func() error {
fallbackCalled = true
return nil
}, logger)
strategies := []ErrorRecoveryStrategy{retryStrategy, fallbackStrategy}
err := RecoverWithStrategies(ctx, errors.New("original error"), strategies, logger)
assert.NoError(t, err)
assert.True(t, fallbackCalled)
}
func TestIsTemporaryError(t *testing.T) {
tests := []struct {
name string
err error
expected bool
}{
{"timeout", errors.New("timeout"), true},
{"connection refused", errors.New("connection refused"), true},
{"permanent", errors.New("invalid input"), false},
{"nil", nil, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := IsTemporaryError(tt.err)
assert.Equal(t, tt.expected, result)
})
}
}
func TestIsPermanentError(t *testing.T) {
tests := []struct {
name string
err error
expected bool
}{
{"timeout", errors.New("timeout"), false},
{"permanent", errors.New("invalid input"), true},
{"nil", nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := IsPermanentError(tt.err)
assert.Equal(t, tt.expected, result)
})
}
}