feat: the assistant's search engine in Настройки - DuckDuckGo, a SearXNG of one's own, Brave Search or Tavily, with DuckDuckGo behind the chosen one

This commit is contained in:
redtea committed 2026-10-16 10:38:12 +03:00
1 parent 9bf30b281e
commit 9912255289
6 files changed
+200 -2

No files matched your search

+52
View File
@@ -1,6 +1,11 @@
package server
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
@@ -23,3 +28,50 @@ func TestParseDDGLite(t *testing.T) {
t.Fatalf("first: %+v", r)
}
}
// Each search engine of the settings, and DuckDuckGo when the chosen one
// fails.
func TestSearchProviders(t *testing.T) {
var gotKey, gotQ string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.URL.Path == "/search" && r.URL.Query().Get("format") == "json": // SearXNG
gotQ = r.URL.Query().Get("q")
fmt.Fprint(w, `{"results":[{"title":"SX","url":"https://a.example/","content":"from searx"}]}`)
case r.URL.Path == "/res/v1/web/search": // Brave
gotKey, gotQ = r.Header.Get("X-Subscription-Token"), r.URL.Query().Get("q")
fmt.Fprint(w, `{"web":{"results":[{"title":"BR","url":"https://b.example/","description":"from <strong>brave</strong>"}]}}`)
case r.URL.Path == "/search" && r.Method == http.MethodPost: // Tavily
var in map[string]any
_ = json.NewDecoder(r.Body).Decode(&in)
gotKey, gotQ = r.Header.Get("Authorization"), in["query"].(string)
fmt.Fprint(w, `{"results":[{"title":"TV","url":"https://t.example/","content":"from tavily"}]}`)
default:
http.Error(w, "no", 500)
}
}))
defer srv.Close()
braveURL, tavilyURL = srv.URL+"/res/v1/web/search", srv.URL+"/search"
ctx := context.Background()
rs, err := searchWith(ctx, searchConf{Engine: "searxng", URL: srv.URL}, "q1", 6)
if err != nil || len(rs) != 1 || rs[0].Title != "SX" || gotQ != "q1" {
t.Fatalf("searxng: %+v %v", rs, err)
}
rs, err = searchWith(ctx, searchConf{Engine: "brave", Key: "bk"}, "q2", 6)
if err != nil || rs[0].Snippet != "from brave" || gotKey != "bk" || gotQ != "q2" {
t.Fatalf("brave: %+v %v %q", rs, err, gotKey)
}
rs, err = searchWith(ctx, searchConf{Engine: "tavily", Key: "tk"}, "q3", 6)
if err != nil || rs[0].Title != "TV" || gotKey != "Bearer tk" || gotQ != "q3" {
t.Fatalf("tavily: %+v %v %q", rs, err, gotKey)
}
// The chosen one fails: DuckDuckGo instead (stood in for here).
ddgSearch = func(ctx context.Context, q string, n int) ([]searchResult, error) {
return []searchResult{{Title: "DDG"}}, nil
}
rs, err = searchWith(ctx, searchConf{Engine: "searxng", URL: srv.URL + "/broken"}, "q4", 6)
if err != nil || rs[0].Title != "DDG" {
t.Fatalf("fallback: %+v %v", rs, err)
}
}