sourcegraph/internal/search/alert_test.go
Julie Tibshirani 5c5ed6ca27
feat(search): remove smart search logic (#64215)
This change removes the backend smart search logic. After this, searches
with smart search enabled (`sm=1`) will be executed in the default
'precise' mode (`sm=0`). For old searches that use `sm=1` and
`patterntype=standard`, it's possible that they will now return no
results.

Looking at telemetry, only 0.1% of searches on dot com trigger any smart
search rule. So this change should only affect a small percentage of
usage. To mitigate the impact on these rare cases, this PR adds an alert
whenever there are no results and smart search is enabled, suggesting
users switch to keyword search. (This will help in the majority of
cases, since the most frequent smart search rule rewrites literal
queries to use 'AND' between terms).

Closes SPLF-92
2024-08-01 18:02:35 +03:00

220 lines
4.9 KiB
Go

package search
import (
"fmt"
"reflect"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/sourcegraph/sourcegraph/internal/search/query"
)
func TestMaxPriorityAlert(t *testing.T) {
t.Run("no alerts", func(t *testing.T) {
require.Equal(t, (*Alert)(nil), MaxPriorityAlert())
})
t.Run("nil alert", func(t *testing.T) {
require.Equal(t, (*Alert)(nil), MaxPriorityAlert(nil))
})
t.Run("one alert", func(t *testing.T) {
a1 := Alert{Title: "test1"}
require.Equal(t, &a1, MaxPriorityAlert(&a1))
})
t.Run("equal priority alerts", func(t *testing.T) {
a1 := Alert{Title: "test1"}
a2 := Alert{Title: "test2"}
require.Equal(t, &a1, MaxPriorityAlert(&a1, &a2))
})
t.Run("higher priority alerts", func(t *testing.T) {
a1 := Alert{Title: "test1"}
a2 := Alert{Title: "test2", Priority: 2}
require.Equal(t, &a2, MaxPriorityAlert(&a1, &a2))
})
t.Run("nil and non-nil", func(t *testing.T) {
a1 := Alert{Title: "test1"}
require.Equal(t, &a1, MaxPriorityAlert(nil, &a1))
})
t.Run("non-nil and nil", func(t *testing.T) {
a1 := Alert{Title: "test1"}
require.Equal(t, &a1, MaxPriorityAlert(&a1, nil))
})
}
func TestSearchPatternForSuggestion(t *testing.T) {
cases := []struct {
Name string
Alert *Alert
Want string
}{
{
Name: "with_regex_suggestion",
Alert: &Alert{
Title: "An alert for regex",
Description: "An alert for regex",
ProposedQueries: []*QueryDescription{
{
Description: "Some query description",
Query: "repo:github.com/sourcegraph/sourcegraph",
PatternType: query.SearchTypeRegex,
},
},
},
Want: "repo:github.com/sourcegraph/sourcegraph patternType:regexp",
},
{
Name: "with_structural_suggestion",
Alert: &Alert{
Title: "An alert for structural",
Description: "An alert for structural",
ProposedQueries: []*QueryDescription{
{
Description: "Some query description",
Query: "repo:github.com/sourcegraph/sourcegraph",
PatternType: query.SearchTypeStructural,
},
},
},
Want: "repo:github.com/sourcegraph/sourcegraph patternType:structural",
},
}
for _, tt := range cases {
t.Run(tt.Name, func(t *testing.T) {
got := tt.Alert.ProposedQueries
if !reflect.DeepEqual(got[0].QueryString(), tt.Want) {
t.Errorf("got: %s, want: %s", got[0].QueryString(), tt.Want)
}
})
}
}
func TestAddQueryRegexpField(t *testing.T) {
tests := []struct {
query string
addField string
addPattern string
want string
}{
{
query: "",
addField: "repo",
addPattern: "p",
want: "repo:p",
},
{
query: "foo",
addField: "repo",
addPattern: "p",
want: "repo:p foo",
},
{
query: "foo repo:p",
addField: "repo",
addPattern: "p",
want: "repo:p foo",
},
{
query: "foo repo:q",
addField: "repo",
addPattern: "p",
want: "repo:q repo:p foo",
},
{
query: "foo repo:p",
addField: "repo",
addPattern: "pp",
want: "repo:pp foo",
},
{
query: "foo repo:p",
addField: "repo",
addPattern: "^p",
want: "repo:^p foo",
},
{
query: "foo repo:p",
addField: "repo",
addPattern: "p$",
want: "repo:p$ foo",
},
{
query: "foo repo:^p",
addField: "repo",
addPattern: "^pq",
want: "repo:^pq foo",
},
{
query: "foo repo:p$",
addField: "repo",
addPattern: "qp$",
want: "repo:qp$ foo",
},
{
query: "foo repo:^p",
addField: "repo",
addPattern: "x$",
want: "repo:^p repo:x$ foo",
},
{
query: "foo repo:p|q",
addField: "repo",
addPattern: "pq",
want: "repo:p|q repo:pq foo",
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("%s, add %s:%s", test.query, test.addField, test.addPattern), func(t *testing.T) {
q, err := query.ParseLiteral(test.query)
if err != nil {
t.Fatal(err)
}
got := query.AddRegexpField(q, test.addField, test.addPattern)
if got != test.want {
t.Errorf("got %q, want %q", got, test.want)
}
})
}
}
func TestCapFirst(t *testing.T) {
tests := []struct {
name string
in string
want string
}{
{name: "empty", in: "", want: ""},
{name: "a", in: "a", want: "A"},
{name: "ab", in: "ab", want: "Ab"},
{name: "хлеб", in: "хлеб", want: "Хлеб"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := capFirst(tt.in); got != tt.want {
t.Errorf("makeTitle() = %v, want %v", got, tt.want)
}
})
}
}
func TestQuoteSuggestions(t *testing.T) {
t.Run("regex error", func(t *testing.T) {
raw := "*"
_, err := query.Pipeline(query.InitRegexp(raw))
if err == nil {
t.Fatalf("error returned from query.ParseRegexp(%q) is nil", raw)
}
alert := AlertForQuery(err)
if !strings.Contains(alert.Description, "regexp") {
t.Errorf("description is '%s', want it to contain 'regexp'", alert.Description)
}
})
}