veza/veza-backend-api/internal/services/cache_service_test.go
senke 286be8ba1d
Some checks failed
Backend API CI / test-unit (push) Failing after 0s
Backend API CI / test-integration (push) Failing after 0s
Frontend CI / test (push) Failing after 0s
Storybook Audit / Build & audit Storybook (push) Failing after 0s
chore(v0.102): consolidate remaining changes — docs, frontend, backend
- docs: SCOPE_CONTROL, CONTRIBUTING, README, .github templates
- frontend: DeveloperDashboardView, Player components, MSW handlers, auth, reactQuerySync
- backend: playback_analytics, playlist_service, testutils, integration README

Excluded (artifacts): .auth, playwright-report, test-results, storybook_audit_detailed.json
2026-02-20 13:02:12 +01:00

539 lines
13 KiB
Go

package services
import (
"context"
"os"
"testing"
"time"
"github.com/google/uuid"
"github.com/redis/go-redis/v9"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)
// setupTestCacheService crée un CacheService de test avec Redis
func setupTestCacheService(t *testing.T) (*CacheService, *redis.Client) {
redisURL := os.Getenv("REDIS_TEST_URL")
if redisURL == "" {
if testing.Short() {
t.Skip("Skipping CacheService test in short mode (requires Redis, set REDIS_TEST_URL)")
return nil, nil
}
redisURL = "redis://localhost:6379/15" // Utilise DB 15 pour les tests
}
opts, err := redis.ParseURL(redisURL)
if err != nil {
t.Skipf("Skipping test: failed to parse Redis URL: %v", err)
return nil, nil
}
client := redis.NewClient(opts)
// Test de connexion
ctx := context.Background()
_, err = client.Ping(ctx).Result()
if err != nil {
t.Skipf("Skipping test: Redis not available: %v", err)
return nil, nil
}
// Nettoyer la base de données de test
client.FlushDB(ctx)
// Cleanup: Flush DB après les tests
t.Cleanup(func() {
client.FlushDB(ctx)
client.Close()
})
logger := zap.NewNop()
service := NewCacheService(client, logger)
return service, client
}
func TestCacheService_SetAndGet_Success(t *testing.T) {
service, _ := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
key := "test:key"
value := map[string]interface{}{
"name": "test",
"value": 123,
}
ttl := 5 * time.Minute
// Set
err := service.Set(ctx, key, value, ttl)
assert.NoError(t, err)
// Get
var retrieved map[string]interface{}
err = service.Get(ctx, key, &retrieved)
assert.NoError(t, err)
assert.Equal(t, value["name"], retrieved["name"])
assert.Equal(t, float64(123), retrieved["value"]) // JSON unmarshals numbers as float64
}
func TestCacheService_Get_CacheMiss(t *testing.T) {
service, _ := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
key := "test:nonexistent"
var value map[string]interface{}
err := service.Get(ctx, key, &value)
assert.Error(t, err)
assert.Equal(t, ErrCacheMiss, err)
}
func TestCacheService_Delete_Success(t *testing.T) {
service, _ := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
key := "test:delete"
value := "test value"
ttl := 5 * time.Minute
// Set
err := service.Set(ctx, key, value, ttl)
assert.NoError(t, err)
// Verify exists
var retrieved string
err = service.Get(ctx, key, &retrieved)
assert.NoError(t, err)
// Delete
err = service.Delete(ctx, key)
assert.NoError(t, err)
// Verify deleted
err = service.Get(ctx, key, &retrieved)
assert.Error(t, err)
assert.Equal(t, ErrCacheMiss, err)
}
func TestCacheService_Exists_True(t *testing.T) {
service, _ := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
key := "test:exists"
value := "test value"
ttl := 5 * time.Minute
// Set
err := service.Set(ctx, key, value, ttl)
assert.NoError(t, err)
// Check exists
exists, err := service.Exists(ctx, key)
assert.NoError(t, err)
assert.True(t, exists)
}
func TestCacheService_Exists_False(t *testing.T) {
service, _ := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
key := "test:nonexistent"
exists, err := service.Exists(ctx, key)
assert.NoError(t, err)
assert.False(t, exists)
}
func TestCacheService_SetUser_Success(t *testing.T) {
service, _ := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
userID := uuid.New()
user := map[string]interface{}{
"id": userID.String(),
"username": "testuser",
"email": "test@example.com",
}
config := DefaultCacheConfig()
err := service.SetUser(ctx, userID, user, config)
assert.NoError(t, err)
// Verify
var retrieved map[string]interface{}
err = service.GetUser(ctx, userID, &retrieved)
assert.NoError(t, err)
assert.Equal(t, user["username"], retrieved["username"])
}
func TestCacheService_DeleteUser_Success(t *testing.T) {
service, _ := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
userID := uuid.New()
user := map[string]interface{}{
"id": userID.String(),
"username": "testuser",
}
config := DefaultCacheConfig()
// Set
err := service.SetUser(ctx, userID, user, config)
assert.NoError(t, err)
// Delete
err = service.DeleteUser(ctx, userID)
assert.NoError(t, err)
// Verify deleted
var retrieved map[string]interface{}
err = service.GetUser(ctx, userID, &retrieved)
assert.Error(t, err)
assert.Equal(t, ErrCacheMiss, err)
}
func TestCacheService_SetTrack_Success(t *testing.T) {
service, _ := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
trackID := uuid.New()
track := map[string]interface{}{
"id": trackID.String(),
"title": "Test Track",
"duration": 180,
}
config := DefaultCacheConfig()
err := service.SetTrack(ctx, trackID, track, config)
assert.NoError(t, err)
// Verify
var retrieved map[string]interface{}
err = service.GetTrack(ctx, trackID, &retrieved)
assert.NoError(t, err)
assert.Equal(t, track["title"], retrieved["title"])
}
func TestCacheService_DeleteTrack_Success(t *testing.T) {
service, _ := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
trackID := uuid.New()
track := map[string]interface{}{
"id": trackID.String(),
"title": "Test Track",
}
config := DefaultCacheConfig()
// Set
err := service.SetTrack(ctx, trackID, track, config)
assert.NoError(t, err)
// Delete
err = service.DeleteTrack(ctx, trackID)
assert.NoError(t, err)
// Verify deleted
var retrieved map[string]interface{}
err = service.GetTrack(ctx, trackID, &retrieved)
assert.Error(t, err)
assert.Equal(t, ErrCacheMiss, err)
}
func TestCacheService_SetPlaylist_Success(t *testing.T) {
service, _ := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
playlistID := uuid.New()
playlist := map[string]interface{}{
"id": playlistID.String(),
"name": "Test Playlist",
"tracks": []string{},
}
config := DefaultCacheConfig()
err := service.SetPlaylist(ctx, playlistID, playlist, config)
assert.NoError(t, err)
// Verify
var retrieved map[string]interface{}
err = service.GetPlaylist(ctx, playlistID, &retrieved)
assert.NoError(t, err)
assert.Equal(t, playlist["name"], retrieved["name"])
}
func TestCacheService_DeletePlaylist_Success(t *testing.T) {
service, _ := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
playlistID := uuid.New()
playlist := map[string]interface{}{
"id": playlistID.String(),
"name": "Test Playlist",
}
config := DefaultCacheConfig()
// Set
err := service.SetPlaylist(ctx, playlistID, playlist, config)
assert.NoError(t, err)
// Delete
err = service.DeletePlaylist(ctx, playlistID)
assert.NoError(t, err)
// Verify deleted
var retrieved map[string]interface{}
err = service.GetPlaylist(ctx, playlistID, &retrieved)
assert.Error(t, err)
assert.Equal(t, ErrCacheMiss, err)
}
func TestCacheService_DeletePattern_Success(t *testing.T) {
service, client := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
pattern := "test:pattern:*"
// Set multiple keys matching pattern
keys := []string{"test:pattern:1", "test:pattern:2", "test:pattern:3"}
for _, key := range keys {
err := client.Set(ctx, key, "value", 5*time.Minute).Err()
require.NoError(t, err)
}
// Set a key that doesn't match
err := client.Set(ctx, "test:other:1", "value", 5*time.Minute).Err()
require.NoError(t, err)
// Delete pattern
err = service.DeletePattern(ctx, pattern)
assert.NoError(t, err)
// Verify pattern keys deleted
for _, key := range keys {
exists, err := client.Exists(ctx, key).Result()
assert.NoError(t, err)
assert.Equal(t, int64(0), exists)
}
// Verify non-matching key still exists
exists, err := client.Exists(ctx, "test:other:1").Result()
assert.NoError(t, err)
assert.Equal(t, int64(1), exists)
}
func TestCacheService_SetUserTracks_Success(t *testing.T) {
service, _ := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
userID := uuid.New()
page := 1
tracks := []map[string]interface{}{
{"id": uuid.New().String(), "title": "Track 1"},
{"id": uuid.New().String(), "title": "Track 2"},
}
config := DefaultCacheConfig()
err := service.SetUserTracks(ctx, userID, page, tracks, config)
assert.NoError(t, err)
// Verify
var retrieved []map[string]interface{}
err = service.GetUserTracks(ctx, userID, page, &retrieved)
assert.NoError(t, err)
assert.Len(t, retrieved, 2)
}
func TestCacheService_DeleteUserTracks_Success(t *testing.T) {
service, _ := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
userID := uuid.New()
page := 1
tracks := []map[string]interface{}{
{"id": uuid.New().String(), "title": "Track 1"},
}
config := DefaultCacheConfig()
// Set
err := service.SetUserTracks(ctx, userID, page, tracks, config)
assert.NoError(t, err)
// Delete
err = service.DeleteUserTracks(ctx, userID)
assert.NoError(t, err)
// Verify deleted
var retrieved []map[string]interface{}
err = service.GetUserTracks(ctx, userID, page, &retrieved)
assert.Error(t, err)
assert.Equal(t, ErrCacheMiss, err)
}
func TestCacheService_SetSearchResults_Success(t *testing.T) {
service, _ := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
query := "test query"
results := map[string]interface{}{
"tracks": []string{"track1", "track2"},
"users": []string{"user1"},
}
config := DefaultCacheConfig()
err := service.SetSearchResults(ctx, query, results, config)
assert.NoError(t, err)
// Verify
var retrieved map[string]interface{}
err = service.GetSearchResults(ctx, query, &retrieved)
assert.NoError(t, err)
assert.NotNil(t, retrieved["tracks"])
}
func TestCacheService_InvalidateUserCache_Success(t *testing.T) {
service, client := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
userID := uuid.New()
config := DefaultCacheConfig()
// Set various user-related cache entries
err := service.SetUser(ctx, userID, map[string]interface{}{"id": userID.String()}, config)
assert.NoError(t, err)
err = service.SetUserTracks(ctx, userID, 1, []map[string]interface{}{}, config)
assert.NoError(t, err)
// Set a user session key manually
err = client.Set(ctx, "user_sessions:"+userID.String()+":session1", "value", 5*time.Minute).Err()
require.NoError(t, err)
// Invalidate
err = service.InvalidateUserCache(ctx, userID)
assert.NoError(t, err)
// Verify user cache deleted
var retrieved map[string]interface{}
err = service.GetUser(ctx, userID, &retrieved)
assert.Error(t, err)
assert.Equal(t, ErrCacheMiss, err)
}
func TestCacheService_InvalidateTrackCache_Success(t *testing.T) {
service, client := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
trackID := uuid.New()
config := DefaultCacheConfig()
// Set track cache
err := service.SetTrack(ctx, trackID, map[string]interface{}{"id": trackID.String()}, config)
assert.NoError(t, err)
// Set search results (should be invalidated)
err = client.Set(ctx, "search:test", "results", 5*time.Minute).Err()
require.NoError(t, err)
// Invalidate
err = service.InvalidateTrackCache(ctx, trackID)
assert.NoError(t, err)
// Verify track cache deleted
var retrieved map[string]interface{}
err = service.GetTrack(ctx, trackID, &retrieved)
assert.Error(t, err)
assert.Equal(t, ErrCacheMiss, err)
}
func TestCacheService_InvalidatePlaylistCache_Success(t *testing.T) {
service, client := setupTestCacheService(t)
if service == nil {
return
}
ctx := context.Background()
playlistID := uuid.New()
config := DefaultCacheConfig()
// Set playlist cache
err := service.SetPlaylist(ctx, playlistID, map[string]interface{}{"id": playlistID.String()}, config)
assert.NoError(t, err)
// Set search results (should be invalidated)
err = client.Set(ctx, "search:test", "results", 5*time.Minute).Err()
require.NoError(t, err)
// Invalidate
err = service.InvalidatePlaylistCache(ctx, playlistID)
assert.NoError(t, err)
// Verify playlist cache deleted
var retrieved map[string]interface{}
err = service.GetPlaylist(ctx, playlistID, &retrieved)
assert.Error(t, err)
assert.Equal(t, ErrCacheMiss, err)
}
func TestCacheService_DefaultCacheConfig(t *testing.T) {
config := DefaultCacheConfig()
assert.NotNil(t, config)
assert.Equal(t, 5*time.Minute, config.DefaultTTL)
assert.Equal(t, 5*time.Minute, config.UserTTL)
assert.Equal(t, 30*time.Minute, config.TrackTTL)
assert.Equal(t, 15*time.Minute, config.PlaylistTTL)
assert.Equal(t, 1*time.Minute, config.RoomTTL)
}