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
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>
158 lines
4.4 KiB
Go
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)
|
|
}
|
|
}
|