package transport import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/golang/mock/gomock" "github.com/influxdata/influxdb/v2" "github.com/influxdata/influxdb/v2/kit/platform" "github.com/influxdata/influxdb/v2/remotes/mock" "github.com/stretchr/testify/assert" tmock "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) //go:generate go run github.com/golang/mock/mockgen -package mock -destination ../mock/service.go github.com/influxdata/influxdb/v2/remotes/transport RemoteConnectionService var ( orgStr = "1234123412341234" orgID, _ = platform.IDFromString(orgStr) remoteOrgStr = "9876987698769876" remoteOrgID, _ = platform.IDFromString(remoteOrgStr) idStr = "4321432143214321" id, _ = platform.IDFromString(idStr) testConn = influxdb.RemoteConnection{ ID: *id, OrgID: *orgID, Name: "example", RemoteURL: "https://influxdb.cloud", RemoteOrgID: remoteOrgID, AllowInsecureTLS: true, } ) func TestRemoteConnectionHandler(t *testing.T) { t.Run("get remotes happy path", func(t *testing.T) { ts, svc := newTestServer(t) defer ts.Close() req := newTestRequest(t, "GET", ts.URL, nil) q := req.URL.Query() q.Add("orgID", orgStr) q.Add("name", testConn.Name) q.Add("remoteURL", testConn.RemoteURL) req.URL.RawQuery = q.Encode() expected := influxdb.RemoteConnections{Remotes: []influxdb.RemoteConnection{testConn}} svc.EXPECT(). ListRemoteConnections(gomock.Any(), tmock.MatchedBy(func(in influxdb.RemoteConnectionListFilter) bool { return assert.Equal(t, *orgID, in.OrgID) && assert.Equal(t, testConn.Name, *in.Name) && assert.Equal(t, testConn.RemoteURL, *in.RemoteURL) })).Return(&expected, nil) res := doTestRequest(t, req, http.StatusOK, true) var got influxdb.RemoteConnections require.NoError(t, json.NewDecoder(res.Body).Decode(&got)) require.Equal(t, expected, got) }) t.Run("create remote happy path", func(t *testing.T) { ts, svc := newTestServer(t) defer ts.Close() body := influxdb.CreateRemoteConnectionRequest{ OrgID: testConn.OrgID, Name: testConn.Name, RemoteURL: testConn.RemoteURL, RemoteToken: "my super secret token", RemoteOrgID: testConn.RemoteOrgID, AllowInsecureTLS: testConn.AllowInsecureTLS, } req := newTestRequest(t, "POST", ts.URL, &body) svc.EXPECT().CreateRemoteConnection(gomock.Any(), body).Return(&testConn, nil) res := doTestRequest(t, req, http.StatusCreated, true) var got influxdb.RemoteConnection require.NoError(t, json.NewDecoder(res.Body).Decode(&got)) require.Equal(t, testConn, got) }) t.Run("get remote happy path", func(t *testing.T) { ts, svc := newTestServer(t) defer ts.Close() req := newTestRequest(t, "GET", ts.URL+"/"+id.String(), nil) svc.EXPECT().GetRemoteConnection(gomock.Any(), *id).Return(&testConn, nil) res := doTestRequest(t, req, http.StatusOK, true) var got influxdb.RemoteConnection require.NoError(t, json.NewDecoder(res.Body).Decode(&got)) require.Equal(t, testConn, got) }) t.Run("delete remote happy path", func(t *testing.T) { ts, svc := newTestServer(t) defer ts.Close() req := newTestRequest(t, "DELETE", ts.URL+"/"+id.String(), nil) svc.EXPECT().DeleteRemoteConnection(gomock.Any(), *id).Return(nil) doTestRequest(t, req, http.StatusNoContent, false) }) t.Run("update remote happy path", func(t *testing.T) { ts, svc := newTestServer(t) defer ts.Close() newToken := "a new even more secret token" body := influxdb.UpdateRemoteConnectionRequest{RemoteToken: &newToken} req := newTestRequest(t, "PATCH", ts.URL+"/"+id.String(), &body) svc.EXPECT().UpdateRemoteConnection(gomock.Any(), *id, body).Return(&testConn, nil) res := doTestRequest(t, req, http.StatusOK, true) var got influxdb.RemoteConnection require.NoError(t, json.NewDecoder(res.Body).Decode(&got)) require.Equal(t, testConn, got) }) t.Run("invalid remote IDs return 400", func(t *testing.T) { ts, _ := newTestServer(t) defer ts.Close() req1 := newTestRequest(t, "GET", ts.URL+"/foo", nil) req2 := newTestRequest(t, "PATCH", ts.URL+"/foo", &influxdb.UpdateRemoteConnectionRequest{}) req3 := newTestRequest(t, "DELETE", ts.URL+"/foo", nil) for _, req := range []*http.Request{req1, req2, req3} { t.Run(req.Method, func(t *testing.T) { doTestRequest(t, req, http.StatusBadRequest, true) }) } }) t.Run("invalid org ID to GET /remotes returns 400", func(t *testing.T) { ts, _ := newTestServer(t) defer ts.Close() req := newTestRequest(t, "GET", ts.URL, nil) q := req.URL.Query() q.Add("orgID", "foo") req.URL.RawQuery = q.Encode() doTestRequest(t, req, http.StatusBadRequest, true) }) t.Run("invalid request bodies return 400", func(t *testing.T) { ts, _ := newTestServer(t) defer ts.Close() body := "o no not an object" req1 := newTestRequest(t, "POST", ts.URL, &body) req2 := newTestRequest(t, "PATCH", ts.URL+"/"+id.String(), &body) for _, req := range []*http.Request{req1, req2} { t.Run(req.Method, func(t *testing.T) { doTestRequest(t, req, http.StatusBadRequest, true) }) } }) } func newTestServer(t *testing.T) (*httptest.Server, *mock.MockRemoteConnectionService) { ctrlr := gomock.NewController(t) svc := mock.NewMockRemoteConnectionService(ctrlr) server := newRemoteConnectionHandler(zaptest.NewLogger(t), svc) return httptest.NewServer(server), svc } func newTestRequest(t *testing.T, method, path string, body interface{}) *http.Request { dat, err := json.Marshal(body) require.NoError(t, err) req, err := http.NewRequest(method, path, bytes.NewBuffer(dat)) require.NoError(t, err) req.Header.Add("Content-Type", "application/json") return req } func doTestRequest(t *testing.T, req *http.Request, wantCode int, needJSON bool) *http.Response { res, err := http.DefaultClient.Do(req) require.NoError(t, err) require.Equal(t, wantCode, res.StatusCode) if needJSON { require.Equal(t, "application/json; charset=utf-8", res.Header.Get("Content-Type")) } return res }