feat: build Go-based SearXNG-compatible search service
Implement an API-first Go rewrite with local engine adapters, upstream fallback, and Nix-based tooling so searches can run without matching the original UI while preserving response compatibility. Made-with: Cursor
This commit is contained in:
parent
7783367c71
commit
dc44837219
32 changed files with 3330 additions and 0 deletions
92
internal/engines/braveapi_test.go
Normal file
92
internal/engines/braveapi_test.go
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
package engines
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/ashie/gosearch/internal/contracts"
|
||||
)
|
||||
|
||||
func TestBraveEngine_GatingAndHeader(t *testing.T) {
|
||||
wantToken := "letmein"
|
||||
wantAPIKey := "api-key"
|
||||
|
||||
transport := roundTripperFunc(func(r *http.Request) (*http.Response, error) {
|
||||
if r.Header.Get("X-Subscription-Token") != wantAPIKey {
|
||||
t.Fatalf("missing/incorrect X-Subscription-Token header: got %q", r.Header.Get("X-Subscription-Token"))
|
||||
}
|
||||
if r.URL.Host != "api.search.brave.com" {
|
||||
t.Fatalf("unexpected host: %s", r.URL.Host)
|
||||
}
|
||||
if r.URL.Path != "/res/v1/web/search" {
|
||||
t.Fatalf("unexpected path: %s", r.URL.Path)
|
||||
}
|
||||
// basic query assertions
|
||||
q := r.URL.Query().Get("q")
|
||||
if q != "hugo" {
|
||||
t.Fatalf("unexpected q: %q", q)
|
||||
}
|
||||
|
||||
body := `{
|
||||
"web": {
|
||||
"results": [
|
||||
{"url":"https://example.com/a","title":"A","description":"B","age":"2024-06-03T00:00:00Z","thumbnail":{"src":"x"}}
|
||||
]
|
||||
}
|
||||
}`
|
||||
return httpResponse(http.StatusOK, body, "application/json"), nil
|
||||
})
|
||||
|
||||
client := &http.Client{Transport: transport}
|
||||
engine := &BraveEngine{
|
||||
client: client,
|
||||
apiKey: wantAPIKey,
|
||||
accessGateToken: wantToken,
|
||||
resultsPerPage: 20,
|
||||
}
|
||||
|
||||
// Wrong token => no upstream call / unresponsive engine.
|
||||
{
|
||||
resp, err := engine.Search(context.Background(), contracts.SearchRequest{
|
||||
Query: "hugo",
|
||||
Pageno: 1,
|
||||
Safesearch: 0,
|
||||
Language: "en",
|
||||
AccessToken: "wrong",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(resp.Results) != 0 {
|
||||
t.Fatalf("expected no results on unauthorized, got %d", len(resp.Results))
|
||||
}
|
||||
if len(resp.UnresponsiveEngines) != 1 {
|
||||
t.Fatalf("expected 1 unresponsive engine entry, got %v", resp.UnresponsiveEngines)
|
||||
}
|
||||
}
|
||||
|
||||
// Correct token => upstream call.
|
||||
{
|
||||
resp, err := engine.Search(context.Background(), contracts.SearchRequest{
|
||||
Query: "hugo",
|
||||
Pageno: 1,
|
||||
Safesearch: 0,
|
||||
Language: "en",
|
||||
AccessToken: wantToken,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(resp.Results) != 1 {
|
||||
t.Fatalf("expected 1 result, got %d", len(resp.Results))
|
||||
}
|
||||
if resp.Results[0].Title != "A" {
|
||||
t.Fatalf("unexpected title: %q", resp.Results[0].Title)
|
||||
}
|
||||
if resp.Results[0].URL == nil || *resp.Results[0].URL != "https://example.com/a" {
|
||||
t.Fatalf("unexpected url: %v", resp.Results[0].URL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue