2021-08-24 18:19:03 +00:00
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"
2021-09-07 20:21:29 +00:00
"github.com/influxdata/influxdb/v2/replications/mock"
2021-08-24 18:19:03 +00:00
"github.com/stretchr/testify/assert"
tmock "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
2021-12-02 20:04:10 +00:00
//go:generate go run github.com/golang/mock/mockgen -package mock -destination ../mock/service.go github.com/influxdata/influxdb/v2/replications/transport ReplicationService
2021-08-24 18:19:03 +00:00
var (
orgStr = "1234123412341234"
orgID , _ = platform . IDFromString ( orgStr )
remoteStr = "9876987698769876"
remoteID , _ = platform . IDFromString ( remoteStr )
idStr = "4321432143214321"
id , _ = platform . IDFromString ( idStr )
localBucketStr = "1111111111111111"
localBucketId , _ = platform . IDFromString ( localBucketStr )
remoteBucketStr = "1234567887654321"
remoteBucketID , _ = platform . IDFromString ( remoteBucketStr )
testReplication = influxdb . Replication {
ID : * id ,
OrgID : * orgID ,
RemoteID : * remoteID ,
LocalBucketID : * localBucketId ,
2022-08-18 18:21:59 +00:00
RemoteBucketID : remoteBucketID ,
2021-08-24 18:19:03 +00:00
Name : "example" ,
MaxQueueSizeBytes : influxdb . DefaultReplicationMaxQueueSizeBytes ,
}
)
func TestReplicationHandler ( t * testing . T ) {
t . Run ( "get replications 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" , testReplication . Name )
q . Add ( "remoteID" , remoteStr )
q . Add ( "localBucketID" , localBucketStr )
req . URL . RawQuery = q . Encode ( )
expected := influxdb . Replications { Replications : [ ] influxdb . Replication { testReplication } }
svc . EXPECT ( ) .
ListReplications ( gomock . Any ( ) , tmock . MatchedBy ( func ( in influxdb . ReplicationListFilter ) bool {
return assert . Equal ( t , * orgID , in . OrgID ) &&
assert . Equal ( t , testReplication . Name , * in . Name ) &&
assert . Equal ( t , testReplication . RemoteID , * in . RemoteID ) &&
assert . Equal ( t , testReplication . LocalBucketID , * in . LocalBucketID )
} ) ) . Return ( & expected , nil )
res := doTestRequest ( t , req , http . StatusOK , true )
var got influxdb . Replications
require . NoError ( t , json . NewDecoder ( res . Body ) . Decode ( & got ) )
require . Equal ( t , expected , got )
} )
t . Run ( "create replication happy path" , func ( t * testing . T ) {
body := influxdb . CreateReplicationRequest {
OrgID : testReplication . OrgID ,
Name : testReplication . Name ,
RemoteID : testReplication . RemoteID ,
LocalBucketID : testReplication . LocalBucketID ,
2022-08-18 18:21:59 +00:00
RemoteBucketID : * testReplication . RemoteBucketID ,
2021-08-24 18:19:03 +00:00
}
t . Run ( "with explicit queue size" , func ( t * testing . T ) {
ts , svc := newTestServer ( t )
defer ts . Close ( )
body := body
body . MaxQueueSizeBytes = 2 * influxdb . DefaultReplicationMaxQueueSizeBytes
req := newTestRequest ( t , "POST" , ts . URL , & body )
svc . EXPECT ( ) . CreateReplication ( gomock . Any ( ) , body ) . Return ( & testReplication , nil )
res := doTestRequest ( t , req , http . StatusCreated , true )
var got influxdb . Replication
require . NoError ( t , json . NewDecoder ( res . Body ) . Decode ( & got ) )
require . Equal ( t , testReplication , got )
} )
t . Run ( "with default queue size" , func ( t * testing . T ) {
ts , svc := newTestServer ( t )
defer ts . Close ( )
req := newTestRequest ( t , "POST" , ts . URL , & body )
expectedBody := body
expectedBody . MaxQueueSizeBytes = influxdb . DefaultReplicationMaxQueueSizeBytes
svc . EXPECT ( ) . CreateReplication ( gomock . Any ( ) , expectedBody ) . Return ( & testReplication , nil )
res := doTestRequest ( t , req , http . StatusCreated , true )
var got influxdb . Replication
require . NoError ( t , json . NewDecoder ( res . Body ) . Decode ( & got ) )
require . Equal ( t , testReplication , got )
} )
} )
t . Run ( "dry-run create happy path" , func ( t * testing . T ) {
body := influxdb . CreateReplicationRequest {
OrgID : testReplication . OrgID ,
Name : testReplication . Name ,
RemoteID : testReplication . RemoteID ,
LocalBucketID : testReplication . LocalBucketID ,
2022-08-18 18:21:59 +00:00
RemoteBucketID : * testReplication . RemoteBucketID ,
2021-08-24 18:19:03 +00:00
}
t . Run ( "with explicit queue size" , func ( t * testing . T ) {
ts , svc := newTestServer ( t )
defer ts . Close ( )
body := body
body . MaxQueueSizeBytes = 2 * influxdb . DefaultReplicationMaxQueueSizeBytes
req := newTestRequest ( t , "POST" , ts . URL , & body )
q := req . URL . Query ( )
q . Add ( "validate" , "true" )
req . URL . RawQuery = q . Encode ( )
svc . EXPECT ( ) . ValidateNewReplication ( gomock . Any ( ) , body ) . Return ( nil )
doTestRequest ( t , req , http . StatusNoContent , false )
} )
t . Run ( "with default queue size" , func ( t * testing . T ) {
ts , svc := newTestServer ( t )
defer ts . Close ( )
req := newTestRequest ( t , "POST" , ts . URL , & body )
q := req . URL . Query ( )
q . Add ( "validate" , "true" )
req . URL . RawQuery = q . Encode ( )
expectedBody := body
expectedBody . MaxQueueSizeBytes = influxdb . DefaultReplicationMaxQueueSizeBytes
svc . EXPECT ( ) . ValidateNewReplication ( gomock . Any ( ) , expectedBody ) . Return ( nil )
doTestRequest ( t , req , http . StatusNoContent , false )
} )
} )
t . Run ( "get replication happy path" , func ( t * testing . T ) {
ts , svc := newTestServer ( t )
defer ts . Close ( )
req := newTestRequest ( t , "GET" , ts . URL + "/" + id . String ( ) , nil )
svc . EXPECT ( ) . GetReplication ( gomock . Any ( ) , * id ) . Return ( & testReplication , nil )
res := doTestRequest ( t , req , http . StatusOK , true )
var got influxdb . Replication
require . NoError ( t , json . NewDecoder ( res . Body ) . Decode ( & got ) )
require . Equal ( t , testReplication , got )
} )
t . Run ( "delete replication happy path" , func ( t * testing . T ) {
ts , svc := newTestServer ( t )
defer ts . Close ( )
req := newTestRequest ( t , "DELETE" , ts . URL + "/" + id . String ( ) , nil )
svc . EXPECT ( ) . DeleteReplication ( gomock . Any ( ) , * id ) . Return ( nil )
doTestRequest ( t , req , http . StatusNoContent , false )
} )
t . Run ( "update replication happy path" , func ( t * testing . T ) {
ts , svc := newTestServer ( t )
defer ts . Close ( )
newDescription := "my cool replication"
newQueueSize := 3 * influxdb . DefaultReplicationMaxQueueSizeBytes
body := influxdb . UpdateReplicationRequest { Description : & newDescription , MaxQueueSizeBytes : & newQueueSize }
req := newTestRequest ( t , "PATCH" , ts . URL + "/" + id . String ( ) , body )
svc . EXPECT ( ) . UpdateReplication ( gomock . Any ( ) , * id , body ) . Return ( & testReplication , nil )
res := doTestRequest ( t , req , http . StatusOK , true )
var got influxdb . Replication
require . NoError ( t , json . NewDecoder ( res . Body ) . Decode ( & got ) )
require . Equal ( t , testReplication , got )
} )
t . Run ( "dry-run update happy path" , func ( t * testing . T ) {
ts , svc := newTestServer ( t )
defer ts . Close ( )
newDescription := "my cool replication"
newQueueSize := 3 * influxdb . DefaultReplicationMaxQueueSizeBytes
body := influxdb . UpdateReplicationRequest { Description : & newDescription , MaxQueueSizeBytes : & newQueueSize }
req := newTestRequest ( t , "PATCH" , ts . URL + "/" + id . String ( ) , body )
q := req . URL . Query ( )
q . Add ( "validate" , "true" )
req . URL . RawQuery = q . Encode ( )
svc . EXPECT ( ) . ValidateUpdatedReplication ( gomock . Any ( ) , * id , body ) . Return ( nil )
doTestRequest ( t , req , http . StatusNoContent , false )
} )
t . Run ( "validate replication happy path" , func ( t * testing . T ) {
ts , svc := newTestServer ( t )
defer ts . Close ( )
req := newTestRequest ( t , "POST" , ts . URL + "/" + id . String ( ) + "/validate" , nil )
svc . EXPECT ( ) . ValidateReplication ( gomock . Any ( ) , * id ) . Return ( nil )
doTestRequest ( t , req , http . StatusNoContent , false )
} )
t . Run ( "invalid replication 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 . UpdateReplicationRequest { } )
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 /replications 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 )
} )
}
} )
t . Run ( "too-small queue size on create is rejected" , func ( t * testing . T ) {
ts , _ := newTestServer ( t )
defer ts . Close ( )
body := influxdb . CreateReplicationRequest {
OrgID : testReplication . OrgID ,
Name : testReplication . Name ,
RemoteID : testReplication . RemoteID ,
LocalBucketID : testReplication . LocalBucketID ,
2022-08-18 18:21:59 +00:00
RemoteBucketID : * testReplication . RemoteBucketID ,
2021-08-24 18:19:03 +00:00
MaxQueueSizeBytes : influxdb . MinReplicationMaxQueueSizeBytes / 2 ,
}
req := newTestRequest ( t , "POST" , ts . URL , & body )
doTestRequest ( t , req , http . StatusBadRequest , true )
} )
t . Run ( "too-small queue size on update is rejected" , func ( t * testing . T ) {
ts , _ := newTestServer ( t )
defer ts . Close ( )
newSize := influxdb . MinReplicationMaxQueueSizeBytes / 2
body := influxdb . UpdateReplicationRequest { MaxQueueSizeBytes : & newSize }
req := newTestRequest ( t , "PATCH" , ts . URL + "/" + id . String ( ) , & body )
doTestRequest ( t , req , http . StatusBadRequest , true )
} )
}
func newTestServer ( t * testing . T ) ( * httptest . Server , * mock . MockReplicationService ) {
ctrl := gomock . NewController ( t )
svc := mock . NewMockReplicationService ( ctrl )
2021-12-13 22:01:50 +00:00
server := newReplicationHandler ( zaptest . NewLogger ( t ) , svc )
2021-08-24 18:19:03 +00:00
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
}