package server import ( "context" "encoding/json" "fmt" "net/http" "net/http/httptest" "os" "strings" "testing" ) // The results of DuckDuckGo's light page: titles, real addresses (not its // redirects), snippets without tags. func TestParseDDGLite(t *testing.T) { b, err := os.ReadFile("testdata/ddg_lite.html") if err != nil { t.Fatal(err) } rs := parseDDG(string(b)) if len(rs) < 5 { t.Fatalf("results: %d", len(rs)) } r := rs[0] if r.URL != "https://go.dev/doc/database/cancel-operations" || !strings.Contains(r.Title, "Canceling in-progress operations") || !strings.Contains(r.Snippet, "Learn how to use context.Context") || strings.Contains(r.Snippet, "") { 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 brave"}]}}`) 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) } }