package research_runtime_test import ( "context" "net/http" "net/http/httptest" "testing" researchadapter "git.cin.su/m/Assistant/adapters/research" "git.cin.su/m/Assistant/contracts/epistemics" ) func TestSearchConnectorReturnsBoundedImmutableEvidence(t *testing.T) { server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(`Verified titleObserved source excerpt`)) })) defer server.Close() request := epistemics.WebSearchRequest{Plan: epistemics.ResearchPlan{ID: "plan", Question: "verified source", SourceClasses: []string{"web-search"}, FreshnessPolicy: "current"}, PropositionKey: "question-1", Query: "verified source"} connector := researchadapter.SearchConnector{EndpointTemplate: server.URL + "?q=%s", AllowedHosts: map[string]struct{}{"127.0.0.1": {}}, Client: server.Client(), MaxResults: 3} // The production connector requires HTTPS. The test server is HTTPS too; // use its host as the allowlisted value and preserve the same transport. candidates, err := connector.Collect(context.Background(), request, 10) if err != nil { t.Fatalf("collect search evidence: %v", err) } if len(candidates) != 1 || candidates[0].Statement != "Verified title — Observed source excerpt" { t.Fatalf("unexpected candidates: %+v", candidates) } if candidates[0].Evidence != epistemics.EvidenceObserved || candidates[0].Snapshot.ContentDigest == "" || candidates[0].Excerpt.Text == "" { t.Fatalf("candidate lost evidence chain: %+v", candidates[0]) } } func TestSearchConnectorRejectsUnallowlistedEndpoint(t *testing.T) { request := epistemics.WebSearchRequest{Plan: epistemics.ResearchPlan{ID: "plan", Question: "q", SourceClasses: []string{"web-search"}, FreshnessPolicy: "current"}, Query: "q"} _, err := (researchadapter.SearchConnector{EndpointTemplate: "https://example.invalid/?q=%s"}).Collect(context.Background(), request, 10) if err != researchadapter.ErrSearchRejected { t.Fatalf("expected allowlist rejection, got %v", err) } } func TestSearchConnectorParsesConfiguredBingStyleResults(t *testing.T) { server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte(`
  • Source title

    Current source excerpt

  • `)) })) defer server.Close() request := epistemics.WebSearchRequest{Plan: epistemics.ResearchPlan{ID: "plan", Question: "q", SourceClasses: []string{"web-search"}, FreshnessPolicy: "current"}, PropositionKey: "p", Query: "q"} connector := researchadapter.SearchConnector{EndpointTemplate: server.URL + "?q=%s", AllowedHosts: map[string]struct{}{"127.0.0.1": {}}, Client: server.Client(), MaxResults: 3} candidates, err := connector.Collect(context.Background(), request, 10) if err != nil || len(candidates) != 1 || candidates[0].Statement != "Source title — Current source excerpt" { t.Fatalf("unexpected Bing-style evidence: candidates=%+v err=%v", candidates, err) } } func TestSearchConnectorDropsIrrelevantResults(t *testing.T) { server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte(`
  • Викисловарь

    Статья о грамматике

  • `)) })) defer server.Close() request := epistemics.WebSearchRequest{Plan: epistemics.ResearchPlan{ID: "plan", Question: "какая погода в Киеве", SourceClasses: []string{"web-search"}, FreshnessPolicy: "current"}, PropositionKey: "p", Query: "какая погода в Киеве"} connector := researchadapter.SearchConnector{EndpointTemplate: server.URL + "?q=%s", AllowedHosts: map[string]struct{}{"127.0.0.1": {}}, Client: server.Client(), MaxResults: 3} candidates, err := connector.Collect(context.Background(), request, 10) if err != nil { t.Fatalf("collect search evidence: %v", err) } if len(candidates) != 0 { t.Fatalf("irrelevant result was retained: %+v", candidates) } }