//go:build rag package rag import ( "context" "fmt" "net" "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // skipIfServicesUnavailable skips the test if either Qdrant or Ollama is not // reachable. Full pipeline tests need both. func skipIfServicesUnavailable(t *testing.T) { t.Helper() for _, addr := range []string{"localhost:6334", "localhost:11434"} { conn, err := net.DialTimeout("tcp", addr, 2*time.Second) if err != nil { t.Skipf("%s not available — skipping pipeline integration test", addr) } _ = conn.Close() } } func TestPipelineIntegration(t *testing.T) { skipIfServicesUnavailable(t) ctx := context.Background() // Create shared clients for the pipeline tests. qdrantCfg := DefaultQdrantConfig() qdrantClient, err := NewQdrantClient(qdrantCfg) require.NoError(t, err) t.Cleanup(func() { _ = qdrantClient.Close() }) ollamaCfg := DefaultOllamaConfig() ollamaClient, err := NewOllamaClient(ollamaCfg) require.NoError(t, err) t.Run("ingest and query end-to-end", func(t *testing.T) { collection := fmt.Sprintf("test-pipeline-%d", time.Now().UnixNano()) t.Cleanup(func() { _ = qdrantClient.DeleteCollection(ctx, collection) }) // Create temp directory with markdown files dir := t.TempDir() writeTestFile(t, filepath.Join(dir, "go-intro.md"), `# Go Programming ## Overview Go is an open-source programming language designed at Google. It features garbage collection, structural typing, and CSP-style concurrency. Go was created by Robert Griesemer, Rob Pike, and Ken Thompson. ## Concurrency Go provides goroutines and channels for concurrent programming. Goroutines are lightweight threads managed by the Go runtime. Channels allow goroutines to communicate safely without shared memory. `) writeTestFile(t, filepath.Join(dir, "qdrant-intro.md"), `# Qdrant Vector Database ## What Is Qdrant Qdrant is a vector similarity search engine and vector database. It provides a convenient API to store, search, and manage points with payload. Qdrant is written in Rust and supports filtering, quantisation, and distributed deployment. ## Use Cases Qdrant is commonly used for semantic search, recommendation systems, and retrieval-augmented generation (RAG) pipelines. It supports cosine, dot product, and Euclidean distance metrics. `) writeTestFile(t, filepath.Join(dir, "rust-intro.md"), `# Rust Programming ## Memory Safety Rust guarantees memory safety without a garbage collector through its ownership system. The borrow checker enforces rules at compile time, preventing data races, dangling pointers, and buffer overflows. `) // Ingest the directory ingestCfg := DefaultIngestConfig() ingestCfg.Directory = dir ingestCfg.Collection = collection ingestCfg.Chunk = ChunkConfig{Size: 500, Overlap: 50} stats, err := Ingest(ctx, qdrantClient, ollamaClient, ingestCfg, nil) require.NoError(t, err, "ingest should succeed") assert.Equal(t, 3, stats.Files, "all three files should be ingested") assert.Greater(t, stats.Chunks, 0, "should produce at least one chunk") assert.Equal(t, 0, stats.Errors, "no errors should occur during ingest") // Allow Qdrant to index time.Sleep(1 * time.Second) // Query for Go-related content queryCfg := DefaultQueryConfig() queryCfg.Collection = collection queryCfg.Limit = 5 queryCfg.Threshold = 0.0 // Accept all results for testing results, err := Query(ctx, qdrantClient, ollamaClient, "goroutines and channels in Go", queryCfg) require.NoError(t, err, "query should succeed") require.NotEmpty(t, results, "query should return at least one result") // The top result should be about Go concurrency foundGoContent := false for _, r := range results { if r.Source != "" && r.Text != "" { foundGoContent = true break } } assert.True(t, foundGoContent, "results should contain content with source and text fields") // Verify all results have expected metadata fields populated for i, r := range results { assert.NotEmpty(t, r.Text, "result %d should have text", i) assert.NotEmpty(t, r.Source, "result %d should have source", i) assert.NotEmpty(t, r.Category, "result %d should have category", i) assert.Greater(t, r.Score, float32(0.0), "result %d should have positive score", i) } }) t.Run("format results from real query", func(t *testing.T) { collection := fmt.Sprintf("test-format-%d", time.Now().UnixNano()) t.Cleanup(func() { _ = qdrantClient.DeleteCollection(ctx, collection) }) dir := t.TempDir() writeTestFile(t, filepath.Join(dir, "format-test.md"), `## Format Test This document is used to verify that the format functions produce non-empty output when given real query results from live services. `) ingestCfg := DefaultIngestConfig() ingestCfg.Directory = dir ingestCfg.Collection = collection _, err := Ingest(ctx, qdrantClient, ollamaClient, ingestCfg, nil) require.NoError(t, err) time.Sleep(1 * time.Second) queryCfg := DefaultQueryConfig() queryCfg.Collection = collection queryCfg.Limit = 3 queryCfg.Threshold = 0.0 results, err := Query(ctx, qdrantClient, ollamaClient, "format test document", queryCfg) require.NoError(t, err) require.NotEmpty(t, results, "should return at least one result for formatting") // FormatResultsText textOutput := FormatResultsText(results) assert.NotEmpty(t, textOutput) assert.NotEqual(t, "No results found.", textOutput) assert.Contains(t, textOutput, "Result 1") assert.Contains(t, textOutput, "Source:") // FormatResultsContext ctxOutput := FormatResultsContext(results) assert.NotEmpty(t, ctxOutput) assert.Contains(t, ctxOutput, "") assert.Contains(t, ctxOutput, "") assert.Contains(t, ctxOutput, "