Files
bifrost/framework/logstore/rdb_perf_test.go
Beyhan Oğur 880f412e2c first commit
2026-04-26 21:52:23 +03:00

261 lines
6.7 KiB
Go

package logstore
import (
"context"
"path/filepath"
"reflect"
"testing"
"time"
"github.com/maximhq/bifrost/core/schemas"
)
type testLogger struct{}
func (testLogger) Debug(string, ...any) {}
func (testLogger) Info(string, ...any) {}
func (testLogger) Warn(string, ...any) {}
func (testLogger) Error(string, ...any) {}
func (testLogger) Fatal(string, ...any) {}
func (testLogger) SetLevel(schemas.LogLevel) {}
func (testLogger) SetOutputType(schemas.LoggerOutputType) {}
func (testLogger) LogHTTPRequest(schemas.LogLevel, string) schemas.LogEventBuilder {
return schemas.NoopLogEvent
}
func newTestSQLiteStore(t *testing.T) *RDBLogStore {
t.Helper()
store, err := newSqliteLogStore(context.Background(), &SQLiteConfig{
Path: filepath.Join(t.TempDir(), "logs.db"),
}, testLogger{})
if err != nil {
t.Fatalf("newSqliteLogStore() error = %v", err)
}
return store
}
func TestLogCreateSerializesFields(t *testing.T) {
store := newTestSQLiteStore(t)
prompt := "hello"
reply := "world"
entry := &Log{
ID: "log-1",
Timestamp: time.Now().UTC(),
Object: "chat_completion",
Provider: "openai",
Model: "gpt-4o-mini",
Status: "success",
InputHistoryParsed: []schemas.ChatMessage{{
Role: schemas.ChatMessageRoleUser,
Content: &schemas.ChatMessageContent{
ContentStr: &prompt,
},
}},
OutputMessageParsed: &schemas.ChatMessage{
Role: schemas.ChatMessageRoleAssistant,
Content: &schemas.ChatMessageContent{
ContentStr: &reply,
},
},
}
if err := store.Create(context.Background(), entry); err != nil {
t.Fatalf("Create() error = %v", err)
}
logEntry, err := store.FindByID(context.Background(), entry.ID)
if err != nil {
t.Fatalf("FindByID() error = %v", err)
}
if logEntry.InputHistory == "" {
t.Fatalf("expected InputHistory to be serialized")
}
if logEntry.OutputMessage == "" {
t.Fatalf("expected OutputMessage to be serialized")
}
if logEntry.ContentSummary == "" {
t.Fatalf("expected ContentSummary to be populated")
}
if logEntry.CreatedAt.IsZero() {
t.Fatalf("expected CreatedAt to be populated")
}
}
func TestMCPToolLogCreateSerializesFields(t *testing.T) {
store := newTestSQLiteStore(t)
entry := &MCPToolLog{
ID: "mcp-1",
Timestamp: time.Now().UTC(),
ToolName: "echo",
Status: "success",
ArgumentsParsed: map[string]any{
"message": "hello",
},
ResultParsed: map[string]any{
"ok": true,
},
}
if err := store.CreateMCPToolLog(context.Background(), entry); err != nil {
t.Fatalf("CreateMCPToolLog() error = %v", err)
}
logEntry, err := store.FindMCPToolLog(context.Background(), entry.ID)
if err != nil {
t.Fatalf("FindMCPToolLog() error = %v", err)
}
if logEntry.Arguments == "" {
t.Fatalf("expected Arguments to be serialized")
}
if logEntry.Result == "" {
t.Fatalf("expected Result to be serialized")
}
}
func TestBuildBulkUpdateCostPostgresSQL(t *testing.T) {
updates := map[string]float64{
"log-a": 1.25,
"log-b": 2.5,
}
query, args := buildBulkUpdateCostPostgresSQL([]string{"log-a", "log-b"}, updates)
wantQuery := "UPDATE logs SET cost = v.cost FROM (VALUES ($1::text,$2::float8),($3::text,$4::float8)) AS v(id, cost) WHERE logs.id = v.id"
wantArgs := []interface{}{"log-a", 1.25, "log-b", 2.5}
if query != wantQuery {
t.Fatalf("query mismatch\n got: %s\nwant: %s", query, wantQuery)
}
if !reflect.DeepEqual(args, wantArgs) {
t.Fatalf("args mismatch\n got: %#v\nwant: %#v", args, wantArgs)
}
}
func TestUpdateSerializesStructEntry(t *testing.T) {
store := newTestSQLiteStore(t)
now := time.Now().UTC()
entry := &Log{
ID: "log-update",
Timestamp: now,
Object: "chat_completion",
Provider: "openai",
Model: "gpt-4o-mini",
Status: "processing",
}
if err := store.Create(context.Background(), entry); err != nil {
t.Fatalf("Create() error = %v", err)
}
reply := "updated response"
if err := store.Update(context.Background(), entry.ID, Log{
Status: "success",
OutputMessageParsed: &schemas.ChatMessage{
Role: schemas.ChatMessageRoleAssistant,
Content: &schemas.ChatMessageContent{
ContentStr: &reply,
},
},
TokenUsageParsed: &schemas.BifrostLLMUsage{
PromptTokens: 3,
CompletionTokens: 7,
TotalTokens: 10,
},
}); err != nil {
t.Fatalf("Update() error = %v", err)
}
logEntry, err := store.FindByID(context.Background(), entry.ID)
if err != nil {
t.Fatalf("FindByID() error = %v", err)
}
if logEntry.OutputMessage == "" {
t.Fatalf("expected OutputMessage to be serialized on Update")
}
if logEntry.TokenUsage == "" {
t.Fatalf("expected TokenUsage to be serialized on Update")
}
if logEntry.TotalTokens != 10 {
t.Fatalf("expected TotalTokens to be updated, got %d", logEntry.TotalTokens)
}
}
func TestUpdateMCPToolLogSerializesStructEntry(t *testing.T) {
store := newTestSQLiteStore(t)
now := time.Now().UTC()
entry := &MCPToolLog{
ID: "mcp-update",
Timestamp: now,
ToolName: "echo",
Status: "processing",
}
if err := store.CreateMCPToolLog(context.Background(), entry); err != nil {
t.Fatalf("CreateMCPToolLog() error = %v", err)
}
if err := store.UpdateMCPToolLog(context.Background(), entry.ID, MCPToolLog{
Status: "success",
ResultParsed: map[string]any{
"message": "done",
},
}); err != nil {
t.Fatalf("UpdateMCPToolLog() error = %v", err)
}
logEntry, err := store.FindMCPToolLog(context.Background(), entry.ID)
if err != nil {
t.Fatalf("FindMCPToolLog() error = %v", err)
}
if logEntry.Result == "" {
t.Fatalf("expected Result to be serialized on UpdateMCPToolLog")
}
}
func TestBulkUpdateCostSQLiteFallback(t *testing.T) {
store := newTestSQLiteStore(t)
now := time.Now().UTC()
entries := []*Log{
{
ID: "log-a",
Timestamp: now,
Object: "chat_completion",
Provider: "openai",
Model: "gpt-4o-mini",
Status: "success",
},
{
ID: "log-b",
Timestamp: now,
Object: "chat_completion",
Provider: "openai",
Model: "gpt-4o-mini",
Status: "success",
},
}
for _, entry := range entries {
if err := store.Create(context.Background(), entry); err != nil {
t.Fatalf("Create(%s) error = %v", entry.ID, err)
}
}
if err := store.BulkUpdateCost(context.Background(), map[string]float64{
"log-a": 1.5,
"log-b": 2.5,
}); err != nil {
t.Fatalf("BulkUpdateCost() error = %v", err)
}
for id, wantCost := range map[string]float64{"log-a": 1.5, "log-b": 2.5} {
logEntry, err := store.FindByID(context.Background(), id)
if err != nil {
t.Fatalf("FindByID(%s) error = %v", id, err)
}
if logEntry.Cost == nil || *logEntry.Cost != wantCost {
t.Fatalf("cost mismatch for %s: got %v want %v", id, logEntry.Cost, wantCost)
}
}
}