homelabby/internal/research/searxng_test.go
Mikkel Georgsen 30cd279f49 feat(07-01): SearXNG client, ListDevicesWithStatus, SearXNGURL config
- internal/research/searxng.go: SearXNGClient implementing ai.ResearchClient
- internal/research/searxng_test.go: httptest mock server tests (4 pass)
- internal/netbox/client.go: ListDevicesWithStatus client-side filter
- internal/config/config.go: SearXNGURL field with default + env binding
2026-04-10 07:48:22 +00:00

111 lines
3.4 KiB
Go

package research_test
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"git.georgsen.dk/hwlab/internal/research"
)
func TestSearXNGSearch_ValidResponse(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/search" {
t.Errorf("unexpected path: %s", r.URL.Path)
}
if r.URL.Query().Get("format") != "json" {
t.Errorf("expected format=json, got %s", r.URL.Query().Get("format"))
}
if r.URL.Query().Get("q") == "" {
t.Error("expected non-empty q param")
}
resp := map[string]interface{}{
"results": []map[string]interface{}{
{"title": "Intel i350 NIC", "url": "https://ark.intel.com/i350", "content": "Quad-port Gigabit Ethernet adapter"},
{"title": "Intel i350 Datasheet", "url": "https://intel.com/datasheet", "content": "Technical specs for i350 series"},
},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}))
defer srv.Close()
client := research.NewSearXNGClient(srv.URL)
results, err := client.Search(context.Background(), "Intel NIC i350")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(results) != 2 {
t.Fatalf("expected 2 results, got %d", len(results))
}
if results[0].Title != "Intel i350 NIC" {
t.Errorf("unexpected title: %s", results[0].Title)
}
if results[0].URL != "https://ark.intel.com/i350" {
t.Errorf("unexpected URL: %s", results[0].URL)
}
if results[0].Snippet != "Quad-port Gigabit Ethernet adapter" {
t.Errorf("unexpected snippet (content): %s", results[0].Snippet)
}
}
func TestSearXNGSearch_HTTP500(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()
client := research.NewSearXNGClient(srv.URL)
_, err := client.Search(context.Background(), "test query")
if err == nil {
t.Fatal("expected error for HTTP 500, got nil")
}
}
func TestSearXNGSearch_EmptyResults(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
resp := map[string]interface{}{"results": []interface{}{}}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}))
defer srv.Close()
client := research.NewSearXNGClient(srv.URL)
results, err := client.Search(context.Background(), "something obscure")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(results) != 0 {
t.Errorf("expected 0 results, got %d", len(results))
}
}
func TestSearXNGSearch_QueryEncoding(t *testing.T) {
var capturedQuery string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
capturedQuery = r.URL.Query().Get("q")
resp := map[string]interface{}{"results": []interface{}{}}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}))
defer srv.Close()
client := research.NewSearXNGClient(srv.URL)
_, err := client.Search(context.Background(), "Intel NIC i350 2.5G")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if capturedQuery != "Intel NIC i350 2.5G" {
t.Errorf("unexpected decoded query: %q", capturedQuery)
}
}
func TestNewSearXNGClient_DefaultURL(t *testing.T) {
// Empty baseURL should use the default LAN address
client := research.NewSearXNGClient("")
if client == nil {
t.Fatal("expected non-nil client")
}
}