package helper import ( "context" "encoding/json" "testing" "time" "authentication/redisclient" "github.com/alicebob/miniredis/v2" "github.com/redis/go-redis/v9" ) // setupTestRedis creates a mock Redis server for testing func setupTestRedis(t *testing.T) (*miniredis.Miniredis, func()) { mr, err := miniredis.Run() if err != nil { t.Fatalf("Failed to start miniredis: %v", err) } // Save original client originalRDB := redisclient.RDB // Create test client redisclient.RDB = redis.NewClient(&redis.Options{ Addr: mr.Addr(), }) cleanup := func() { redisclient.RDB.Close() redisclient.RDB = originalRDB mr.Close() } return mr, cleanup } func TestSetJSON(t *testing.T) { mr, cleanup := setupTestRedis(t) defer cleanup() ctx := context.Background() testData := map[string]interface{}{ "name": "Test User", "email": "test@example.com", "age": 30, } // Test with default TTL err := SetJSON(ctx, "test:user:1", testData, nil) if err != nil { t.Errorf("Expected no error, got: %v", err) } // Verify data was set val, err := redisclient.RDB.Get(ctx, "test:user:1").Result() if err != nil { t.Errorf("Failed to get value: %v", err) } var retrieved map[string]interface{} err = json.Unmarshal([]byte(val), &retrieved) if err != nil { t.Errorf("Failed to unmarshal: %v", err) } if retrieved["name"] != testData["name"] { t.Errorf("Expected name '%v', got '%v'", testData["name"], retrieved["name"]) } // Verify TTL was set (miniredis returns TTL) ttl := mr.TTL("test:user:1") if ttl <= 0 { t.Error("Expected TTL to be set") } } func TestSetJSON_CustomTTL(t *testing.T) { _, cleanup := setupTestRedis(t) defer cleanup() ctx := context.Background() testData := map[string]string{"key": "value"} customTTL := 120 err := SetJSON(ctx, "test:custom:ttl", testData, &customTTL) if err != nil { t.Errorf("Expected no error, got: %v", err) } // Verify data was set val, err := redisclient.RDB.Get(ctx, "test:custom:ttl").Result() if err != nil { t.Errorf("Failed to get value: %v", err) } if val == "" { t.Error("Expected value to be set") } } func TestSlotSetJSON(t *testing.T) { _, cleanup := setupTestRedis(t) defer cleanup() ctx := context.Background() testData := map[string]int{"count": 42} // Test with no TTL err := SlotSetJSON(ctx, "test:slot:1", testData, nil) if err != nil { t.Errorf("Expected no error, got: %v", err) } // Verify data was set val, err := redisclient.RDB.Get(ctx, "test:slot:1").Result() if err != nil { t.Errorf("Failed to get value: %v", err) } var retrieved map[string]int err = json.Unmarshal([]byte(val), &retrieved) if err != nil { t.Errorf("Failed to unmarshal: %v", err) } if retrieved["count"] != testData["count"] { t.Errorf("Expected count %d, got %d", testData["count"], retrieved["count"]) } } func TestSlotSetJSON_WithTTL(t *testing.T) { _, cleanup := setupTestRedis(t) defer cleanup() ctx := context.Background() testData := []string{"item1", "item2"} ttl := 300 err := SlotSetJSON(ctx, "test:slot:ttl", testData, &ttl) if err != nil { t.Errorf("Expected no error, got: %v", err) } // Verify data exists exists, err := redisclient.RDB.Exists(ctx, "test:slot:ttl").Result() if err != nil { t.Errorf("Failed to check existence: %v", err) } if exists != 1 { t.Error("Expected key to exist") } } func TestGetJSON(t *testing.T) { _, cleanup := setupTestRedis(t) defer cleanup() ctx := context.Background() type TestStruct struct { Name string `json:"name"` Email string `json:"email"` Age int `json:"age"` } original := TestStruct{ Name: "John Doe", Email: "john@example.com", Age: 25, } // Set data err := SetJSON(ctx, "test:user:get", original, nil) if err != nil { t.Fatalf("Failed to set JSON: %v", err) } // Get data var retrieved TestStruct err = GetJSON(ctx, "test:user:get", &retrieved) if err != nil { t.Errorf("Expected no error, got: %v", err) } if retrieved.Name != original.Name { t.Errorf("Expected name '%s', got '%s'", original.Name, retrieved.Name) } if retrieved.Email != original.Email { t.Errorf("Expected email '%s', got '%s'", original.Email, retrieved.Email) } if retrieved.Age != original.Age { t.Errorf("Expected age %d, got %d", original.Age, retrieved.Age) } } func TestGetJSON_NonExistentKey(t *testing.T) { _, cleanup := setupTestRedis(t) defer cleanup() ctx := context.Background() var result map[string]string err := GetJSON(ctx, "test:nonexistent", &result) if err == nil { t.Error("Expected error for nonexistent key") } } func TestGetJSON_InvalidJSON(t *testing.T) { _, cleanup := setupTestRedis(t) defer cleanup() ctx := context.Background() // Set invalid JSON err := redisclient.RDB.Set(ctx, "test:invalid", "not valid json", time.Minute).Err() if err != nil { t.Fatalf("Failed to set invalid data: %v", err) } var result map[string]string err = GetJSON(ctx, "test:invalid", &result) if err == nil { t.Error("Expected error for invalid JSON") } } func TestSetTTL(t *testing.T) { mr, cleanup := setupTestRedis(t) defer cleanup() ctx := context.Background() // Set initial data err := redisclient.RDB.Set(ctx, "test:ttl:key", "value", 0).Err() if err != nil { t.Fatalf("Failed to set initial data: %v", err) } // Update TTL ttl := 300 err = SetTTL(ctx, "test:ttl:key", &ttl) if err != nil { t.Errorf("Expected no error, got: %v", err) } // Verify TTL was set actualTTL := mr.TTL("test:ttl:key") if actualTTL <= 0 { t.Error("Expected TTL to be set") } } func TestSetTTL_DefaultTTL(t *testing.T) { mr, cleanup := setupTestRedis(t) defer cleanup() ctx := context.Background() // Set initial data err := redisclient.RDB.Set(ctx, "test:ttl:default", "value", 0).Err() if err != nil { t.Fatalf("Failed to set initial data: %v", err) } // Set default TTL err = SetTTL(ctx, "test:ttl:default", nil) if err != nil { t.Errorf("Expected no error, got: %v", err) } // Verify TTL was set actualTTL := mr.TTL("test:ttl:default") if actualTTL <= 0 { t.Error("Expected default TTL to be set") } } func TestSetTTL_NonExistentKey(t *testing.T) { _, cleanup := setupTestRedis(t) defer cleanup() ctx := context.Background() err := SetTTL(ctx, "test:ttl:nonexistent", nil) if err == nil { t.Error("Expected error for nonexistent key") } expectedMsg := "failed to set TTL: key does not exist" if err.Error() != expectedMsg { t.Errorf("Expected error '%s', got '%s'", expectedMsg, err.Error()) } } func TestSetJSON_MarshalError(t *testing.T) { _, cleanup := setupTestRedis(t) defer cleanup() ctx := context.Background() // Channel cannot be marshaled to JSON invalidData := make(chan int) err := SetJSON(ctx, "test:invalid", invalidData, nil) if err == nil { t.Error("Expected error for unmarshalable data") } } func TestDefaultRedisTTLSeconds(t *testing.T) { if defaultRedisTTLSeconds != 60 { t.Errorf("Expected default TTL 60 seconds, got %d", defaultRedisTTLSeconds) } } func TestSetJSON_RoundTrip(t *testing.T) { _, cleanup := setupTestRedis(t) defer cleanup() ctx := context.Background() type ComplexStruct struct { ID string `json:"id"` Name string `json:"name"` Tags []string `json:"tags"` Metadata map[string]interface{} `json:"metadata"` Active bool `json:"active"` } original := ComplexStruct{ ID: "123", Name: "Test", Tags: []string{"tag1", "tag2"}, Metadata: map[string]interface{}{"key": "value", "count": 5}, Active: true, } // Set err := SetJSON(ctx, "test:complex", original, nil) if err != nil { t.Fatalf("Failed to set: %v", err) } // Get var retrieved ComplexStruct err = GetJSON(ctx, "test:complex", &retrieved) if err != nil { t.Fatalf("Failed to get: %v", err) } // Verify if retrieved.ID != original.ID { t.Errorf("ID mismatch") } if retrieved.Name != original.Name { t.Errorf("Name mismatch") } if len(retrieved.Tags) != len(original.Tags) { t.Errorf("Tags length mismatch") } if retrieved.Active != original.Active { t.Errorf("Active mismatch") } } func BenchmarkSetJSON(b *testing.B) { mr, err := miniredis.Run() if err != nil { b.Fatalf("Failed to start miniredis: %v", err) } defer mr.Close() originalRDB := redisclient.RDB redisclient.RDB = redis.NewClient(&redis.Options{Addr: mr.Addr()}) defer func() { redisclient.RDB.Close() redisclient.RDB = originalRDB }() ctx := context.Background() testData := map[string]string{"key": "value"} b.ResetTimer() for i := 0; i < b.N; i++ { SetJSON(ctx, "bench:key", testData, nil) } } func BenchmarkGetJSON(b *testing.B) { mr, err := miniredis.Run() if err != nil { b.Fatalf("Failed to start miniredis: %v", err) } defer mr.Close() originalRDB := redisclient.RDB redisclient.RDB = redis.NewClient(&redis.Options{Addr: mr.Addr()}) defer func() { redisclient.RDB.Close() redisclient.RDB = originalRDB }() ctx := context.Background() testData := map[string]string{"key": "value"} SetJSON(ctx, "bench:key", testData, nil) b.ResetTimer() var result map[string]string for i := 0; i < b.N; i++ { GetJSON(ctx, "bench:key", &result) } }