mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-07-31 17:59:49 -04:00
test(search): convert bleve geo/mtime tests to ginkgo
The package's engine suite is ginkgo; these new tests were plain.
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
package bleve_test
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
bleveSearch "github.com/blevesearch/bleve/v2"
|
||||
"github.com/blevesearch/bleve/v2/search/query"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
libregraph "github.com/opencloud-eu/libre-graph-api-go"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/bleve"
|
||||
@@ -14,18 +13,14 @@ import (
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/search"
|
||||
)
|
||||
|
||||
// geoFixture builds an in-memory bleve index with a single resource that
|
||||
// carries the given lon/lat/alt. Used by the search tests below.
|
||||
func geoFixture(t *testing.T, lon, lat, alt float64) bleveSearch.Index {
|
||||
t.Helper()
|
||||
// geoFixture builds an in-memory bleve index with a single resource carrying
|
||||
// the given lon/lat/alt, indexed through the full bleve pipeline.
|
||||
func geoFixture(lon, lat, alt float64) bleveSearch.Index {
|
||||
idxMapping, err := bleve.NewMapping()
|
||||
if err != nil {
|
||||
t.Fatalf("NewMapping: %v", err)
|
||||
}
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
idx, err := bleveSearch.NewMemOnly(idxMapping)
|
||||
if err != nil {
|
||||
t.Fatalf("NewMemOnly: %v", err)
|
||||
}
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r := search.Resource{
|
||||
ID: "x",
|
||||
Document: content.Document{
|
||||
@@ -38,141 +33,95 @@ func geoFixture(t *testing.T, lon, lat, alt float64) bleveSearch.Index {
|
||||
},
|
||||
}
|
||||
doc, err := mapping.PrepareForIndex(r, r.SearchFieldOverrides())
|
||||
if err != nil {
|
||||
t.Fatalf("PrepareForIndex: %v", err)
|
||||
}
|
||||
if err := idx.Index(r.ID, doc); err != nil {
|
||||
t.Fatalf("Index: %v", err)
|
||||
}
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(idx.Index(r.ID, doc)).To(Succeed())
|
||||
return idx
|
||||
}
|
||||
|
||||
// TestLocationAltitudeRoundTrip proves that every subfield of Location
|
||||
// (including altitude) ends up in hit.Fields when a Resource is indexed
|
||||
// through the full bleve pipeline. This is the invariant the Move /
|
||||
// Delete / Restore round-trip depends on.
|
||||
func TestLocationAltitudeRoundTrip(t *testing.T) {
|
||||
idx := geoFixture(t, 11.103870357204285, 49.48675890884328, 1047.7)
|
||||
var _ = Describe("Location geo queries", func() {
|
||||
// Every Location subfield (including altitude) must end up in hit.Fields
|
||||
// when a Resource is indexed through the full bleve pipeline. This is the
|
||||
// invariant the Move / Delete / Restore round-trip depends on.
|
||||
It("round-trips every Location subfield into hit.Fields", func() {
|
||||
idx := geoFixture(11.103870357204285, 49.48675890884328, 1047.7)
|
||||
|
||||
req := bleveSearch.NewSearchRequest(bleveSearch.NewMatchAllQuery())
|
||||
req.Fields = []string{"*"}
|
||||
res, err := idx.Search(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Search: %v", err)
|
||||
}
|
||||
if len(res.Hits) == 0 {
|
||||
t.Fatal("no hits")
|
||||
}
|
||||
keys := make([]string, 0, len(res.Hits[0].Fields))
|
||||
for k := range res.Hits[0].Fields {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
t.Logf("hit.Fields keys: %v", keys)
|
||||
req := bleveSearch.NewSearchRequest(bleveSearch.NewMatchAllQuery())
|
||||
req.Fields = []string{"*"}
|
||||
res, err := idx.Search(req)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Hits).ToNot(BeEmpty())
|
||||
|
||||
for _, k := range []string{"location.longitude", "location.latitude", "location.altitude"} {
|
||||
if _, ok := res.Hits[0].Fields[k]; !ok {
|
||||
t.Errorf("missing %q in hit.Fields (got %v)", k, keys)
|
||||
for _, k := range []string{"location.longitude", "location.latitude", "location.altitude"} {
|
||||
Expect(res.Hits[0].Fields).To(HaveKey(k))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
func TestLocationLatitudeRangeQueryMatches(t *testing.T) {
|
||||
idx := geoFixture(t, 11.1, 49.48, 1000)
|
||||
It("matches a latitude numeric range and misses outside it", func() {
|
||||
idx := geoFixture(11.1, 49.48, 1000)
|
||||
|
||||
// numeric range on the sub-field
|
||||
min, max := 49.0, 50.0
|
||||
incl := true
|
||||
q := query.NewNumericRangeInclusiveQuery(&min, &max, &incl, &incl)
|
||||
q.SetField("location.latitude")
|
||||
res, err := idx.Search(bleveSearch.NewSearchRequest(q))
|
||||
if err != nil {
|
||||
t.Fatalf("Search: %v", err)
|
||||
}
|
||||
if len(res.Hits) != 1 {
|
||||
t.Fatalf("latitude range: got %d hits, want 1", len(res.Hits))
|
||||
}
|
||||
min, max := 49.0, 50.0
|
||||
incl := true
|
||||
q := query.NewNumericRangeInclusiveQuery(&min, &max, &incl, &incl)
|
||||
q.SetField("location.latitude")
|
||||
res, err := idx.Search(bleveSearch.NewSearchRequest(q))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Hits).To(HaveLen(1))
|
||||
|
||||
// same range excluding the indexed latitude => no match
|
||||
lowMin, lowMax := 0.0, 10.0
|
||||
q2 := query.NewNumericRangeInclusiveQuery(&lowMin, &lowMax, &incl, &incl)
|
||||
q2.SetField("location.latitude")
|
||||
res, err = idx.Search(bleveSearch.NewSearchRequest(q2))
|
||||
if err != nil {
|
||||
t.Fatalf("Search: %v", err)
|
||||
}
|
||||
if len(res.Hits) != 0 {
|
||||
t.Errorf("latitude range outside value: got %d hits, want 0", len(res.Hits))
|
||||
}
|
||||
}
|
||||
lowMin, lowMax := 0.0, 10.0
|
||||
q2 := query.NewNumericRangeInclusiveQuery(&lowMin, &lowMax, &incl, &incl)
|
||||
q2.SetField("location.latitude")
|
||||
res, err = idx.Search(bleveSearch.NewSearchRequest(q2))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Hits).To(BeEmpty())
|
||||
})
|
||||
|
||||
func TestLocationLongitudeRangeQueryMatches(t *testing.T) {
|
||||
idx := geoFixture(t, 11.1, 49.48, 1000)
|
||||
It("matches a longitude numeric range", func() {
|
||||
idx := geoFixture(11.1, 49.48, 1000)
|
||||
|
||||
min, max := 11.0, 12.0
|
||||
incl := true
|
||||
q := query.NewNumericRangeInclusiveQuery(&min, &max, &incl, &incl)
|
||||
q.SetField("location.longitude")
|
||||
res, err := idx.Search(bleveSearch.NewSearchRequest(q))
|
||||
if err != nil {
|
||||
t.Fatalf("Search: %v", err)
|
||||
}
|
||||
if len(res.Hits) != 1 {
|
||||
t.Fatalf("longitude range: got %d hits, want 1", len(res.Hits))
|
||||
}
|
||||
}
|
||||
min, max := 11.0, 12.0
|
||||
incl := true
|
||||
q := query.NewNumericRangeInclusiveQuery(&min, &max, &incl, &incl)
|
||||
q.SetField("location.longitude")
|
||||
res, err := idx.Search(bleveSearch.NewSearchRequest(q))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Hits).To(HaveLen(1))
|
||||
})
|
||||
|
||||
func TestLocationAltitudeRangeQueryMatches(t *testing.T) {
|
||||
idx := geoFixture(t, 11.1, 49.48, 1047.7)
|
||||
It("matches an altitude lower-bound range and misses above it", func() {
|
||||
idx := geoFixture(11.1, 49.48, 1047.7)
|
||||
|
||||
min := 1000.0
|
||||
incl := true
|
||||
q := query.NewNumericRangeInclusiveQuery(&min, nil, &incl, nil)
|
||||
q.SetField("location.altitude")
|
||||
res, err := idx.Search(bleveSearch.NewSearchRequest(q))
|
||||
if err != nil {
|
||||
t.Fatalf("Search: %v", err)
|
||||
}
|
||||
if len(res.Hits) != 1 {
|
||||
t.Fatalf("altitude >= 1000: got %d hits, want 1", len(res.Hits))
|
||||
}
|
||||
min := 1000.0
|
||||
incl := true
|
||||
q := query.NewNumericRangeInclusiveQuery(&min, nil, &incl, nil)
|
||||
q.SetField("location.altitude")
|
||||
res, err := idx.Search(bleveSearch.NewSearchRequest(q))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Hits).To(HaveLen(1))
|
||||
|
||||
// altitude floor above the indexed value => no hits
|
||||
highMin := 2000.0
|
||||
q2 := query.NewNumericRangeInclusiveQuery(&highMin, nil, &incl, nil)
|
||||
q2.SetField("location.altitude")
|
||||
res, err = idx.Search(bleveSearch.NewSearchRequest(q2))
|
||||
if err != nil {
|
||||
t.Fatalf("Search: %v", err)
|
||||
}
|
||||
if len(res.Hits) != 0 {
|
||||
t.Errorf("altitude >= 2000 against 1047.7: got %d hits, want 0", len(res.Hits))
|
||||
}
|
||||
}
|
||||
highMin := 2000.0
|
||||
q2 := query.NewNumericRangeInclusiveQuery(&highMin, nil, &incl, nil)
|
||||
q2.SetField("location.altitude")
|
||||
res, err = idx.Search(bleveSearch.NewSearchRequest(q2))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Hits).To(BeEmpty())
|
||||
})
|
||||
|
||||
func TestLocationGeoDistanceQueryMatches(t *testing.T) {
|
||||
// Nuremberg-ish coordinates.
|
||||
idx := geoFixture(t, 11.103870357204285, 49.48675890884328, 1047.7)
|
||||
It("matches a geo-distance query near the point and misses far away", func() {
|
||||
// Nuremberg-ish coordinates.
|
||||
idx := geoFixture(11.103870357204285, 49.48675890884328, 1047.7)
|
||||
|
||||
// 10 km radius around the indexed point should match.
|
||||
near := query.NewGeoDistanceQuery(11.103870357204285, 49.48675890884328, "10km")
|
||||
near.SetField("location" + mapping.GeopointSuffix)
|
||||
res, err := idx.Search(bleveSearch.NewSearchRequest(near))
|
||||
if err != nil {
|
||||
t.Fatalf("Search (near): %v", err)
|
||||
}
|
||||
if len(res.Hits) != 1 {
|
||||
t.Fatalf("geo distance near: got %d hits, want 1", len(res.Hits))
|
||||
}
|
||||
// 10 km radius around the indexed point should match.
|
||||
near := query.NewGeoDistanceQuery(11.103870357204285, 49.48675890884328, "10km")
|
||||
near.SetField("location" + mapping.GeopointSuffix)
|
||||
res, err := idx.Search(bleveSearch.NewSearchRequest(near))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Hits).To(HaveLen(1))
|
||||
|
||||
// Far away (Berlin, ~400 km) with a 10 km radius should miss.
|
||||
far := query.NewGeoDistanceQuery(13.404954, 52.520008, "10km")
|
||||
far.SetField("location" + mapping.GeopointSuffix)
|
||||
res, err = idx.Search(bleveSearch.NewSearchRequest(far))
|
||||
if err != nil {
|
||||
t.Fatalf("Search (far): %v", err)
|
||||
}
|
||||
if len(res.Hits) != 0 {
|
||||
t.Errorf("geo distance far: got %d hits, want 0", len(res.Hits))
|
||||
}
|
||||
}
|
||||
// Far away (Berlin, ~400 km) with a 10 km radius should miss.
|
||||
far := query.NewGeoDistanceQuery(13.404954, 52.520008, "10km")
|
||||
far.SetField("location" + mapping.GeopointSuffix)
|
||||
res, err = idx.Search(bleveSearch.NewSearchRequest(far))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Hits).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package bleve_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
bleveSearch "github.com/blevesearch/bleve/v2"
|
||||
bquery "github.com/blevesearch/bleve/v2/search/query"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/bleve"
|
||||
"github.com/opencloud-eu/opencloud/services/search/pkg/content"
|
||||
@@ -14,35 +14,24 @@ import (
|
||||
|
||||
// Mtime is typed as a date, so range queries are chronological, not a
|
||||
// lexicographic keyword compare.
|
||||
func TestMtimeDateRange(t *testing.T) {
|
||||
m, err := bleve.NewMapping()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
idx, err := bleveSearch.NewMemOnly(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r := search.Resource{ID: "x", Document: content.Document{Name: "f", Mtime: "2026-03-15T12:00:00.123456789Z"}}
|
||||
doc, err := mapping.PrepareForIndex(r, r.SearchFieldOverrides())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := idx.Index(r.ID, doc); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var _ = Describe("Mtime date range", func() {
|
||||
It("compares chronologically, not lexicographically", func() {
|
||||
m, err := bleve.NewMapping()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
idx, err := bleveSearch.NewMemOnly(m)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
hits := func(qs string) uint64 {
|
||||
res, err := idx.Search(bleveSearch.NewSearchRequest(bquery.NewQueryStringQuery(qs)))
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %v", qs, err)
|
||||
r := search.Resource{ID: "x", Document: content.Document{Name: "f", Mtime: "2026-03-15T12:00:00.123456789Z"}}
|
||||
doc, err := mapping.PrepareForIndex(r, r.SearchFieldOverrides())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(idx.Index(r.ID, doc)).To(Succeed())
|
||||
|
||||
hits := func(qs string) uint64 {
|
||||
res, err := idx.Search(bleveSearch.NewSearchRequest(bquery.NewQueryStringQuery(qs)))
|
||||
Expect(err).ToNot(HaveOccurred(), qs)
|
||||
return res.Total
|
||||
}
|
||||
return res.Total
|
||||
}
|
||||
if got := hits(`Mtime:>"2026-01-01T00:00:00Z"`); got != 1 {
|
||||
t.Errorf("in-range: got %d hits, want 1", got)
|
||||
}
|
||||
if got := hits(`Mtime:>"2026-06-01T00:00:00Z"`); got != 0 {
|
||||
t.Errorf("out-of-range: got %d hits, want 0", got)
|
||||
}
|
||||
}
|
||||
Expect(hits(`Mtime:>"2026-01-01T00:00:00Z"`)).To(Equal(uint64(1)), "in-range")
|
||||
Expect(hits(`Mtime:>"2026-06-01T00:00:00Z"`)).To(Equal(uint64(0)), "out-of-range")
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user