Documentation
¶
Overview ¶
Package testutil provides shared testing utilities for the koopa project.
This package contains reusable test infrastructure that can be used across multiple packages, following the pattern of Go standard library packages like net/http/httptest and testing/iotest.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CleanTables ¶
CleanTables truncates all test tables between tests for isolation.
Call this at the start of each test when using shared containers via TestMain. Uses TRUNCATE CASCADE to handle foreign key relationships.
func DiscardLogger ¶
DiscardLogger returns a *slog.Logger that discards all output. Use this in tests to reduce log noise.
func FindProjectRoot ¶
FindProjectRoot finds the project root directory by looking for go.mod. This allows tests to run from any subdirectory and still find migration files.
Types ¶
type GoogleAISetup ¶
GoogleAISetup contains all resources needed for Google AI-based tests.
func SetupGoogleAI ¶
func SetupGoogleAI(tb testing.TB) *GoogleAISetup
SetupGoogleAI creates a Google AI embedder with logger for testing.
This is the preferred setup function for integration tests that need both embedder and logger with real Google AI API access.
Requirements:
- GEMINI_API_KEY environment variable must be set
- Skips test if API key is not available
Example:
func TestKnowledge(t *testing.T) {
setup := testutil.SetupGoogleAI(t)
store := knowledge.NewStore(pool, setup.Logger)
// Use setup.Embedder, setup.Genkit, setup.Logger
}
Note: Accepts testing.TB interface to support both *testing.T (tests) and *testing.B (benchmarks). This allows the same setup to be used in both contexts.
func SetupGoogleAIForMain ¶
func SetupGoogleAIForMain() (*GoogleAISetup, error)
SetupGoogleAIForMain creates a Google AI embedder for use in TestMain.
Unlike SetupGoogleAI, it returns an error instead of calling tb.Fatal. Returns nil and a descriptive error if GEMINI_API_KEY is not set.
Example:
func TestMain(m *testing.M) {
ai, err := testutil.SetupGoogleAIForMain()
if err != nil {
fmt.Println(err)
os.Exit(0) // skip all tests
}
// use ai.Embedder, ai.Genkit, ai.Logger
}
type MockCall ¶
type MockCall struct {
UserMessage string // last user message text
Response string // response text returned
}
MockCall records a single call to the mock model.
type MockEmbedder ¶
type MockEmbedder struct {
// contains filtered or unexported fields
}
MockEmbedder provides deterministic embedding vectors for testing.
By default, it generates a deterministic vector from content using SHA-256. Explicit mappings can be added for precise cosine similarity control.
Thread-safe for concurrent use.
func NewMockEmbedder ¶
func NewMockEmbedder(dim int) *MockEmbedder
NewMockEmbedder creates a mock embedder with the given vector dimensions.
func (*MockEmbedder) RegisterEmbedder ¶
func (e *MockEmbedder) RegisterEmbedder(g *genkit.Genkit) ai.Embedder
RegisterEmbedder registers the mock as a Genkit embedder. The embedder name will be "mock/test-embedder".
func (*MockEmbedder) SetVector ¶
func (e *MockEmbedder) SetVector(content string, vec []float32)
SetVector registers an explicit vector for a given content string. Use this to control exact cosine similarity between test inputs.
type MockLLM ¶
type MockLLM struct {
// contains filtered or unexported fields
}
MockLLM provides deterministic LLM responses for testing. It matches user message content against registered patterns and returns the corresponding response.
Thread-safe for concurrent use.
func NewMockLLM ¶
NewMockLLM creates a mock LLM with the given fallback response. The fallback is returned when no pattern matches.
func (*MockLLM) AddResponse ¶
AddResponse registers a pattern-response pair. When a user message contains the pattern (case-insensitive), the response is returned. Patterns are checked in registration order; first match wins.
func (*MockLLM) AddToolResponse ¶
func (m *MockLLM) AddToolResponse(pattern string, tools []*ai.ToolRequest, textResponse string)
AddToolResponse registers a pattern that triggers tool calls.
func (*MockLLM) RegisterModel ¶
RegisterModel registers the mock as a Genkit model and returns a reference. The model name will be "mock/test-model".
type RAGSetup ¶
type RAGSetup struct {
// Genkit instance with both GoogleAI and PostgreSQL plugins
Genkit *genkit.Genkit
// Embedder for creating vector embeddings
Embedder ai.Embedder
// DocStore for indexing documents (from Genkit PostgreSQL plugin)
DocStore *postgresql.DocStore
// Retriever for semantic search (from Genkit PostgreSQL plugin)
Retriever ai.Retriever
}
RAGSetup contains all resources needed for RAG-enabled integration tests. This uses the Genkit PostgreSQL plugin for DocStore and Retriever.
func SetupRAG ¶
SetupRAG creates a complete RAG test environment using Genkit PostgreSQL plugin.
This function sets up:
- Genkit with GoogleAI plugin (for embeddings)
- PostgreSQL plugin wrapping the provided connection pool
- DocStore for indexing documents
- Retriever for semantic search
Requirements:
- GEMINI_API_KEY environment variable must be set
- PostgreSQL pool must be initialized (from SetupTestDB)
- Migrations must be run (SetupTestDB does this automatically)
Example:
func TestRAGFeature(t *testing.T) {
db := testutil.SetupTestDB(t)
rag := testutil.SetupRAG(t, db.Pool)
// Index documents
doc := ai.DocumentFromText("test content", map[string]any{
"source_type": "file",
})
rag.DocStore.Index(ctx, []*ai.Document{doc})
// Query using retriever
req := &ai.RetrieverRequest{Query: ai.DocumentFromText("query", nil)}
resp, _ := rag.Retriever.Retrieve(ctx, req)
}
type TestDBContainer ¶
type TestDBContainer struct {
Pool *pgxpool.Pool
ConnStr string
// contains filtered or unexported fields
}
TestDBContainer wraps a PostgreSQL test container with connection pool.
Provides:
- Isolated PostgreSQL instance with pgvector extension
- Connection pool for database operations
- Automatic cleanup via tb.Cleanup (no manual cleanup needed)
Usage:
db := testutil.SetupTestDB(t) // Use db.Pool for database operations
func SetupTestDB ¶
func SetupTestDB(tb testing.TB) *TestDBContainer
SetupTestDB creates a PostgreSQL container for testing with pgvector extension.
Creates a fully-configured PostgreSQL container with:
- pgvector extension (for vector similarity search)
- Test database schema (via migrations)
- Connection pool ready for use
Cleanup is registered via tb.Cleanup and runs automatically when the test ends.
Example:
func TestMyFeature(t *testing.T) {
db := testutil.SetupTestDB(t)
// Use db.Pool for queries
var count int
err := db.Pool.QueryRow(ctx, "SELECT COUNT(*) FROM documents").Scan(&count)
if err != nil {
t.Fatalf("QueryRow() unexpected error: %v", err)
}
}
Note: Accepts testing.TB interface to support both *testing.T (tests) and *testing.B (benchmarks). This allows the same setup to be used in both contexts.
func SetupTestDBForMain ¶
func SetupTestDBForMain() (*TestDBContainer, func(), error)
SetupTestDBForMain creates a PostgreSQL container for use in TestMain.
Unlike SetupTestDB, it does not register cleanup via tb.Cleanup. The caller must call the returned cleanup function after m.Run().
Use this when multiple tests in a package share a single container to reduce Docker resource usage. Use CleanTables between tests for isolation.
Example:
var sharedDB *testutil.TestDBContainer
func TestMain(m *testing.M) {
var cleanup func()
var err error
sharedDB, cleanup, err = testutil.SetupTestDBForMain()
if err != nil {
log.Fatalf("starting test database: %v", err)
}
code := m.Run()
cleanup()
os.Exit(code)
}