github.com/influxdata/influxdb/v2@v2.7.6/replications/transport/http_test.go (about)

     1  package transport
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"testing"
     9  
    10  	"github.com/golang/mock/gomock"
    11  	"github.com/influxdata/influxdb/v2"
    12  	"github.com/influxdata/influxdb/v2/kit/platform"
    13  	"github.com/influxdata/influxdb/v2/replications/mock"
    14  	"github.com/stretchr/testify/assert"
    15  	tmock "github.com/stretchr/testify/mock"
    16  	"github.com/stretchr/testify/require"
    17  	"go.uber.org/zap/zaptest"
    18  )
    19  
    20  //go:generate go run github.com/golang/mock/mockgen -package mock -destination ../mock/service.go github.com/influxdata/influxdb/v2/replications/transport ReplicationService
    21  
    22  var (
    23  	orgStr            = "1234123412341234"
    24  	orgID, _          = platform.IDFromString(orgStr)
    25  	remoteStr         = "9876987698769876"
    26  	remoteID, _       = platform.IDFromString(remoteStr)
    27  	idStr             = "4321432143214321"
    28  	id, _             = platform.IDFromString(idStr)
    29  	localBucketStr    = "1111111111111111"
    30  	localBucketId, _  = platform.IDFromString(localBucketStr)
    31  	remoteBucketStr   = "1234567887654321"
    32  	remoteBucketID, _ = platform.IDFromString(remoteBucketStr)
    33  	testReplication   = influxdb.Replication{
    34  		ID:                *id,
    35  		OrgID:             *orgID,
    36  		RemoteID:          *remoteID,
    37  		LocalBucketID:     *localBucketId,
    38  		RemoteBucketID:    remoteBucketID,
    39  		Name:              "example",
    40  		MaxQueueSizeBytes: influxdb.DefaultReplicationMaxQueueSizeBytes,
    41  	}
    42  )
    43  
    44  func TestReplicationHandler(t *testing.T) {
    45  	t.Run("get replications happy path", func(t *testing.T) {
    46  		ts, svc := newTestServer(t)
    47  		defer ts.Close()
    48  
    49  		req := newTestRequest(t, "GET", ts.URL, nil)
    50  
    51  		q := req.URL.Query()
    52  		q.Add("orgID", orgStr)
    53  		q.Add("name", testReplication.Name)
    54  		q.Add("remoteID", remoteStr)
    55  		q.Add("localBucketID", localBucketStr)
    56  		req.URL.RawQuery = q.Encode()
    57  
    58  		expected := influxdb.Replications{Replications: []influxdb.Replication{testReplication}}
    59  
    60  		svc.EXPECT().
    61  			ListReplications(gomock.Any(), tmock.MatchedBy(func(in influxdb.ReplicationListFilter) bool {
    62  				return assert.Equal(t, *orgID, in.OrgID) &&
    63  					assert.Equal(t, testReplication.Name, *in.Name) &&
    64  					assert.Equal(t, testReplication.RemoteID, *in.RemoteID) &&
    65  					assert.Equal(t, testReplication.LocalBucketID, *in.LocalBucketID)
    66  			})).Return(&expected, nil)
    67  
    68  		res := doTestRequest(t, req, http.StatusOK, true)
    69  
    70  		var got influxdb.Replications
    71  		require.NoError(t, json.NewDecoder(res.Body).Decode(&got))
    72  		require.Equal(t, expected, got)
    73  	})
    74  
    75  	t.Run("create replication happy path", func(t *testing.T) {
    76  
    77  		body := influxdb.CreateReplicationRequest{
    78  			OrgID:          testReplication.OrgID,
    79  			Name:           testReplication.Name,
    80  			RemoteID:       testReplication.RemoteID,
    81  			LocalBucketID:  testReplication.LocalBucketID,
    82  			RemoteBucketID: *testReplication.RemoteBucketID,
    83  		}
    84  
    85  		t.Run("with explicit queue size", func(t *testing.T) {
    86  			ts, svc := newTestServer(t)
    87  			defer ts.Close()
    88  
    89  			body := body
    90  			body.MaxQueueSizeBytes = 2 * influxdb.DefaultReplicationMaxQueueSizeBytes
    91  
    92  			req := newTestRequest(t, "POST", ts.URL, &body)
    93  
    94  			svc.EXPECT().CreateReplication(gomock.Any(), body).Return(&testReplication, nil)
    95  
    96  			res := doTestRequest(t, req, http.StatusCreated, true)
    97  
    98  			var got influxdb.Replication
    99  			require.NoError(t, json.NewDecoder(res.Body).Decode(&got))
   100  			require.Equal(t, testReplication, got)
   101  		})
   102  
   103  		t.Run("with default queue size", func(t *testing.T) {
   104  			ts, svc := newTestServer(t)
   105  			defer ts.Close()
   106  
   107  			req := newTestRequest(t, "POST", ts.URL, &body)
   108  
   109  			expectedBody := body
   110  			expectedBody.MaxQueueSizeBytes = influxdb.DefaultReplicationMaxQueueSizeBytes
   111  
   112  			svc.EXPECT().CreateReplication(gomock.Any(), expectedBody).Return(&testReplication, nil)
   113  
   114  			res := doTestRequest(t, req, http.StatusCreated, true)
   115  
   116  			var got influxdb.Replication
   117  			require.NoError(t, json.NewDecoder(res.Body).Decode(&got))
   118  			require.Equal(t, testReplication, got)
   119  		})
   120  	})
   121  
   122  	t.Run("dry-run create happy path", func(t *testing.T) {
   123  
   124  		body := influxdb.CreateReplicationRequest{
   125  			OrgID:          testReplication.OrgID,
   126  			Name:           testReplication.Name,
   127  			RemoteID:       testReplication.RemoteID,
   128  			LocalBucketID:  testReplication.LocalBucketID,
   129  			RemoteBucketID: *testReplication.RemoteBucketID,
   130  		}
   131  
   132  		t.Run("with explicit queue size", func(t *testing.T) {
   133  			ts, svc := newTestServer(t)
   134  			defer ts.Close()
   135  
   136  			body := body
   137  			body.MaxQueueSizeBytes = 2 * influxdb.DefaultReplicationMaxQueueSizeBytes
   138  
   139  			req := newTestRequest(t, "POST", ts.URL, &body)
   140  			q := req.URL.Query()
   141  			q.Add("validate", "true")
   142  			req.URL.RawQuery = q.Encode()
   143  
   144  			svc.EXPECT().ValidateNewReplication(gomock.Any(), body).Return(nil)
   145  
   146  			doTestRequest(t, req, http.StatusNoContent, false)
   147  		})
   148  
   149  		t.Run("with default queue size", func(t *testing.T) {
   150  			ts, svc := newTestServer(t)
   151  			defer ts.Close()
   152  
   153  			req := newTestRequest(t, "POST", ts.URL, &body)
   154  			q := req.URL.Query()
   155  			q.Add("validate", "true")
   156  			req.URL.RawQuery = q.Encode()
   157  
   158  			expectedBody := body
   159  			expectedBody.MaxQueueSizeBytes = influxdb.DefaultReplicationMaxQueueSizeBytes
   160  
   161  			svc.EXPECT().ValidateNewReplication(gomock.Any(), expectedBody).Return(nil)
   162  
   163  			doTestRequest(t, req, http.StatusNoContent, false)
   164  		})
   165  	})
   166  
   167  	t.Run("get replication happy path", func(t *testing.T) {
   168  		ts, svc := newTestServer(t)
   169  		defer ts.Close()
   170  
   171  		req := newTestRequest(t, "GET", ts.URL+"/"+id.String(), nil)
   172  
   173  		svc.EXPECT().GetReplication(gomock.Any(), *id).Return(&testReplication, nil)
   174  
   175  		res := doTestRequest(t, req, http.StatusOK, true)
   176  
   177  		var got influxdb.Replication
   178  		require.NoError(t, json.NewDecoder(res.Body).Decode(&got))
   179  		require.Equal(t, testReplication, got)
   180  	})
   181  
   182  	t.Run("delete replication happy path", func(t *testing.T) {
   183  		ts, svc := newTestServer(t)
   184  		defer ts.Close()
   185  
   186  		req := newTestRequest(t, "DELETE", ts.URL+"/"+id.String(), nil)
   187  
   188  		svc.EXPECT().DeleteReplication(gomock.Any(), *id).Return(nil)
   189  
   190  		doTestRequest(t, req, http.StatusNoContent, false)
   191  	})
   192  
   193  	t.Run("update replication happy path", func(t *testing.T) {
   194  		ts, svc := newTestServer(t)
   195  		defer ts.Close()
   196  
   197  		newDescription := "my cool replication"
   198  		newQueueSize := 3 * influxdb.DefaultReplicationMaxQueueSizeBytes
   199  		body := influxdb.UpdateReplicationRequest{Description: &newDescription, MaxQueueSizeBytes: &newQueueSize}
   200  
   201  		req := newTestRequest(t, "PATCH", ts.URL+"/"+id.String(), body)
   202  
   203  		svc.EXPECT().UpdateReplication(gomock.Any(), *id, body).Return(&testReplication, nil)
   204  
   205  		res := doTestRequest(t, req, http.StatusOK, true)
   206  
   207  		var got influxdb.Replication
   208  		require.NoError(t, json.NewDecoder(res.Body).Decode(&got))
   209  		require.Equal(t, testReplication, got)
   210  	})
   211  
   212  	t.Run("dry-run update happy path", func(t *testing.T) {
   213  		ts, svc := newTestServer(t)
   214  		defer ts.Close()
   215  
   216  		newDescription := "my cool replication"
   217  		newQueueSize := 3 * influxdb.DefaultReplicationMaxQueueSizeBytes
   218  		body := influxdb.UpdateReplicationRequest{Description: &newDescription, MaxQueueSizeBytes: &newQueueSize}
   219  
   220  		req := newTestRequest(t, "PATCH", ts.URL+"/"+id.String(), body)
   221  		q := req.URL.Query()
   222  		q.Add("validate", "true")
   223  		req.URL.RawQuery = q.Encode()
   224  
   225  		svc.EXPECT().ValidateUpdatedReplication(gomock.Any(), *id, body).Return(nil)
   226  
   227  		doTestRequest(t, req, http.StatusNoContent, false)
   228  	})
   229  
   230  	t.Run("validate replication happy path", func(t *testing.T) {
   231  		ts, svc := newTestServer(t)
   232  		defer ts.Close()
   233  
   234  		req := newTestRequest(t, "POST", ts.URL+"/"+id.String()+"/validate", nil)
   235  
   236  		svc.EXPECT().ValidateReplication(gomock.Any(), *id).Return(nil)
   237  
   238  		doTestRequest(t, req, http.StatusNoContent, false)
   239  	})
   240  
   241  	t.Run("invalid replication IDs return 400", func(t *testing.T) {
   242  		ts, _ := newTestServer(t)
   243  		defer ts.Close()
   244  
   245  		req1 := newTestRequest(t, "GET", ts.URL+"/foo", nil)
   246  		req2 := newTestRequest(t, "PATCH", ts.URL+"/foo", &influxdb.UpdateReplicationRequest{})
   247  		req3 := newTestRequest(t, "DELETE", ts.URL+"/foo", nil)
   248  
   249  		for _, req := range []*http.Request{req1, req2, req3} {
   250  			t.Run(req.Method, func(t *testing.T) {
   251  				doTestRequest(t, req, http.StatusBadRequest, true)
   252  			})
   253  		}
   254  	})
   255  
   256  	t.Run("invalid org ID to GET /replications returns 400", func(t *testing.T) {
   257  		ts, _ := newTestServer(t)
   258  		defer ts.Close()
   259  
   260  		req := newTestRequest(t, "GET", ts.URL, nil)
   261  		q := req.URL.Query()
   262  		q.Add("orgID", "foo")
   263  		req.URL.RawQuery = q.Encode()
   264  
   265  		doTestRequest(t, req, http.StatusBadRequest, true)
   266  	})
   267  
   268  	t.Run("invalid request bodies return 400", func(t *testing.T) {
   269  		ts, _ := newTestServer(t)
   270  		defer ts.Close()
   271  
   272  		body := "o no not an object"
   273  		req1 := newTestRequest(t, "POST", ts.URL, &body)
   274  		req2 := newTestRequest(t, "PATCH", ts.URL+"/"+id.String(), &body)
   275  
   276  		for _, req := range []*http.Request{req1, req2} {
   277  			t.Run(req.Method, func(t *testing.T) {
   278  				doTestRequest(t, req, http.StatusBadRequest, true)
   279  			})
   280  		}
   281  	})
   282  
   283  	t.Run("too-small queue size on create is rejected", func(t *testing.T) {
   284  		ts, _ := newTestServer(t)
   285  		defer ts.Close()
   286  
   287  		body := influxdb.CreateReplicationRequest{
   288  			OrgID:             testReplication.OrgID,
   289  			Name:              testReplication.Name,
   290  			RemoteID:          testReplication.RemoteID,
   291  			LocalBucketID:     testReplication.LocalBucketID,
   292  			RemoteBucketID:    *testReplication.RemoteBucketID,
   293  			MaxQueueSizeBytes: influxdb.MinReplicationMaxQueueSizeBytes / 2,
   294  		}
   295  
   296  		req := newTestRequest(t, "POST", ts.URL, &body)
   297  
   298  		doTestRequest(t, req, http.StatusBadRequest, true)
   299  	})
   300  
   301  	t.Run("too-small queue size on update is rejected", func(t *testing.T) {
   302  		ts, _ := newTestServer(t)
   303  		defer ts.Close()
   304  
   305  		newSize := influxdb.MinReplicationMaxQueueSizeBytes / 2
   306  		body := influxdb.UpdateReplicationRequest{MaxQueueSizeBytes: &newSize}
   307  
   308  		req := newTestRequest(t, "PATCH", ts.URL+"/"+id.String(), &body)
   309  
   310  		doTestRequest(t, req, http.StatusBadRequest, true)
   311  	})
   312  }
   313  
   314  func newTestServer(t *testing.T) (*httptest.Server, *mock.MockReplicationService) {
   315  	ctrl := gomock.NewController(t)
   316  	svc := mock.NewMockReplicationService(ctrl)
   317  	server := newReplicationHandler(zaptest.NewLogger(t), svc)
   318  	return httptest.NewServer(server), svc
   319  }
   320  
   321  func newTestRequest(t *testing.T, method, path string, body interface{}) *http.Request {
   322  	dat, err := json.Marshal(body)
   323  	require.NoError(t, err)
   324  
   325  	req, err := http.NewRequest(method, path, bytes.NewBuffer(dat))
   326  	require.NoError(t, err)
   327  
   328  	req.Header.Add("Content-Type", "application/json")
   329  
   330  	return req
   331  }
   332  
   333  func doTestRequest(t *testing.T, req *http.Request, wantCode int, needJSON bool) *http.Response {
   334  	res, err := http.DefaultClient.Do(req)
   335  	require.NoError(t, err)
   336  	require.Equal(t, wantCode, res.StatusCode)
   337  	if needJSON {
   338  		require.Equal(t, "application/json; charset=utf-8", res.Header.Get("Content-Type"))
   339  	}
   340  	return res
   341  }