setup_test.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. // Copyright 2012-present Oliver Eilhard. All rights reserved.
  2. // Use of this source code is governed by a MIT-license.
  3. // See http://olivere.mit-license.org/license.txt for details.
  4. package elastic
  5. import (
  6. "bytes"
  7. "context"
  8. "encoding/json"
  9. "flag"
  10. "fmt"
  11. "log"
  12. "math/rand"
  13. "net/http"
  14. "os"
  15. "time"
  16. )
  17. const (
  18. testIndexName = "elastic-test"
  19. testIndexName2 = "elastic-test2"
  20. testMapping = `
  21. {
  22. "settings":{
  23. "number_of_shards":1,
  24. "number_of_replicas":0
  25. },
  26. "mappings":{
  27. "_default_": {
  28. "_all": {
  29. "enabled": true
  30. }
  31. },
  32. "tweet":{
  33. "properties":{
  34. "user":{
  35. "type":"keyword"
  36. },
  37. "message":{
  38. "type":"text",
  39. "store": true,
  40. "fielddata": true
  41. },
  42. "tags":{
  43. "type":"keyword"
  44. },
  45. "location":{
  46. "type":"geo_point"
  47. },
  48. "suggest_field":{
  49. "type":"completion",
  50. "contexts":[
  51. {
  52. "name": "user_name",
  53. "type": "category"
  54. }
  55. ]
  56. }
  57. }
  58. },
  59. "comment":{
  60. "_parent": {
  61. "type": "tweet"
  62. }
  63. },
  64. "order":{
  65. "properties":{
  66. "article":{
  67. "type":"text"
  68. },
  69. "manufacturer":{
  70. "type":"keyword"
  71. },
  72. "price":{
  73. "type":"float"
  74. },
  75. "time":{
  76. "type":"date",
  77. "format": "YYYY-MM-dd"
  78. }
  79. }
  80. },
  81. "doctype":{
  82. "properties":{
  83. "message":{
  84. "type":"text",
  85. "store": true,
  86. "fielddata": true
  87. }
  88. }
  89. },
  90. "queries":{
  91. "properties": {
  92. "query": {
  93. "type": "percolator"
  94. }
  95. }
  96. },
  97. "tweet-nosource":{
  98. "_source": {
  99. "enabled": false
  100. },
  101. "properties":{
  102. "user":{
  103. "type":"keyword"
  104. },
  105. "message":{
  106. "type":"text",
  107. "store": true,
  108. "fielddata": true
  109. },
  110. "tags":{
  111. "type":"keyword"
  112. },
  113. "location":{
  114. "type":"geo_point"
  115. },
  116. "suggest_field":{
  117. "type":"completion",
  118. "contexts":[
  119. {
  120. "name":"user_name",
  121. "type":"category"
  122. }
  123. ]
  124. }
  125. }
  126. }
  127. }
  128. }
  129. `
  130. )
  131. type tweet struct {
  132. User string `json:"user"`
  133. Message string `json:"message"`
  134. Retweets int `json:"retweets"`
  135. Image string `json:"image,omitempty"`
  136. Created time.Time `json:"created,omitempty"`
  137. Tags []string `json:"tags,omitempty"`
  138. Location string `json:"location,omitempty"`
  139. Suggest *SuggestField `json:"suggest_field,omitempty"`
  140. }
  141. func (t tweet) String() string {
  142. return fmt.Sprintf("tweet{User:%q,Message:%q,Retweets:%d}", t.User, t.Message, t.Retweets)
  143. }
  144. type comment struct {
  145. User string `json:"user"`
  146. Comment string `json:"comment"`
  147. Created time.Time `json:"created,omitempty"`
  148. }
  149. func (c comment) String() string {
  150. return fmt.Sprintf("comment{User:%q,Comment:%q}", c.User, c.Comment)
  151. }
  152. type order struct {
  153. Article string `json:"article"`
  154. Manufacturer string `json:"manufacturer"`
  155. Price float64 `json:"price"`
  156. Time string `json:"time,omitempty"`
  157. }
  158. func (o order) String() string {
  159. return fmt.Sprintf("order{Article:%q,Manufacturer:%q,Price:%v,Time:%v}", o.Article, o.Manufacturer, o.Price, o.Time)
  160. }
  161. // doctype is required for Percolate tests.
  162. type doctype struct {
  163. Message string `json:"message"`
  164. }
  165. // queries is required for Percolate tests.
  166. type queries struct {
  167. Query string `json:"query"`
  168. }
  169. func isTravis() bool {
  170. return os.Getenv("TRAVIS") != ""
  171. }
  172. func travisGoVersion() string {
  173. return os.Getenv("TRAVIS_GO_VERSION")
  174. }
  175. type logger interface {
  176. Error(args ...interface{})
  177. Errorf(format string, args ...interface{})
  178. Fatal(args ...interface{})
  179. Fatalf(format string, args ...interface{})
  180. Fail()
  181. FailNow()
  182. Log(args ...interface{})
  183. Logf(format string, args ...interface{})
  184. }
  185. // strictDecoder returns an error if any JSON fields aren't decoded.
  186. type strictDecoder struct{}
  187. func (d *strictDecoder) Decode(data []byte, v interface{}) error {
  188. dec := json.NewDecoder(bytes.NewReader(data))
  189. dec.DisallowUnknownFields()
  190. return dec.Decode(v)
  191. }
  192. var (
  193. logDeprecations = flag.String("deprecations", "off", "log or fail on deprecation warnings")
  194. strict = flag.Bool("strict-decoder", false, "treat missing unknown fields in response as errors")
  195. )
  196. func setupTestClient(t logger, options ...ClientOptionFunc) (client *Client) {
  197. var err error
  198. client, err = NewClient(options...)
  199. if err != nil {
  200. t.Fatal(err)
  201. }
  202. // Use strict JSON decoder (unless a specific decoder has been specified already)
  203. if *strict {
  204. if client.decoder == nil {
  205. client.decoder = &strictDecoder{}
  206. } else if _, ok := client.decoder.(*DefaultDecoder); ok {
  207. client.decoder = &strictDecoder{}
  208. }
  209. }
  210. // Log deprecations during tests
  211. if loglevel := *logDeprecations; loglevel != "off" {
  212. client.deprecationlog = func(req *http.Request, res *http.Response) {
  213. for _, warning := range res.Header["Warning"] {
  214. switch loglevel {
  215. default:
  216. t.Logf("[%s] Deprecation warning: %s", req.URL, warning)
  217. case "fail", "error":
  218. t.Errorf("[%s] Deprecation warning: %s", req.URL, warning)
  219. }
  220. }
  221. }
  222. }
  223. client.DeleteIndex(testIndexName).Do(context.TODO())
  224. client.DeleteIndex(testIndexName2).Do(context.TODO())
  225. return client
  226. }
  227. func setupTestClientAndCreateIndex(t logger, options ...ClientOptionFunc) *Client {
  228. client := setupTestClient(t, options...)
  229. // Create index
  230. createIndex, err := client.CreateIndex(testIndexName).Body(testMapping).Do(context.TODO())
  231. if err != nil {
  232. t.Fatal(err)
  233. }
  234. if createIndex == nil {
  235. t.Errorf("expected result to be != nil; got: %v", createIndex)
  236. }
  237. // Create second index
  238. createIndex2, err := client.CreateIndex(testIndexName2).Body(testMapping).Do(context.TODO())
  239. if err != nil {
  240. t.Fatal(err)
  241. }
  242. if createIndex2 == nil {
  243. t.Errorf("expected result to be != nil; got: %v", createIndex2)
  244. }
  245. return client
  246. }
  247. func setupTestClientAndCreateIndexAndLog(t logger, options ...ClientOptionFunc) *Client {
  248. return setupTestClientAndCreateIndex(t, SetTraceLog(log.New(os.Stdout, "", 0)))
  249. }
  250. func setupTestClientAndCreateIndexAndAddDocs(t logger, options ...ClientOptionFunc) *Client {
  251. client := setupTestClientAndCreateIndex(t, options...)
  252. // Add tweets
  253. tweet1 := tweet{User: "olivere", Message: "Welcome to Golang and Elasticsearch."}
  254. tweet2 := tweet{User: "olivere", Message: "Another unrelated topic."}
  255. tweet3 := tweet{User: "sandrae", Message: "Cycling is fun."}
  256. comment1 := comment{User: "nico", Comment: "You bet."}
  257. _, err := client.Index().Index(testIndexName).Type("tweet").Id("1").BodyJson(&tweet1).Do(context.TODO())
  258. if err != nil {
  259. t.Fatal(err)
  260. }
  261. _, err = client.Index().Index(testIndexName).Type("tweet").Id("2").BodyJson(&tweet2).Do(context.TODO())
  262. if err != nil {
  263. t.Fatal(err)
  264. }
  265. _, err = client.Index().Index(testIndexName).Type("tweet").Id("3").Routing("someroutingkey").BodyJson(&tweet3).Do(context.TODO())
  266. if err != nil {
  267. t.Fatal(err)
  268. }
  269. _, err = client.Index().Index(testIndexName).Type("comment").Id("1").Parent("3").BodyJson(&comment1).Do(context.TODO())
  270. if err != nil {
  271. t.Fatal(err)
  272. }
  273. // Add orders
  274. var orders []order
  275. orders = append(orders, order{Article: "Apple MacBook", Manufacturer: "Apple", Price: 1290, Time: "2015-01-18"})
  276. orders = append(orders, order{Article: "Paper", Manufacturer: "Canon", Price: 100, Time: "2015-03-01"})
  277. orders = append(orders, order{Article: "Apple iPad", Manufacturer: "Apple", Price: 499, Time: "2015-04-12"})
  278. orders = append(orders, order{Article: "Dell XPS 13", Manufacturer: "Dell", Price: 1600, Time: "2015-04-18"})
  279. orders = append(orders, order{Article: "Apple Watch", Manufacturer: "Apple", Price: 349, Time: "2015-04-29"})
  280. orders = append(orders, order{Article: "Samsung TV", Manufacturer: "Samsung", Price: 790, Time: "2015-05-03"})
  281. orders = append(orders, order{Article: "Hoodie", Manufacturer: "h&m", Price: 49, Time: "2015-06-03"})
  282. orders = append(orders, order{Article: "T-Shirt", Manufacturer: "h&m", Price: 19, Time: "2015-06-18"})
  283. for i, o := range orders {
  284. id := fmt.Sprintf("%d", i)
  285. _, err = client.Index().Index(testIndexName).Type("order").Id(id).BodyJson(&o).Do(context.TODO())
  286. if err != nil {
  287. t.Fatal(err)
  288. }
  289. }
  290. // Flush
  291. _, err = client.Flush().Index(testIndexName).Do(context.TODO())
  292. if err != nil {
  293. t.Fatal(err)
  294. }
  295. return client
  296. }
  297. func setupTestClientAndCreateIndexAndAddDocsNoSource(t logger, options ...ClientOptionFunc) *Client {
  298. client := setupTestClientAndCreateIndex(t, options...)
  299. // Add tweets
  300. tweet1 := tweet{User: "olivere", Message: "Welcome to Golang and Elasticsearch."}
  301. tweet2 := tweet{User: "olivere", Message: "Another unrelated topic."}
  302. _, err := client.Index().Index(testIndexName).Type("tweet-nosource").Id("1").BodyJson(&tweet1).Do(context.TODO())
  303. if err != nil {
  304. t.Fatal(err)
  305. }
  306. _, err = client.Index().Index(testIndexName).Type("tweet-nosource").Id("2").BodyJson(&tweet2).Do(context.TODO())
  307. if err != nil {
  308. t.Fatal(err)
  309. }
  310. // Flush
  311. _, err = client.Flush().Index(testIndexName).Do(context.TODO())
  312. if err != nil {
  313. t.Fatal(err)
  314. }
  315. return client
  316. }
  317. var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
  318. func randomString(n int) string {
  319. b := make([]rune, n)
  320. for i := range b {
  321. b[i] = letters[rand.Intn(len(letters))]
  322. }
  323. return string(b)
  324. }