blob: 4022748e8c24e2f1f6102e95b12d87a2c2fa323e [file]
// Copyright 2026 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package searcheval
import (
"context"
"errors"
"math"
"testing"
)
type mockSearcher struct {
results map[string][]SearchResult
err error
}
func (m *mockSearcher) Search(ctx context.Context, query string, opts SearchOptions) ([]SearchResult, error) {
if m.err != nil {
return nil, m.err
}
return m.results[query], nil
}
func TestRunEvaluation(t *testing.T) {
queries := []GoldenQuery{
{
Query: "http client",
ExpectedPackages: []ExpectedPackage{
{PackagePath: "net/http"},
},
},
{
Query: "json parser",
ExpectedPackages: []ExpectedPackage{
{PackagePath: "encoding/json"},
},
},
}
searcher := &mockSearcher{
results: map[string][]SearchResult{
"http client": {
{PackagePath: "net/http"},
},
"json parser": {
{PackagePath: "github.com/foo/bar"},
{PackagePath: "encoding/json"},
},
},
}
opts := SearchOptions{Limit: 5}
t.Run("successful evaluation", func(t *testing.T) {
report, err := RunEvaluation(context.Background(), searcher, queries, opts, opts, opts)
if err != nil {
t.Fatalf("RunEvaluation error: %v", err)
}
if len(report.QueryReports) != 2 {
t.Fatalf("len(QueryReports) = %d, want 2", len(report.QueryReports))
}
// http client: net/http is rank 1 -> RR = 1.0
if math.Abs(report.QueryReports[0].KeywordRR-1.0) > 1e-6 {
t.Errorf("QueryReports[0].KeywordRR = %f, want 1.0", report.QueryReports[0].KeywordRR)
}
// json parser: encoding/json is rank 2 -> RR = 0.5
if math.Abs(report.QueryReports[1].KeywordRR-0.5) > 1e-6 {
t.Errorf("QueryReports[1].KeywordRR = %f, want 0.5", report.QueryReports[1].KeywordRR)
}
// Mean = (1.0 + 0.5) / 2 = 0.75
if math.Abs(report.MeanKeyword-0.75) > 1e-6 {
t.Errorf("report.MeanKeyword = %f, want 0.75", report.MeanKeyword)
}
})
t.Run("context canceled", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel() // cancel context immediately
_, err := RunEvaluation(ctx, searcher, queries, opts, opts, opts)
if !errors.Is(err, context.Canceled) {
t.Errorf("RunEvaluation on canceled context = %v, want context.Canceled", err)
}
})
t.Run("search error propagation", func(t *testing.T) {
errSearch := errors.New("database connection failed")
failingSearcher := &mockSearcher{err: errSearch}
report, err := RunEvaluation(context.Background(), failingSearcher, queries, opts, opts, opts)
if err != nil {
t.Fatalf("RunEvaluation error: %v", err)
}
if len(report.QueryReports) != 2 {
t.Fatalf("len(QueryReports) = %d, want 2", len(report.QueryReports))
}
if report.QueryReports[0].Error == nil {
t.Errorf("QueryReports[0].Error = nil, want error")
}
})
}