Files
pikasTech-HWLAB/internal/workbenchruntime/cache_test.go
T

103 lines
3.2 KiB
Go

package workbenchruntime
import (
"context"
"errors"
"strings"
"testing"
"github.com/redis/go-redis/v9"
)
func TestBuildCacheKeyActorIsolationAndAuthority(t *testing.T) {
base := cacheKeyParts{Class: "sessions.summary", ActorID: "actor-a", SessionID: "ses-1", TraceID: "trace-1", Cursor: "idx:0", ProjectionSeq: 42}
keyA, err := buildCacheKey(512, base)
if err != nil {
t.Fatalf("build key actor a: %v", err)
}
base.ActorID = "actor-b"
keyB, err := buildCacheKey(512, base)
if err != nil {
t.Fatalf("build key actor b: %v", err)
}
if keyA == keyB {
t.Fatalf("expected actor-isolated keys, got identical key %q", keyA)
}
if !strings.Contains(keyA, cacheKeySchemaVersion) || !strings.Contains(keyA, "class=sessions.summary") {
t.Fatalf("key missing schema/class: %s", keyA)
}
if strings.Contains(keyA, "actor-a") || strings.Contains(keyB, "actor-b") {
t.Fatalf("cache key should hash raw actor ids: %s / %s", keyA, keyB)
}
_, err = buildCacheKey(512, cacheKeyParts{Class: "sessions.summary", ActorID: "actor-a"})
if cacheErrKind(err) != "cache_key_invalid" {
t.Fatalf("expected missing authority to be rejected, got %v", err)
}
_, err = buildCacheKey(512, cacheKeyParts{Class: "sessions.summary", ProjectionSeq: 1})
if cacheErrKind(err) != "cache_key_invalid" {
t.Fatalf("expected missing actor to be rejected, got %v", err)
}
}
func TestCachePayloadBoundary(t *testing.T) {
_, err := cachePayloadBytes(map[string]any{"sessionId": "ses-1", "summary": map[string]any{"messageCount": 2}}, 1024)
if err != nil {
t.Fatalf("safe payload rejected: %v", err)
}
for _, payload := range []map[string]any{
{"authorization": "bearer secret"},
{"session": map[string]any{"prompt": "full prompt text"}},
{"providerPayload": map[string]any{"raw": true}},
{"stdout": strings.Repeat("x", 16)},
} {
_, err := cachePayloadBytes(payload, 1024)
if cacheErrKind(err) != "cache_payload_rejected" {
t.Fatalf("expected payload rejection for %#v, got %v", payload, err)
}
}
_, err = cachePayloadBytes(map[string]any{"sessionId": strings.Repeat("x", 64)}, 16)
if cacheErrKind(err) != "cache_payload_too_large" {
t.Fatalf("expected payload too large, got %v", err)
}
}
func TestCacheErrorClassification(t *testing.T) {
cases := []struct {
err error
kind string
}{
{redis.Nil, "cache_miss"},
{context.DeadlineExceeded, "cache_timeout"},
{context.Canceled, "cache_context_canceled"},
{errors.New("dial tcp 127.0.0.1:6379: connect: connection refused"), "cache_unavailable"},
}
for _, tc := range cases {
if got := classifyCacheError(tc.err); got != tc.kind {
t.Fatalf("classifyCacheError(%v)=%s, want %s", tc.err, got, tc.kind)
}
}
}
func TestDisabledCacheDoesNotFailReadPath(t *testing.T) {
cache := newDerivedCache(cacheConfig{Enabled: false})
hit, err := cache.Get(context.Background(), "ignored", &map[string]any{})
if err != nil || hit {
t.Fatalf("disabled cache should be transparent, hit=%v err=%v", hit, err)
}
diag := cache.Diagnostic(context.Background())
if diag["enabled"] != false || diag["available"] != false {
t.Fatalf("unexpected disabled diagnostic: %#v", diag)
}
}
func cacheErrKind(err error) string {
var cacheErr cacheError
if errors.As(err, &cacheErr) {
return cacheErr.Kind
}
return classifyCacheError(err)
}