github.com/opensearch-project/opensearch-go/v2@v2.3.0/opensearchapi/api.document_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // 3 // The OpenSearch Contributors require contributions made to 4 // this file be licensed under the Apache-2.0 license or a 5 // compatible open source license. 6 // 7 // Modifications Copyright OpenSearch Contributors. See 8 // GitHub history for details. 9 10 //go:build integration 11 // +build integration 12 13 package opensearchapi_test 14 15 import ( 16 "bytes" 17 "context" 18 "encoding/json" 19 "fmt" 20 "github.com/opensearch-project/opensearch-go/v2" 21 "github.com/opensearch-project/opensearch-go/v2/opensearchapi" 22 "github.com/stretchr/testify/require" 23 "io/ioutil" 24 "net/http" 25 "regexp" 26 "strings" 27 "testing" 28 "time" 29 ) 30 31 func TestDocumentRequest_Do(t *testing.T) { 32 index := fmt.Sprintf("index-%s", time.Now().Format("2006-01-02-15-04-05")) 33 34 tests := []struct { 35 name string 36 r DataStreamRequest 37 want *opensearchapi.Response 38 wantBody string 39 wantErr bool 40 refresh bool 41 }{ 42 // Create document 43 { 44 name: "TestCreateRequest_Do", 45 r: opensearchapi.CreateRequest{ 46 Index: index, 47 DocumentID: "1", 48 Body: strings.NewReader(`{ "title": "Moneyball", "director": "Bennett Miller", "year": "2011" }`), 49 }, 50 want: &opensearchapi.Response{ 51 StatusCode: 201, 52 Header: http.Header{ 53 "Content-Type": []string{"application/json; charset=UTF-8"}, 54 "Location": []string{fmt.Sprintf("/%s/_doc/1", index)}, 55 }, 56 }, 57 wantBody: fmt.Sprintf(`{"_index":"%s","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":0,"_primary_term":1}`, index), 58 wantErr: false, 59 }, 60 { 61 name: "TestCreateRequest_Do", 62 r: opensearchapi.CreateRequest{ 63 Index: index, 64 DocumentID: "2", 65 Body: strings.NewReader(`{ "title": "Tenet", "director": "Christopher Nolan", "year": "2019" }`), 66 }, 67 want: &opensearchapi.Response{ 68 StatusCode: 201, 69 Header: http.Header{ 70 "Content-Type": []string{"application/json; charset=UTF-8"}, 71 "Location": []string{fmt.Sprintf("/%s/_doc/2", index)}, 72 }, 73 }, 74 wantBody: fmt.Sprintf(`{"_index":"%s","_id":"2","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":1,"_primary_term":1}`, index), 75 wantErr: false, 76 }, 77 78 // Get document 79 { 80 name: "TestGetRequest_Do", 81 r: opensearchapi.GetRequest{ 82 Index: index, 83 DocumentID: "2", 84 Source: true, 85 }, 86 want: &opensearchapi.Response{ 87 StatusCode: 200, 88 Header: http.Header{ 89 "Content-Type": []string{"application/json; charset=UTF-8"}, 90 }, 91 }, 92 wantBody: fmt.Sprintf(`{"_id":"2", "_index":"%s", "_primary_term":1, "_seq_no":1, "_source": {"director":"Christopher Nolan", "title":"Tenet", "year":"2019"}, "_version":1, "found":true}`, index), 93 wantErr: false, 94 }, 95 // Get multiple documents 96 { 97 name: "TestMultiGetRequest_Do. Source parameter is a bool and slice of strings", 98 r: opensearchapi.MgetRequest{ 99 Index: index, 100 Body: strings.NewReader(`{ "docs": [ { "_id": "1", "_source": true }, { "_id": "2", "_source": [ "title" ] } ] }`), 101 // seems to does not work 102 Source: false, 103 }, 104 want: &opensearchapi.Response{ 105 StatusCode: 200, 106 Header: http.Header{ 107 "Content-Type": []string{"application/json; charset=UTF-8"}, 108 }, 109 }, 110 wantBody: fmt.Sprintf(`{ "docs": [ { "_id": "1", "_index": "%s", "_primary_term": 1, "_seq_no": 0, "_source": { "director": "Bennett Miller", "title": "Moneyball", "year": "2011" }, "_version": 1, "found": true }, { "_id": "2", "_index": "%s", "_primary_term": 1, "_seq_no": 1, "_source": {"title":"Tenet"}, "_version": 1, "found": true } ] }`, index, index), 111 wantErr: false, 112 }, 113 // Get source document 114 { 115 name: "TestGetSourceRequest_Do. Source parameter is a bool and slice of strings", 116 r: opensearchapi.GetSourceRequest{ 117 Index: index, 118 DocumentID: "2", 119 }, 120 want: &opensearchapi.Response{ 121 StatusCode: 200, 122 Header: http.Header{ 123 "Content-Type": []string{"application/json; charset=UTF-8"}, 124 }, 125 }, 126 wantBody: `{"director":"Christopher Nolan", "title":"Tenet", "year":"2019"}`, 127 wantErr: false, 128 }, 129 130 // Exists document 131 { 132 name: "TestExistsRequest_Do", 133 r: opensearchapi.ExistsRequest{ 134 Index: index, 135 DocumentID: "2", 136 Source: true, 137 }, 138 want: &opensearchapi.Response{ 139 StatusCode: 200, 140 Header: http.Header{ 141 "Content-Type": []string{"application/json; charset=UTF-8"}, 142 "Content-Length": []string{"189"}, 143 }, 144 }, 145 wantBody: ``, 146 wantErr: false, 147 }, 148 149 // Explain document 150 { 151 name: "TestExplainRequest_Do", 152 r: opensearchapi.ExplainRequest{ 153 Index: index, 154 DocumentID: "2", 155 Query: `title: "Tenet"`, 156 Source: true, 157 }, 158 want: &opensearchapi.Response{ 159 StatusCode: 200, 160 Header: http.Header{ 161 "Content-Type": []string{"application/json; charset=UTF-8"}, 162 }, 163 }, 164 wantBody: ``, 165 wantErr: false, 166 refresh: true, 167 }, 168 169 // Search document 170 { 171 name: "TestSearchRequest_Do. Source parameter is a slice of strings", 172 r: opensearchapi.SearchRequest{ 173 Index: []string{index}, 174 Body: strings.NewReader(`{ "query": { "match": { "title": "Tenet" } } }`), 175 Source: true, 176 }, 177 want: &opensearchapi.Response{ 178 StatusCode: 200, 179 Header: http.Header{ 180 "Content-Type": []string{"application/json; charset=UTF-8"}, 181 }, 182 }, 183 wantBody: fmt.Sprintf(`{"took":0,"timed_out":false,"_shards":{"total":4,"successful":4,"skipped":0,"failed":0},"hits":{"total":{"value":1,"relation":"eq"},"max_score":0.6931471,"hits":[{"_index":"%s","_id":"2","_score":0.6931471,"_source":{ "title": "Tenet", "director": "Christopher Nolan", "year": "2019" }}]}}`, index), 184 wantErr: false, 185 refresh: true, 186 }, 187 188 // Update document 189 { 190 name: "TestUpdateRequest_Do", 191 r: opensearchapi.UpdateRequest{ 192 Index: index, 193 DocumentID: "1", 194 Body: strings.NewReader(`{ "doc": { "title": "Moneyball", "director": "Bennett", "year": "2012" } }`), 195 Source: nil, 196 }, 197 want: &opensearchapi.Response{ 198 StatusCode: 200, 199 Header: http.Header{ 200 "Content-Type": []string{"application/json; charset=UTF-8"}, 201 }, 202 }, 203 wantBody: fmt.Sprintf(`{"_index":"%s","_id":"1","_version":2,"result":"updated","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":2,"_primary_term":1}`, index), 204 wantErr: false, 205 }, 206 { 207 name: "TestUpdateRequest_Do. Source parameter is bool", 208 r: opensearchapi.UpdateRequest{ 209 Index: index, 210 DocumentID: "1", 211 Body: strings.NewReader(`{ "doc": { "title": "Moneyball", "director": "Bennett", "year": "2012" } }`), 212 Source: true, 213 }, 214 want: &opensearchapi.Response{ 215 StatusCode: 200, 216 Header: http.Header{ 217 "Content-Type": []string{"application/json; charset=UTF-8"}, 218 }, 219 }, 220 wantBody: fmt.Sprintf(`{"_index":"%s","_id":"1","_version":2,"result":"noop","_shards":{"total":0,"successful":0,"failed":0},"_seq_no":2,"_primary_term":1,"get":{"_seq_no":2,"_primary_term":1,"found":true,"_source":{"title":"Moneyball","director":"Bennett","year":"2012"}}}`, index), 221 wantErr: false, 222 }, 223 { 224 name: "TestUpdateRequest_Do. Source parameter is a slice of strings", 225 r: opensearchapi.UpdateRequest{ 226 Index: index, 227 DocumentID: "1", 228 Body: strings.NewReader(`{ "doc": { "title": "Moneyball", "director": "Bennett", "year": "2012" } }`), 229 Source: []string{"true"}, 230 }, 231 want: &opensearchapi.Response{ 232 StatusCode: 200, 233 Header: http.Header{ 234 "Content-Type": []string{"application/json; charset=UTF-8"}, 235 }, 236 }, 237 wantBody: fmt.Sprintf(`{"_index":"%s","_id":"1","_version":2,"result":"noop","_shards":{"total":0,"successful":0,"failed":0},"_seq_no":2,"_primary_term":1,"get":{"_seq_no":2,"_primary_term":1,"found":true,"_source":{"title":"Moneyball","director":"Bennett","year":"2012"}}}`, index), 238 wantErr: false, 239 }, 240 { 241 name: "TestUpdateRequest_Do. Source Excludes", 242 r: opensearchapi.UpdateRequest{ 243 Index: index, 244 DocumentID: "1", 245 Body: strings.NewReader(`{ "doc": { "title": "Moneyball", "director": "Bennett", "year": "2012" } }`), 246 Source: []string{"true"}, 247 SourceExcludes: []string{"director"}, 248 }, 249 want: &opensearchapi.Response{ 250 StatusCode: 200, 251 Header: http.Header{ 252 "Content-Type": []string{"application/json; charset=UTF-8"}, 253 }, 254 }, 255 wantBody: fmt.Sprintf(`{"_index":"%s","_id":"1","_version":2,"result":"noop","_shards":{"total":0,"successful":0,"failed":0},"_seq_no":2,"_primary_term":1,"get":{"_seq_no":2,"_primary_term":1,"found":true,"_source":{"title":"Moneyball","year":"2012"}}}`, index), 256 wantErr: false, 257 }, 258 // Update by query 259 { 260 name: "TestUpdateByQueryRequest_Do. Source Excludes", 261 r: opensearchapi.UpdateByQueryRequest{ 262 Index: []string{index}, 263 Query: `title: "Tenet"`, 264 Body: strings.NewReader(`{ "script" : { "source": "ctx._source.title += params.title", "lang": "painless", "params" : { "title" : "TeneT" } } }`), 265 }, 266 want: &opensearchapi.Response{ 267 StatusCode: 200, 268 Header: http.Header{ 269 "Content-Type": []string{"application/json; charset=UTF-8"}, 270 }, 271 }, 272 wantBody: `{"batches":1, "deleted":0, "failures":[], "noops":0, "requests_per_second":-1, "retries":{"bulk":0, "search":0}, "throttled_millis":0, "throttled_until_millis":0, "timed_out":false, "took":0, "total":1, "updated":1, "version_conflicts":0}`, 273 wantErr: false, 274 refresh: true, 275 }, 276 277 // Bulk document 278 { 279 name: "TestBulkRequest_Do.", 280 r: opensearchapi.BulkRequest{ 281 Index: index, 282 Body: strings.NewReader(`{ "index": { "_index": "movies", "_id": "tt1979320" } } 283 { "title": "Rush", "year": 2013 } 284 { "create": { "_index": "movies", "_id": "tt1392214" } } 285 { "title": "Prisoners", "year": 2013 } 286 { "update": { "_index": "movies", "_id": "tt0816711" } } 287 { "doc" : { "title": "World War Z" } } 288 `), 289 Source: []string{"true"}, 290 }, 291 want: &opensearchapi.Response{ 292 StatusCode: 200, 293 Header: http.Header{ 294 "Content-Type": []string{"application/json; charset=UTF-8"}, 295 }, 296 }, 297 wantErr: false, 298 }, 299 } 300 301 client, err := opensearch.NewDefaultClient() 302 require.NoError(t, err) 303 304 iCreate := opensearchapi.IndicesCreateRequest{ 305 Index: index, 306 Pretty: true, 307 Human: true, 308 ErrorTrace: true, 309 Body: strings.NewReader(fmt.Sprintf(`{"settings": {"index": {"number_of_shards": 4}}}`)), 310 } 311 312 _, err = iCreate.Do(context.Background(), client) 313 require.NoError(t, err) 314 315 for _, tt := range tests { 316 if tt.refresh { 317 refresh, err := client.Indices.Refresh() 318 require.NoError(t, err) 319 t.Logf("response: %+v", refresh) 320 } 321 322 t.Run(tt.name, func(t *testing.T) { 323 got, err := tt.r.Do(context.Background(), client) 324 if (err != nil) != tt.wantErr { 325 t.Errorf("Do() error = %+v, wantErr %v", err, tt.wantErr) 326 return 327 } 328 329 require.Equalf(t, got.StatusCode, tt.want.StatusCode, "Do() got = %v, want %v", got.StatusCode, tt.want.StatusCode) 330 331 if tt.wantBody != "" { 332 require.Equalf(t, got.Header, tt.want.Header, "Do() got = %v, want %v", got.Header, tt.want.Header) 333 334 defer got.Body.Close() 335 body, err := ioutil.ReadAll(got.Body) 336 require.NoError(t, err) 337 338 buffer := new(bytes.Buffer) 339 err = json.Compact(buffer, body) 340 require.NoError(t, err) 341 342 // ignore took field, since it is dynamic 343 took := regexp.MustCompile(`"took":\d+`) 344 actual := took.ReplaceAllString(buffer.String(), `"took":0`) 345 // ignore _type field, since it is legacy 346 actual = strings.ReplaceAll(actual, `"_type":"_doc",`, "") 347 348 require.JSONEqf(t, tt.wantBody, actual, "Do() got = %v, want %v", got.String(), tt.wantBody) 349 } 350 }) 351 } 352 }