Release v0.2.34
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -277,6 +278,74 @@ func TestSearchHistoryResponseEncodesEmptyQueriesAsArray(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Both routes that write search_history apply one rule, so a query /v1/search records is
|
||||
// exactly one /v1/search/history would have accepted. The length is counted in runes:
|
||||
// bytes would reject a Japanese title at a third of an English one's length.
|
||||
func TestSearchQueryRecordable(t *testing.T) {
|
||||
long := strings.Repeat("a", maxSearchQueryRunes)
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
term string
|
||||
want bool
|
||||
}{
|
||||
{"ordinary", "titanic", true},
|
||||
{"at the floor", "up", true},
|
||||
{"one letter", "u", false},
|
||||
{"blank", " ", false},
|
||||
{"padded is measured trimmed", " up ", true},
|
||||
{"at the ceiling", long, true},
|
||||
{"past the ceiling", long + "a", false},
|
||||
{"multibyte counted as runes", strings.Repeat("あ", maxSearchQueryRunes), true},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := searchQueryRecordable(tc.term); got != tc.want {
|
||||
t.Fatalf("searchQueryRecordable(%q) = %v, want %v", tc.term, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// A search whose viewer no longer has a session keeps its row: the query is what the page
|
||||
// is for, and an id still tells one searcher from another.
|
||||
func TestNameSearchEventsKeepsUnattributedRows(t *testing.T) {
|
||||
events := []store.SearchEvent{
|
||||
{Query: "severance", UserID: "u-1"},
|
||||
{Query: "dune", UserID: "gone"},
|
||||
}
|
||||
users := []store.KnownUser{
|
||||
{ID: "u-1", Username: "matt"},
|
||||
{ID: "u-2", Username: "sam"},
|
||||
}
|
||||
|
||||
got := nameSearchEvents(events, users)
|
||||
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("event count = %d, want 2", len(got))
|
||||
}
|
||||
if got[0].Username != "matt" {
|
||||
t.Fatalf("username = %q, want matt", got[0].Username)
|
||||
}
|
||||
if got[1].Username != "" || got[1].Query != "dune" {
|
||||
t.Fatalf("unattributed event = %+v, want an empty name and its query", got[1])
|
||||
}
|
||||
}
|
||||
|
||||
// The page must not offer a window the table cannot fill: RecordSearch prunes to the
|
||||
// retention period, so a wider one would draw a flat line for the difference.
|
||||
func TestSearchWindowMatchesRetention(t *testing.T) {
|
||||
if searchWindowDays != 30 {
|
||||
t.Fatalf("search window = %d days, want 30 to match store.SearchRetention", searchWindowDays)
|
||||
}
|
||||
}
|
||||
|
||||
// Recording must never be what stops a search being answered. A gateway with no database
|
||||
// reaches this on every keystroke, so the guard comes before anything that could panic on
|
||||
// a half-built server.
|
||||
func TestRecordSearchQueryWithoutStoreIsSilent(t *testing.T) {
|
||||
s := &Server{}
|
||||
s.recordSearchQuery(context.Background(), store.Session{EmbyUserID: "u1"}, "titanic")
|
||||
}
|
||||
|
||||
// The fixed rows must keep their order, ids and kinds: the client maps kinds onto
|
||||
// card shapes and uses ids as Compose keys.
|
||||
func TestBaseRowsShape(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user