//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, "