Files
go-hauk/store/redis_test.go
2025-12-24 15:26:25 +01:00

210 lines
4.9 KiB
Go

package store
import (
"context"
"os"
"testing"
"time"
)
func getRedisAddr() string {
if addr := os.Getenv("TEST_REDIS_ADDR"); addr != "" {
return addr
}
return "localhost:16379"
}
func TestRedisStore(t *testing.T) {
ctx := context.Background()
r, err := NewRedis(getRedisAddr(), "", "hauk-test")
if err != nil {
t.Skipf("redis not available: %v", err)
}
defer r.Close()
t.Run("set and get", func(t *testing.T) {
data := map[string]string{"foo": "bar"}
err := r.Set(ctx, "test1", data, time.Now().Add(time.Hour))
if err != nil {
t.Fatalf("set failed: %v", err)
}
var result map[string]string
err = r.Get(ctx, "test1", &result)
if err != nil {
t.Fatalf("get failed: %v", err)
}
if result["foo"] != "bar" {
t.Errorf("expected bar, got: %s", result["foo"])
}
})
t.Run("get nonexistent", func(t *testing.T) {
var result string
err := r.Get(ctx, "nonexistent-redis-key", &result)
if err == nil {
t.Error("expected error for nonexistent key")
}
})
t.Run("exists", func(t *testing.T) {
r.Set(ctx, "exists-test", "value", time.Now().Add(time.Hour))
exists, err := r.Exists(ctx, "exists-test")
if err != nil {
t.Fatalf("exists failed: %v", err)
}
if !exists {
t.Error("expected exists to be true")
}
exists, err = r.Exists(ctx, "nonexistent-key")
if err != nil {
t.Fatalf("exists failed: %v", err)
}
if exists {
t.Error("expected exists to be false")
}
})
t.Run("delete", func(t *testing.T) {
r.Set(ctx, "delete-test", "value", time.Now().Add(time.Hour))
err := r.Delete(ctx, "delete-test")
if err != nil {
t.Fatalf("delete failed: %v", err)
}
exists, _ := r.Exists(ctx, "delete-test")
if exists {
t.Error("expected key to be deleted")
}
})
t.Run("expiration", func(t *testing.T) {
// set with 1s TTL and poll for expiration
r.SetTTL(ctx, "ttl-expire-test", "value", time.Second)
// poll for expiration with timeout
deadline := time.Now().Add(3 * time.Second)
for time.Now().Before(deadline) {
exists, _ := r.Exists(ctx, "ttl-expire-test")
if !exists {
return // success
}
time.Sleep(100 * time.Millisecond)
}
t.Error("key did not expire within timeout")
})
t.Run("complex types", func(t *testing.T) {
type nested struct {
Points [][]float64 `json:"points"`
Name string `json:"name"`
}
data := nested{
Points: [][]float64{{1.5, 2.5}, {3.5, 4.5}},
Name: "test",
}
err := r.Set(ctx, "complex-redis", data, time.Now().Add(time.Hour))
if err != nil {
t.Fatalf("set failed: %v", err)
}
var result nested
err = r.Get(ctx, "complex-redis", &result)
if err != nil {
t.Fatalf("get failed: %v", err)
}
if result.Name != "test" {
t.Errorf("expected test, got: %s", result.Name)
}
if len(result.Points) != 2 {
t.Errorf("expected 2 points, got: %d", len(result.Points))
}
})
t.Run("key prefixing", func(t *testing.T) {
// keys should be prefixed
r.Set(ctx, "prefix-test", "value", time.Now().Add(time.Hour))
// the internal key should be "hauk-test-prefix-test"
exists, _ := r.Exists(ctx, "prefix-test")
if !exists {
t.Error("expected prefixed key to exist")
}
})
}
func TestRedisIntegration(t *testing.T) {
ctx := context.Background()
r, err := NewRedis(getRedisAddr(), "", "hauk-integration")
if err != nil {
t.Skipf("redis not available: %v", err)
}
defer r.Close()
// test full session lifecycle like the API does
type sessionData struct {
ID string `json:"id"`
Targets []string `json:"targets"`
Interval float64 `json:"interval"`
}
t.Run("session lifecycle", func(t *testing.T) {
session := sessionData{
ID: "test-session-123",
Targets: []string{"share-abc"},
Interval: 5.0,
}
// save session
err := r.Set(ctx, "session-"+session.ID, session, time.Now().Add(time.Hour))
if err != nil {
t.Fatalf("save session failed: %v", err)
}
// load session
var loaded sessionData
err = r.Get(ctx, "session-"+session.ID, &loaded)
if err != nil {
t.Fatalf("load session failed: %v", err)
}
if loaded.ID != session.ID {
t.Errorf("ID mismatch: %s vs %s", loaded.ID, session.ID)
}
if len(loaded.Targets) != 1 || loaded.Targets[0] != "share-abc" {
t.Errorf("targets mismatch: %v", loaded.Targets)
}
// update session
loaded.Targets = append(loaded.Targets, "share-def")
err = r.Set(ctx, "session-"+loaded.ID, loaded, time.Now().Add(time.Hour))
if err != nil {
t.Fatalf("update session failed: %v", err)
}
// verify update
var updated sessionData
r.Get(ctx, "session-"+loaded.ID, &updated)
if len(updated.Targets) != 2 {
t.Errorf("expected 2 targets after update, got: %d", len(updated.Targets))
}
// delete session
err = r.Delete(ctx, "session-"+loaded.ID)
if err != nil {
t.Fatalf("delete session failed: %v", err)
}
exists, _ := r.Exists(ctx, "session-"+loaded.ID)
if exists {
t.Error("session should be deleted")
}
})
}