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  }