veza/veza-backend-api/internal/shutdown/shutdown_test.go
senke f6a40c9ec6 [BE-SVC-017] be-svc: Implement graceful shutdown
- Created ShutdownManager for coordinated graceful shutdown of all services
- Added Shutdowner interface for services that need graceful shutdown
- Implemented parallel shutdown with individual timeouts (10s per service)
- Added global shutdown timeout (30s total)
- Integrated shutdown manager in main.go for:
  - HTTP server shutdown
  - JobWorker cancellation
  - Config.Close() (DB, Redis, RabbitMQ)
  - Logger sync
  - Sentry flush
- Added comprehensive unit tests for shutdown manager
- Prevents registration of new services during shutdown

Phase: PHASE-6
Priority: P2
Progress: 113/267 (42.32%)
2025-12-24 17:03:11 +01:00

145 lines
3.4 KiB
Go

package shutdown
import (
"context"
"errors"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)
func TestNewShutdownManager(t *testing.T) {
logger, _ := zap.NewDevelopment()
sm := NewShutdownManager(logger)
assert.NotNil(t, sm)
assert.Equal(t, 0, len(sm.shutdowners))
assert.False(t, sm.shuttingDown)
}
func TestShutdownManager_Register(t *testing.T) {
logger, _ := zap.NewDevelopment()
sm := NewShutdownManager(logger)
shutdowner := NewShutdownFunc("test_service", func(ctx context.Context) error {
return nil
})
sm.Register(shutdowner)
assert.Equal(t, 1, len(sm.shutdowners))
}
func TestShutdownManager_Shutdown_Success(t *testing.T) {
logger, _ := zap.NewDevelopment()
sm := NewShutdownManager(logger)
shutdownCount := 0
shutdowner1 := NewShutdownFunc("service1", func(ctx context.Context) error {
shutdownCount++
return nil
})
shutdowner2 := NewShutdownFunc("service2", func(ctx context.Context) error {
shutdownCount++
return nil
})
sm.Register(shutdowner1)
sm.Register(shutdowner2)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
err := sm.Shutdown(ctx)
require.NoError(t, err)
assert.Equal(t, 2, shutdownCount)
}
func TestShutdownManager_Shutdown_WithError(t *testing.T) {
logger, _ := zap.NewDevelopment()
sm := NewShutdownManager(logger)
shutdowner1 := NewShutdownFunc("service1", func(ctx context.Context) error {
return nil
})
shutdowner2 := NewShutdownFunc("service2", func(ctx context.Context) error {
return errors.New("shutdown error")
})
sm.Register(shutdowner1)
sm.Register(shutdowner2)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
err := sm.Shutdown(ctx)
require.Error(t, err)
assert.Contains(t, err.Error(), "shutdown completed with 1 errors")
}
func TestShutdownManager_Shutdown_Timeout(t *testing.T) {
logger, _ := zap.NewDevelopment()
sm := NewShutdownManager(logger)
shutdowner := NewShutdownFunc("slow_service", func(ctx context.Context) error {
// Simuler un service qui prend trop de temps
time.Sleep(2 * time.Second)
return nil
})
sm.Register(shutdowner)
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
err := sm.Shutdown(ctx)
// Le timeout devrait être atteint
assert.Error(t, err)
}
func TestShutdownManager_RegisterDuringShutdown(t *testing.T) {
logger, _ := zap.NewDevelopment()
sm := NewShutdownManager(logger)
shutdowner1 := NewShutdownFunc("service1", func(ctx context.Context) error {
return nil
})
sm.Register(shutdowner1)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Démarrer le shutdown dans une goroutine
done := make(chan bool)
go func() {
sm.Shutdown(ctx)
done <- true
}()
// Essayer d'enregistrer pendant le shutdown
time.Sleep(10 * time.Millisecond)
shutdowner2 := NewShutdownFunc("service2", func(ctx context.Context) error {
return nil
})
sm.Register(shutdowner2)
<-done
// Le deuxième service ne devrait pas être arrêté car enregistré pendant le shutdown
assert.Equal(t, 1, len(sm.shutdowners))
}
func TestShutdownFunc(t *testing.T) {
shutdownFunc := NewShutdownFunc("test", func(ctx context.Context) error {
return nil
})
assert.Equal(t, "test", shutdownFunc.Name())
ctx := context.Background()
err := shutdownFunc.Shutdown(ctx)
assert.NoError(t, err)
}