Files
latticelm/internal/conversation/testing.go
A8065384 1e0bb0be8c Add comprehensive test coverage improvements
Improved overall test coverage from 37.9% to 51.0% (+13.1 percentage points)

New test files:
- internal/observability/metrics_test.go (18 test functions)
- internal/observability/tracing_test.go (11 test functions)
- internal/observability/provider_wrapper_test.go (12 test functions)
- internal/conversation/sql_store_test.go (16 test functions)
- internal/conversation/redis_store_test.go (15 test functions)

Test helper utilities:
- internal/observability/testing.go
- internal/conversation/testing.go

Coverage improvements by package:
- internal/conversation: 0% → 66.0% (+66.0%)
- internal/observability: 0% → 34.5% (+34.5%)

Test infrastructure:
- Added miniredis/v2 for Redis store testing
- Added prometheus/testutil for metrics testing

Total: ~2,000 lines of test code, 72 new test functions

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-05 17:58:03 +00:00

173 lines
3.9 KiB
Go

package conversation
import (
"context"
"database/sql"
"fmt"
"testing"
"time"
"github.com/alicebob/miniredis/v2"
_ "github.com/mattn/go-sqlite3"
"github.com/redis/go-redis/v9"
"github.com/ajac-zero/latticelm/internal/api"
)
// SetupTestDB creates an in-memory SQLite database for testing
func SetupTestDB(t *testing.T, driver string) *sql.DB {
t.Helper()
var dsn string
switch driver {
case "sqlite3":
// Use in-memory SQLite database
dsn = ":memory:"
case "postgres":
// For postgres tests, use a mock or skip
t.Skip("PostgreSQL tests require external database")
return nil
case "mysql":
// For mysql tests, use a mock or skip
t.Skip("MySQL tests require external database")
return nil
default:
t.Fatalf("unsupported driver: %s", driver)
return nil
}
db, err := sql.Open(driver, dsn)
if err != nil {
t.Fatalf("failed to open database: %v", err)
}
// Create the conversations table
schema := `
CREATE TABLE IF NOT EXISTS conversations (
conversation_id TEXT PRIMARY KEY,
messages TEXT NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`
if _, err := db.Exec(schema); err != nil {
db.Close()
t.Fatalf("failed to create schema: %v", err)
}
return db
}
// SetupTestRedis creates a miniredis instance for testing
func SetupTestRedis(t *testing.T) (*redis.Client, *miniredis.Miniredis) {
t.Helper()
mr := miniredis.RunT(t)
client := redis.NewClient(&redis.Options{
Addr: mr.Addr(),
})
// Test connection
ctx := context.Background()
if err := client.Ping(ctx).Err(); err != nil {
t.Fatalf("failed to connect to miniredis: %v", err)
}
return client, mr
}
// CreateTestMessages generates test message fixtures
func CreateTestMessages(count int) []api.Message {
messages := make([]api.Message, count)
for i := 0; i < count; i++ {
role := "user"
if i%2 == 1 {
role = "assistant"
}
messages[i] = api.Message{
Role: role,
Content: []api.ContentBlock{
{
Type: "text",
Text: fmt.Sprintf("Test message %d", i+1),
},
},
}
}
return messages
}
// CreateTestConversation creates a test conversation with the given ID and messages
func CreateTestConversation(conversationID string, messageCount int) *Conversation {
return &Conversation{
ID: conversationID,
Messages: CreateTestMessages(messageCount),
Model: "test-model",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
}
// MockStore is a simple in-memory store for testing
type MockStore struct {
conversations map[string]*Conversation
getCalled bool
createCalled bool
appendCalled bool
deleteCalled bool
sizeCalled bool
}
func NewMockStore() *MockStore {
return &MockStore{
conversations: make(map[string]*Conversation),
}
}
func (m *MockStore) Get(ctx context.Context, conversationID string) (*Conversation, error) {
m.getCalled = true
conv, ok := m.conversations[conversationID]
if !ok {
return nil, fmt.Errorf("conversation not found")
}
return conv, nil
}
func (m *MockStore) Create(ctx context.Context, conversationID string, model string, messages []api.Message) (*Conversation, error) {
m.createCalled = true
m.conversations[conversationID] = &Conversation{
ID: conversationID,
Model: model,
Messages: messages,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
return m.conversations[conversationID], nil
}
func (m *MockStore) Append(ctx context.Context, conversationID string, messages ...api.Message) (*Conversation, error) {
m.appendCalled = true
conv, ok := m.conversations[conversationID]
if !ok {
return nil, fmt.Errorf("conversation not found")
}
conv.Messages = append(conv.Messages, messages...)
conv.UpdatedAt = time.Now()
return conv, nil
}
func (m *MockStore) Delete(ctx context.Context, conversationID string) error {
m.deleteCalled = true
delete(m.conversations, conversationID)
return nil
}
func (m *MockStore) Size() int {
m.sizeCalled = true
return len(m.conversations)
}
func (m *MockStore) Close() error {
return nil
}