327 lines
12 KiB
Go
327 lines
12 KiB
Go
package semanticcache
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/maximhq/bifrost/core/schemas"
|
|
)
|
|
|
|
// TestCacheNoStoreBasicFunctionality tests that CacheNoStoreKey prevents caching
|
|
func TestCacheNoStoreBasicFunctionality(t *testing.T) {
|
|
setup := NewTestSetup(t)
|
|
defer setup.Cleanup()
|
|
|
|
testRequest := CreateBasicChatRequest("What is artificial intelligence?", 0.7, 100)
|
|
|
|
// Test 1: Normal caching (control test)
|
|
ctx1 := CreateContextWithCacheKey("test-no-store-control")
|
|
t.Log("Making normal request (should be cached)...")
|
|
response1, err1 := setup.Client.ChatCompletionRequest(ctx1, testRequest)
|
|
if err1 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response1}) // Fresh request
|
|
|
|
WaitForCache(setup.Plugin)
|
|
|
|
// Verify it got cached
|
|
t.Log("Verifying normal caching worked...")
|
|
response2, err2 := setup.Client.ChatCompletionRequest(ctx1, testRequest)
|
|
if err2 != nil {
|
|
if err2.Error != nil {
|
|
t.Fatalf("Second request failed: %v", err2.Error.Message)
|
|
} else {
|
|
t.Fatalf("Second request failed: %v", err2)
|
|
}
|
|
}
|
|
AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}, "direct") // Should be cached
|
|
|
|
// Test 2: NoStore = true (should not cache)
|
|
ctx2 := CreateContextWithCacheKeyAndNoStore("test-no-store-disabled", true)
|
|
t.Log("Making request with CacheNoStoreKey=true (should not be cached)...")
|
|
response3, err3 := setup.Client.ChatCompletionRequest(ctx2, testRequest)
|
|
if err3 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response3}) // Fresh request
|
|
|
|
WaitForCache(setup.Plugin)
|
|
|
|
// Verify it was NOT cached
|
|
t.Log("Verifying no-store request was not cached...")
|
|
response4, err4 := setup.Client.ChatCompletionRequest(ctx2, testRequest)
|
|
if err4 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response4}) // Should still be fresh (not cached)
|
|
|
|
// Test 3: NoStore = false (should cache normally)
|
|
ctx3 := CreateContextWithCacheKeyAndNoStore("test-no-store-enabled", false)
|
|
t.Log("Making request with CacheNoStoreKey=false (should be cached)...")
|
|
response5, err5 := setup.Client.ChatCompletionRequest(ctx3, testRequest)
|
|
if err5 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response5}) // Fresh request
|
|
|
|
WaitForCache(setup.Plugin)
|
|
|
|
// Verify it got cached
|
|
t.Log("Verifying no-store=false request was cached...")
|
|
response6, err6 := setup.Client.ChatCompletionRequest(ctx3, testRequest)
|
|
if err6 != nil {
|
|
t.Fatalf("Sixth request failed: %v", err6)
|
|
}
|
|
AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response6}, "direct") // Should be cached
|
|
|
|
t.Log("✅ CacheNoStoreKey basic functionality works correctly")
|
|
}
|
|
|
|
// TestCacheNoStoreWithDifferentRequestTypes tests NoStore with various request types
|
|
func TestCacheNoStoreWithDifferentRequestTypes(t *testing.T) {
|
|
t.Skip("Skipping Embedding Tests")
|
|
|
|
setup := NewTestSetup(t)
|
|
defer setup.Cleanup()
|
|
|
|
// Test with chat completion
|
|
chatRequest := CreateBasicChatRequest("Test no-store with chat", 0.7, 50)
|
|
ctx1 := CreateContextWithCacheKeyAndNoStore("test-no-store-chat", true)
|
|
|
|
t.Log("Testing no-store with chat completion...")
|
|
response1, err1 := setup.Client.ChatCompletionRequest(ctx1, chatRequest)
|
|
if err1 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response1})
|
|
|
|
WaitForCache(setup.Plugin)
|
|
|
|
// Verify not cached
|
|
response2, err2 := setup.Client.ChatCompletionRequest(ctx1, chatRequest)
|
|
if err2 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}) // Should not be cached
|
|
|
|
// Test with embedding request
|
|
embeddingRequest := CreateEmbeddingRequest([]string{"Test no-store with embeddings"})
|
|
ctx2 := CreateContextWithCacheKeyAndNoStore("test-no-store-embedding", true)
|
|
|
|
t.Log("Testing no-store with embedding request...")
|
|
response3, err3 := setup.Client.EmbeddingRequest(ctx2, embeddingRequest)
|
|
if err3 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{EmbeddingResponse: response3})
|
|
|
|
WaitForCache(setup.Plugin)
|
|
|
|
// Verify not cached
|
|
response4, err4 := setup.Client.EmbeddingRequest(ctx2, embeddingRequest)
|
|
if err4 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{EmbeddingResponse: response4}) // Should not be cached
|
|
|
|
t.Log("✅ CacheNoStoreKey works with different request types")
|
|
}
|
|
|
|
// TestCacheNoStoreWithConversationHistory tests NoStore with conversation context
|
|
func TestCacheNoStoreWithConversationHistory(t *testing.T) {
|
|
setup := NewTestSetup(t)
|
|
defer setup.Cleanup()
|
|
|
|
// Create conversation context
|
|
conversation := BuildConversationHistory(
|
|
"You are a helpful assistant",
|
|
[]string{"Hello", "Hi! How can I help?"},
|
|
)
|
|
messages := AddUserMessage(conversation, "What is machine learning?")
|
|
request := CreateConversationRequest(messages, 0.7, 100)
|
|
|
|
// Test with no-store enabled
|
|
ctx := CreateContextWithCacheKeyAndNoStore("test-no-store-conversation", true)
|
|
|
|
t.Log("Testing no-store with conversation history...")
|
|
response1, err1 := setup.Client.ChatCompletionRequest(ctx, request)
|
|
if err1 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response1})
|
|
|
|
WaitForCache(setup.Plugin)
|
|
|
|
// Verify not cached (same conversation should not hit cache)
|
|
response2, err2 := setup.Client.ChatCompletionRequest(ctx, request)
|
|
if err2 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}) // Should not be cached due to no-store
|
|
|
|
t.Log("✅ CacheNoStoreKey works with conversation history")
|
|
}
|
|
|
|
// TestCacheNoStoreWithCacheTypes tests NoStore interaction with CacheTypeKey
|
|
func TestCacheNoStoreWithCacheTypes(t *testing.T) {
|
|
setup := NewTestSetup(t)
|
|
defer setup.Cleanup()
|
|
|
|
testRequest := CreateBasicChatRequest("Test no-store with cache types", 0.7, 50)
|
|
|
|
// Test no-store with direct cache type
|
|
ctx1 := CreateContextWithCacheKey("test-no-store-cache-types")
|
|
ctx1 = ctx1.WithValue(CacheNoStoreKey, true)
|
|
ctx1 = ctx1.WithValue(CacheTypeKey, CacheTypeDirect)
|
|
|
|
t.Log("Testing no-store with CacheTypeKey=direct...")
|
|
response1, err1 := setup.Client.ChatCompletionRequest(ctx1, testRequest)
|
|
if err1 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response1})
|
|
|
|
WaitForCache(setup.Plugin)
|
|
|
|
// Should not be cached
|
|
response2, err2 := setup.Client.ChatCompletionRequest(ctx1, testRequest)
|
|
if err2 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}) // No-store should override cache type
|
|
|
|
// Test no-store with semantic cache type
|
|
ctx2 := CreateContextWithCacheKey("test-no-store-cache-types")
|
|
ctx2 = ctx2.WithValue(CacheNoStoreKey, true)
|
|
ctx2 = ctx2.WithValue(CacheTypeKey, CacheTypeSemantic)
|
|
|
|
t.Log("Testing no-store with CacheTypeKey=semantic...")
|
|
response3, err3 := setup.Client.ChatCompletionRequest(ctx2, testRequest)
|
|
if err3 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response3})
|
|
|
|
WaitForCache(setup.Plugin)
|
|
|
|
// Should not be cached
|
|
response4, err4 := setup.Client.ChatCompletionRequest(ctx2, testRequest)
|
|
if err4 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response4}) // No-store should override cache type
|
|
|
|
t.Log("✅ CacheNoStoreKey correctly overrides cache type settings")
|
|
}
|
|
|
|
// TestCacheNoStoreErrorHandling tests error scenarios with NoStore
|
|
func TestCacheNoStoreErrorHandling(t *testing.T) {
|
|
setup := NewTestSetup(t)
|
|
defer setup.Cleanup()
|
|
|
|
testRequest := CreateBasicChatRequest("Test no-store error handling", 0.7, 50)
|
|
|
|
// Test with invalid no-store value (non-boolean)
|
|
ctx1 := CreateContextWithCacheKey("test-no-store-errors")
|
|
ctx1 = ctx1.WithValue(CacheNoStoreKey, "invalid")
|
|
|
|
t.Log("Testing no-store with invalid value (should cache normally)...")
|
|
response1, err1 := setup.Client.ChatCompletionRequest(ctx1, testRequest)
|
|
if err1 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response1})
|
|
|
|
WaitForCache(setup.Plugin)
|
|
|
|
// Should be cached (invalid value should be ignored)
|
|
response2, err2 := setup.Client.ChatCompletionRequest(ctx1, testRequest)
|
|
if err2 != nil {
|
|
if err2.Error != nil {
|
|
t.Fatalf("Second request failed: %v", err2.Error.Message)
|
|
} else {
|
|
t.Fatalf("Second request failed: %v", err2)
|
|
}
|
|
}
|
|
AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}, "direct") // Should be cached (invalid value ignored)
|
|
|
|
// Test with nil value (should cache normally)
|
|
ctx2 := CreateContextWithCacheKey("test-no-store-nil")
|
|
ctx2 = ctx2.WithValue(CacheNoStoreKey, nil)
|
|
|
|
t.Log("Testing no-store with nil value (should cache normally)...")
|
|
response3, err3 := setup.Client.ChatCompletionRequest(ctx2, testRequest)
|
|
if err3 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response3})
|
|
|
|
WaitForCache(setup.Plugin)
|
|
|
|
// Should be cached (nil should be treated as normal caching)
|
|
response4, err4 := setup.Client.ChatCompletionRequest(ctx2, testRequest)
|
|
if err4 != nil {
|
|
t.Fatalf("Fourth request failed: %v", err4)
|
|
}
|
|
AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response4}, "direct") // Should be cached (nil ignored)
|
|
|
|
t.Log("✅ CacheNoStoreKey error handling works correctly")
|
|
}
|
|
|
|
// TestCacheNoStoreReadButNoWrite tests that NoStore allows reading cache but prevents writing
|
|
func TestCacheNoStoreReadButNoWrite(t *testing.T) {
|
|
setup := NewTestSetup(t)
|
|
defer setup.Cleanup()
|
|
|
|
testRequest := CreateBasicChatRequest("Describe Isaac Newton's three laws of motion", 0.7, 50)
|
|
|
|
// Step 1: Cache a response normally
|
|
ctx1 := CreateContextWithCacheKey("test-no-store-read")
|
|
t.Log("Caching response normally...")
|
|
response1, err1 := setup.Client.ChatCompletionRequest(ctx1, testRequest)
|
|
if err1 != nil {
|
|
return // Test will be skipped by retry function
|
|
}
|
|
AssertNoCacheHit(t, &schemas.BifrostResponse{ChatResponse: response1})
|
|
|
|
WaitForCache(setup.Plugin)
|
|
|
|
// Step 2: Try to read with no-store enabled (should still read from cache)
|
|
ctx2 := CreateContextWithCacheKeyAndNoStore("test-no-store-read", true)
|
|
t.Log("Reading with no-store enabled (should still hit cache for reads)...")
|
|
response2, err2 := setup.Client.ChatCompletionRequest(ctx2, testRequest)
|
|
if err2 != nil {
|
|
if err2.Error != nil {
|
|
t.Fatalf("Second request failed: %v", err2.Error.Message)
|
|
} else {
|
|
t.Fatalf("Second request failed: %v", err2)
|
|
}
|
|
}
|
|
// The current implementation should still read from cache even with no-store
|
|
// (no-store only affects writing, not reading)
|
|
AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response2}, "direct")
|
|
|
|
// Step 3: Make a semantically similar request with no-store (strong paraphrase for deterministic semantic hit)
|
|
newRequest := CreateBasicChatRequest("Describe the three laws of motion by Isaac Newton", 0.7, 50)
|
|
t.Log("Making semantically similar request with no-store (should get semantic hit, but not cache response)...")
|
|
response3, err3 := setup.Client.ChatCompletionRequest(ctx2, newRequest)
|
|
if err3 != nil {
|
|
t.Fatalf("Third request failed: %v", err3)
|
|
}
|
|
// Should get semantic cache hit (no-store allows reads, just prevents writes)
|
|
AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response3}, "semantic")
|
|
|
|
WaitForCache(setup.Plugin)
|
|
|
|
// Step 4: Repeat similar request with no-store (should still get semantic hit)
|
|
t.Log("Repeating similar request with no-store (should still get semantic hit)...")
|
|
response4, err4 := setup.Client.ChatCompletionRequest(ctx2, newRequest)
|
|
if err4 != nil {
|
|
t.Fatalf("Fourth request failed: %v", err4)
|
|
}
|
|
// Should get semantic cache hit again (consistent behavior)
|
|
AssertCacheHit(t, &schemas.BifrostResponse{ChatResponse: response4}, "semantic")
|
|
|
|
t.Log("✅ CacheNoStoreKey allows reading but prevents writing")
|
|
}
|