0
0
Fork 0

refactored integration tests into separate package

also made integration tests declarative
you can now easily define new datasets/mappings/searches/results
This commit is contained in:
Marty Schoch 2014-11-19 15:57:12 -05:00
parent eb16b3c563
commit 68a2b9614d
10 changed files with 544 additions and 343 deletions

View File

@ -12,352 +12,9 @@ package bleve
import (
"io/ioutil"
"os"
"strings"
"testing"
"time"
)
type Address struct {
Street string `json:"street"`
City string `json:"city"`
State string `json:"state"`
Zip string `json:"zip"`
}
type Person struct {
Identifier string `json:"id"`
Name string `json:"name"`
Age float64 `json:"age"`
Title string `json:"title"`
Birthday time.Time `json:"birthday"`
Address *Address `json:"address"`
Hideouts []*Address `json:"hideouts"`
Tags []string `json:"tags"`
}
func (p *Person) Type() string {
return "person"
}
func buildTestMapping() *IndexMapping {
enTextMapping := NewTextFieldMapping()
enTextMapping.Analyzer = "en"
standardTextMapping := NewTextFieldMapping()
standardTextMapping.Analyzer = "standard"
personMapping := NewDocumentMapping()
personMapping.AddFieldMappingsAt("name", enTextMapping)
personMapping.AddSubDocumentMapping("id", NewDocumentDisabledMapping())
personMapping.AddFieldMappingsAt("tags", standardTextMapping)
mapping := NewIndexMapping()
mapping.AddDocumentMapping("person", personMapping)
return mapping
}
var people = []*Person{
&Person{
Identifier: "a",
Name: "marty",
Age: 19,
// has no birthday set to test handling of zero time
Title: "mista",
Tags: []string{"gopher", "belieber"},
},
&Person{
Identifier: "b",
Name: "steve has a long name",
Age: 27,
Birthday: time.Unix(1000000000, 0),
Title: "missess",
},
&Person{
Identifier: "c",
Name: "bob walks home",
Age: 64,
Birthday: time.Unix(1400000000, 0),
Title: "masta",
},
&Person{
Identifier: "d",
Name: "bobbleheaded wings top the phone",
Age: 72,
Birthday: time.Unix(1400000000, 0),
Title: "mizz",
},
}
// FIXME needs more assertions
func TestIndex(t *testing.T) {
defer os.RemoveAll("testidx")
mapping := buildTestMapping()
index, err := New("testidx", mapping)
if err != nil {
t.Fatal(err)
}
defer index.Close()
// index all the people
for _, person := range people {
err = index.Index(person.Identifier, person)
if err != nil {
t.Error(err)
}
}
termQuery := NewTermQuery("marti").SetField("name")
searchRequest := NewSearchRequest(termQuery)
searchResult, err := index.Search(searchRequest)
if err != nil {
t.Error(err)
}
if searchResult.Total != uint64(1) {
t.Errorf("expected 1 total hit for term query, got %d", searchResult.Total)
} else {
if searchResult.Hits[0].ID != "a" {
t.Errorf("expected top hit id 'a', got '%s'", searchResult.Hits[0].ID)
}
}
termQuery = NewTermQuery("noone").SetField("name")
searchRequest = NewSearchRequest(termQuery)
searchResult, err = index.Search(searchRequest)
if err != nil {
t.Error(err)
}
if searchResult.Total != uint64(0) {
t.Errorf("expected 0 total hits")
}
matchPhraseQuery := NewMatchPhraseQuery("long name")
searchRequest = NewSearchRequest(matchPhraseQuery)
searchResult, err = index.Search(searchRequest)
if err != nil {
t.Error(err)
}
if searchResult.Total != uint64(1) {
t.Errorf("expected 1 total hit for phrase query, got %d", searchResult.Total)
} else {
if searchResult.Hits[0].ID != "b" {
t.Errorf("expected top hit id 'b', got '%s'", searchResult.Hits[0].ID)
}
}
termQuery = NewTermQuery("walking").SetField("name")
searchRequest = NewSearchRequest(termQuery)
searchResult, err = index.Search(searchRequest)
if err != nil {
t.Error(err)
}
if searchResult.Total != uint64(0) {
t.Errorf("expected 0 total hits")
}
matchQuery := NewMatchQuery("walking").SetField("name")
searchRequest = NewSearchRequest(matchQuery)
searchResult, err = index.Search(searchRequest)
if err != nil {
t.Error(err)
}
if searchResult.Total != uint64(1) {
t.Errorf("expected 1 total hit for match query, got %d", searchResult.Total)
} else {
if searchResult.Hits[0].ID != "c" {
t.Errorf("expected top hit id 'c', got '%s'", searchResult.Hits[0].ID)
}
}
prefixQuery := NewPrefixQuery("bobble").SetField("name")
searchRequest = NewSearchRequest(prefixQuery)
searchResult, err = index.Search(searchRequest)
if err != nil {
t.Error(err)
}
if searchResult.Total != uint64(1) {
t.Errorf("expected 1 total hit for prefix query, got %d", searchResult.Total)
} else {
if searchResult.Hits[0].ID != "d" {
t.Errorf("expected top hit id 'd', got '%s'", searchResult.Hits[0].ID)
}
}
syntaxQuery := NewQueryStringQuery("+name:phone")
searchRequest = NewSearchRequest(syntaxQuery)
searchResult, err = index.Search(searchRequest)
if err != nil {
t.Error(err)
}
if searchResult.Total != uint64(1) {
t.Errorf("expected 1 total hit for syntax query, got %d", searchResult.Total)
} else {
if searchResult.Hits[0].ID != "d" {
t.Errorf("expected top hit id 'd', got '%s'", searchResult.Hits[0].ID)
}
}
maxAge := 30.0
numericRangeQuery := NewNumericRangeQuery(nil, &maxAge).SetField("age")
searchRequest = NewSearchRequest(numericRangeQuery)
searchResult, err = index.Search(searchRequest)
if err != nil {
t.Error(err)
}
if searchResult.Total != uint64(2) {
t.Errorf("expected 2 total hits for numeric range query, got %d", searchResult.Total)
} else {
if searchResult.Hits[0].ID != "b" {
t.Errorf("expected top hit id 'b', got '%s'", searchResult.Hits[0].ID)
}
if searchResult.Hits[1].ID != "a" {
t.Errorf("expected next hit id 'a', got '%s'", searchResult.Hits[1].ID)
}
}
// test a numeric range with both endpoints
minAge := 20.0
numericRangeQuery = NewNumericRangeQuery(&minAge, &maxAge).SetField("age")
searchRequest = NewSearchRequest(numericRangeQuery)
searchResult, err = index.Search(searchRequest)
if err != nil {
t.Error(err)
}
if searchResult.Total != uint64(1) {
t.Errorf("expected 1 total hits for numeric range query, got %d", searchResult.Total)
} else {
if searchResult.Hits[0].ID != "b" {
t.Errorf("expected top hit id 'b', got '%s'", searchResult.Hits[0].ID)
}
}
// test the same query done as two
// individual range queries and'd together
q1 := NewNumericRangeQuery(&minAge, nil).SetField("age")
q2 := NewNumericRangeQuery(nil, &maxAge).SetField("age")
conQuery := NewConjunctionQuery([]Query{q1, q2})
searchRequest = NewSearchRequest(conQuery)
searchResult, err = index.Search(searchRequest)
if err != nil {
t.Error(err)
}
if searchResult.Total != uint64(1) {
t.Errorf("expected 1 total hits for numeric range query, got %d", searchResult.Total)
} else {
if searchResult.Hits[0].ID != "b" {
t.Errorf("expected top hit id 'b', got '%s'", searchResult.Hits[0].ID)
}
}
startDate = "2010-01-01"
dateRangeQuery := NewDateRangeQuery(&startDate, nil).SetField("birthday")
searchRequest = NewSearchRequest(dateRangeQuery)
searchResult, err = index.Search(searchRequest)
if err != nil {
t.Error(err)
}
if searchResult.Total != uint64(2) {
t.Errorf("expected 2 total hits for numeric range query, got %d", searchResult.Total)
} else {
if searchResult.Hits[0].ID != "d" {
t.Errorf("expected top hit id 'd', got '%s'", searchResult.Hits[0].ID)
}
if searchResult.Hits[1].ID != "c" {
t.Errorf("expected next hit id 'c', got '%s'", searchResult.Hits[1].ID)
}
}
// test that 0 time doesn't get indexed
endDate = "2010-01-01"
dateRangeQuery = NewDateRangeQuery(nil, &endDate).SetField("birthday")
searchRequest = NewSearchRequest(dateRangeQuery)
searchResult, err = index.Search(searchRequest)
if err != nil {
t.Error(err)
}
if searchResult.Total != uint64(1) {
t.Errorf("expected 1 total hit for numeric range query, got %d", searchResult.Total)
} else {
if searchResult.Hits[0].ID != "b" {
t.Errorf("expected top hit id 'b', got '%s'", searchResult.Hits[0].ID)
}
}
// test behavior of arrays
// make sure we can successfully find by all elements in array
termQuery = NewTermQuery("gopher").SetField("tags")
searchRequest = NewSearchRequest(termQuery)
searchResult, err = index.Search(searchRequest)
if err != nil {
t.Error(err)
} else {
if searchResult.Total != uint64(1) {
t.Errorf("expected 1 total hit for term query, got %d", searchResult.Total)
} else {
if searchResult.Hits[0].ID != "a" {
t.Errorf("expected top hit id 'a', got '%s'", searchResult.Hits[0].ID)
}
}
}
termQuery = NewTermQuery("belieber").SetField("tags")
searchRequest = NewSearchRequest(termQuery)
searchResult, err = index.Search(searchRequest)
if err != nil {
t.Error(err)
} else {
if searchResult.Total != uint64(1) {
t.Errorf("expected 1 total hit for term query, got %d", searchResult.Total)
} else {
if searchResult.Hits[0].ID != "a" {
t.Errorf("expected top hit id 'a', got '%s'", searchResult.Hits[0].ID)
}
}
}
termQuery = NewTermQuery("notintagsarray").SetField("tags")
searchRequest = NewSearchRequest(termQuery)
searchResult, err = index.Search(searchRequest)
if err != nil {
t.Error(err)
}
if searchResult.Total != uint64(0) {
t.Errorf("expected 0 total hits")
}
// lookup document a
// expect to find 2 values for field "tags"
tagsCount := 0
doc, err := index.Document("a")
if err != nil {
t.Error(err)
} else {
for _, f := range doc.Fields {
if f.Name() == "tags" {
tagsCount++
}
}
}
if tagsCount != 2 {
t.Errorf("expected to find 2 values for tags")
}
termQuery = NewTermQuery("marti").SetField("name")
searchRequest = NewSearchRequest(termQuery)
searchRequest.Size = 0
searchResult, err = index.Search(searchRequest)
if err != nil {
t.Error(err)
}
srstring := searchResult.String()
if !strings.HasPrefix(srstring, "1 matches") {
t.Errorf("expected prefix '1 matches', got %s", srstring)
}
}
func TestIndexCreateNewOverExisting(t *testing.T) {
defer os.RemoveAll("testidx")

33
search_test.go Normal file
View File

@ -0,0 +1,33 @@
// Copyright (c) 2014 Couchbase, Inc.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
package bleve
import (
"strings"
"testing"
"github.com/blevesearch/bleve/search"
)
func TestSearchResultString(t *testing.T) {
searchResult := &SearchResult{
Request: &SearchRequest{
Size: 0,
},
Total: 5,
Hits: search.DocumentMatchCollection{},
}
srstring := searchResult.String()
if !strings.HasPrefix(srstring, "5 matches") {
t.Errorf("expected prefix '5 matches', got %s", srstring)
}
}

22
test/integration.go Normal file
View File

@ -0,0 +1,22 @@
// Copyright (c) 2014 Couchbase, Inc.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
package test
import (
"github.com/blevesearch/bleve"
)
type SearchTest struct {
Search *bleve.SearchRequest `json:"search"`
Result *bleve.SearchResult `json:"result"`
Comment string `json:"comment"`
}
type SearchTests []*SearchTest

120
test/integration_test.go Normal file
View File

@ -0,0 +1,120 @@
// Copyright (c) 2014 Couchbase, Inc.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
package test
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"testing"
"github.com/blevesearch/bleve"
)
func TestIntegration(t *testing.T) {
fis, err := ioutil.ReadDir("tests")
if err != nil {
t.Fatal(err)
}
for _, fi := range fis {
if fi.IsDir() {
t.Logf("Running test: %s", fi.Name())
runTestDir(t, "tests"+string(filepath.Separator)+fi.Name())
}
}
}
func runTestDir(t *testing.T, dir string) {
// read the mapping
mappingBytes, err := ioutil.ReadFile(dir + string(filepath.Separator) + "mapping.json")
if err != nil {
t.Errorf("error reading mapping: %v", err)
return
}
var mapping bleve.IndexMapping
err = json.Unmarshal(mappingBytes, &mapping)
if err != nil {
t.Errorf("error unmarshalling mapping: %v", err)
return
}
// open new index
defer os.RemoveAll("test.bleve")
index, err := bleve.New("test.bleve", &mapping)
if err != nil {
t.Errorf("error creating new index: %v", err)
return
}
defer index.Close()
//index data
fis, err := ioutil.ReadDir(dir + string(filepath.Separator) + "data")
if err != nil {
t.Errorf("error reading data dir: %v", err)
return
}
for _, fi := range fis {
fileBytes, err := ioutil.ReadFile(dir + string(filepath.Separator) + "data" + string(filepath.Separator) + fi.Name())
if err != nil {
t.Errorf("error reading data file: %v", err)
return
}
filename := fi.Name()
ext := filepath.Ext(filename)
id := filename[0 : len(filename)-len(ext)]
err = index.Index(id, fileBytes)
if err != nil {
t.Errorf("error indexing data: %v", err)
return
}
}
// read the searches
searchBytes, err := ioutil.ReadFile(dir + string(filepath.Separator) + "searches.json")
if err != nil {
t.Errorf("error reading searches: %v", err)
return
}
var searches SearchTests
err = json.Unmarshal(searchBytes, &searches)
if err != nil {
t.Errorf("error unmarshalling searches: %v", err)
return
}
// run the searches
for _, search := range searches {
res, err := index.Search(search.Search)
if err != nil {
t.Errorf("error running search: %v", err)
}
if res.Total != search.Result.Total {
t.Errorf("expected total: %d got %d", search.Result.Total, res.Total)
continue
}
if len(res.Hits) != len(search.Result.Hits) {
t.Errorf("expected hits len: %d got %d", len(search.Result.Hits), len(res.Hits))
continue
}
for hi, hit := range search.Result.Hits {
if hit.ID != res.Hits[hi].ID {
t.Errorf("expected hit %d to have ID %s got %s", hi, hit.ID, res.Hits[hi].ID)
}
if hit.Fields != nil {
if !reflect.DeepEqual(hit.Fields, res.Hits[hi].Fields) {
t.Errorf("expected hit %d to have fields %#v got %#v", hi, hit.Fields, res.Hits[hi].Fields)
}
}
}
}
}

View File

@ -0,0 +1,7 @@
{
"id": "a",
"name": "marty",
"age": 19,
"title": "mista",
"tags": ["gopher", "belieber"]
}

View File

@ -0,0 +1,7 @@
{
"id": "b",
"name": "steve has a long name",
"age": 27,
"birthday": "2001-09-09T01:46:40Z",
"title": "missess"
}

View File

@ -0,0 +1,7 @@
{
"id": "c",
"name": "bob walks home",
"age": 64,
"birthday": "2014-05-13T16:53:20Z",
"title": "masta"
}

View File

@ -0,0 +1,7 @@
{
"id": "d",
"name": "bobbleheaded wings top the phone",
"age": 72,
"birthday": "2014-05-13T16:53:20Z",
"title": "mizz"
}

View File

@ -0,0 +1,26 @@
{
"types": {
"person": {
"properties": {
"name": {
"fields": [
{
"include_in_all": true,
"index": true,
"store": true,
"analyzer": "en",
"type": "text"
}
],
"dynamic": true,
"enabled": true
},
"id": {
"dynamic": false,
"enabled": false
}
}
}
},
"default_type": "person"
}

View File

@ -0,0 +1,315 @@
[
{
"search": {
"from": 0,
"size": 10,
"query": {
"field": "name",
"term": "marti"
}
},
"result": {
"total_hits": 1,
"hits": [
{
"id": "a"
}
]
}
},
{
"search": {
"from": 0,
"size": 10,
"query": {
"field": "name",
"term": "noone"
}
},
"result": {
"total_hits": 0,
"hits": []
}
},
{
"search": {
"from": 0,
"size": 10,
"query": {
"match_phrase": "long name"
}
},
"result": {
"total_hits": 1,
"hits": [
{
"id": "b"
}
]
}
},
{
"search": {
"from": 0,
"size": 10,
"query": {
"field": "name",
"term": "walking"
}
},
"result": {
"total_hits": 0,
"hits": []
}
},
{
"search": {
"from": 0,
"size": 10,
"query": {
"fuzziness": 0,
"prefix_length": 0,
"field": "name",
"match": "walking"
}
},
"result": {
"total_hits": 1,
"hits": [
{
"id": "c"
}
]
}
},
{
"search": {
"from": 0,
"size": 10,
"query": {
"field": "name",
"prefix": "bobble"
}
},
"result": {
"total_hits": 1,
"hits": [
{
"id": "d"
}
]
}
},
{
"search": {
"from": 0,
"size": 10,
"query": {
"query": "+name:phone"
}
},
"result": {
"total_hits": 1,
"hits": [
{
"id": "d"
}
]
}
},
{
"search": {
"from": 0,
"size": 10,
"query": {
"field": "age",
"max": 30
}
},
"result": {
"total_hits": 2,
"hits": [
{
"id": "b"
},
{
"id": "a"
}
]
}
},
{
"search": {
"from": 0,
"size": 10,
"query": {
"field": "age",
"max": 30,
"min": 20
}
},
"result": {
"total_hits": 1,
"hits": [
{
"id": "b"
}
]
}
},
{
"search": {
"from": 0,
"size": 10,
"query": {
"conjuncts": [
{
"boost": 1,
"field": "age",
"min": 20
},
{
"boost": 1,
"field": "age",
"max": 30
}
]
}
},
"result": {
"total_hits": 1,
"hits": [
{
"id": "b"
}
]
}
},
{
"search": {
"from": 0,
"size": 10,
"query": {
"field": "birthday",
"start": "2010-01-01"
}
},
"result": {
"total_hits": 2,
"hits": [
{
"id": "d"
},
{
"id": "c"
}
]
}
},
{
"search": {
"from": 0,
"size": 10,
"query": {
"field": "birthday",
"end": "2010-01-01"
}
},
"result": {
"total_hits": 1,
"hits": [
{
"id": "b"
}
]
}
},
{
"search": {
"from": 0,
"size": 10,
"query": {
"field": "tags",
"term": "gopher"
}
},
"result": {
"total_hits": 1,
"hits": [
{
"id": "a"
}
]
}
},
{
"search": {
"from": 0,
"size": 10,
"query": {
"field": "tags",
"term": "belieber"
}
},
"result": {
"total_hits": 1,
"hits": [
{
"id": "a"
}
]
}
},
{
"search": {
"from": 0,
"size": 10,
"query": {
"field": "tags",
"term": "notintagsarray"
}
},
"result": {
"total_hits": 0,
"hits": []
}
},
{
"comment": "with size 0, total should be 1, but hits empty",
"search": {
"from": 0,
"size": 0,
"query": {
"field": "name",
"term": "marti"
}
},
"result": {
"total_hits": 1,
"hits": []
}
},
{
"comment": "a search for doc a that includes tags field, verifies both values come back",
"search": {
"from": 0,
"size": 10,
"fields": ["tags"],
"query": {
"field": "name",
"term": "marti"
}
},
"result": {
"total_hits": 1,
"hits": [
{
"id": "a",
"fields": {
"tags": ["gopher", "belieber"]
}
}
]
}
}
]