Files
breakpilot-compliance/ai-compliance-sdk/internal/ucca/legal_rag_test.go
Benjamin Admin 14a99322eb
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 35s
CI / test-python-backend-compliance (push) Successful in 26s
CI / test-python-document-crawler (push) Successful in 22s
CI / test-python-dsms-gateway (push) Successful in 19s
feat: Phase 2 — RAG integration in Requirements + DSFA Draft
Add legal context enrichment from Qdrant vector corpus to the two
highest-priority modules (Requirements AI assistant and DSFA drafting
engine).

Go SDK:
- Add SearchCollection() with collection override + whitelist validation
- Refactor Search() to delegate to shared searchInternal()

Python backend:
- New ComplianceRAGClient proxying POST /sdk/v1/rag/search (error-tolerant)
- AI assistant: enrich interpret_requirement() and suggest_controls() with RAG
- Requirements API: add ?include_legal_context=true query parameter

Admin (Next.js):
- Extract shared queryRAG() utility from chat route
- Inject RAG legal context into v1 and v2 draft pipelines

Tests for all three layers (Go, Python, TypeScript shared utility).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 08:57:39 +01:00

158 lines
4.4 KiB
Go

package ucca
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestSearchCollection_UsesCorrectCollection(t *testing.T) {
// Track which collection was requested
var requestedURL string
// Mock Ollama (embedding)
ollamaMock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(ollamaEmbeddingResponse{
Embedding: make([]float64, 1024),
})
}))
defer ollamaMock.Close()
// Mock Qdrant
qdrantMock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestedURL = r.URL.Path
json.NewEncoder(w).Encode(qdrantSearchResponse{
Result: []qdrantSearchHit{},
})
}))
defer qdrantMock.Close()
// Parse qdrant mock host/port
qdrantAddr := strings.TrimPrefix(qdrantMock.URL, "http://")
parts := strings.Split(qdrantAddr, ":")
client := &LegalRAGClient{
qdrantHost: parts[0],
qdrantPort: parts[1],
ollamaURL: ollamaMock.URL,
embeddingModel: "bge-m3",
collection: "bp_compliance_ce",
httpClient: http.DefaultClient,
}
// Test with explicit collection
_, err := client.SearchCollection(context.Background(), "bp_compliance_recht", "test query", nil, 3)
if err != nil {
t.Fatalf("SearchCollection failed: %v", err)
}
if !strings.Contains(requestedURL, "/collections/bp_compliance_recht/") {
t.Errorf("Expected collection bp_compliance_recht in URL, got: %s", requestedURL)
}
}
func TestSearchCollection_FallbackDefault(t *testing.T) {
var requestedURL string
ollamaMock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(ollamaEmbeddingResponse{
Embedding: make([]float64, 1024),
})
}))
defer ollamaMock.Close()
qdrantMock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestedURL = r.URL.Path
json.NewEncoder(w).Encode(qdrantSearchResponse{
Result: []qdrantSearchHit{},
})
}))
defer qdrantMock.Close()
qdrantAddr := strings.TrimPrefix(qdrantMock.URL, "http://")
parts := strings.Split(qdrantAddr, ":")
client := &LegalRAGClient{
qdrantHost: parts[0],
qdrantPort: parts[1],
ollamaURL: ollamaMock.URL,
embeddingModel: "bge-m3",
collection: "bp_compliance_ce",
httpClient: http.DefaultClient,
}
// Test with empty collection (should fall back to default)
_, err := client.SearchCollection(context.Background(), "", "test query", nil, 3)
if err != nil {
t.Fatalf("SearchCollection failed: %v", err)
}
if !strings.Contains(requestedURL, "/collections/bp_compliance_ce/") {
t.Errorf("Expected default collection bp_compliance_ce in URL, got: %s", requestedURL)
}
}
func TestSearch_StillWorks(t *testing.T) {
var requestedURL string
ollamaMock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(ollamaEmbeddingResponse{
Embedding: make([]float64, 1024),
})
}))
defer ollamaMock.Close()
qdrantMock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestedURL = r.URL.Path
json.NewEncoder(w).Encode(qdrantSearchResponse{
Result: []qdrantSearchHit{
{
ID: "1",
Score: 0.95,
Payload: map[string]interface{}{
"chunk_text": "Test content",
"regulation_id": "eu_2016_679",
"regulation_name_de": "DSGVO",
"regulation_short": "DSGVO",
"category": "regulation",
"source": "https://example.com",
},
},
},
})
}))
defer qdrantMock.Close()
qdrantAddr := strings.TrimPrefix(qdrantMock.URL, "http://")
parts := strings.Split(qdrantAddr, ":")
client := &LegalRAGClient{
qdrantHost: parts[0],
qdrantPort: parts[1],
ollamaURL: ollamaMock.URL,
embeddingModel: "bge-m3",
collection: "bp_compliance_ce",
httpClient: http.DefaultClient,
}
results, err := client.Search(context.Background(), "DSGVO Art. 35", nil, 5)
if err != nil {
t.Fatalf("Search failed: %v", err)
}
if len(results) != 1 {
t.Fatalf("Expected 1 result, got %d", len(results))
}
if results[0].RegulationCode != "eu_2016_679" {
t.Errorf("Expected regulation_code eu_2016_679, got %s", results[0].RegulationCode)
}
if !strings.Contains(requestedURL, "/collections/bp_compliance_ce/") {
t.Errorf("Expected default collection in URL, got: %s", requestedURL)
}
}