github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/api/internal/rest/request_test.go (about) 1 // Copyright 2021 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package rest 15 16 import ( 17 "bytes" 18 "context" 19 "errors" 20 "io" 21 "net/http" 22 "net/http/httptest" 23 "net/url" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/pingcap/tiflow/pkg/httputil" 29 "github.com/stretchr/testify/require" 30 ) 31 32 func TestRequestParams(t *testing.T) { 33 req := (&Request{}).WithParam("foo", "bar") 34 require.Equal(t, req.params, url.Values{"foo": []string{"bar"}}) 35 36 req.WithParam("hello", "world") 37 require.Equal(t, req.params, url.Values{"foo": []string{"bar"}, "hello": []string{"world"}}) 38 } 39 40 func TestRequestURI(t *testing.T) { 41 req := (&Request{}).WithParam("foo", "bar").WithPrefix("test") 42 req.WithURI("/production?foo=hello&val=1024") 43 require.Equal(t, req.pathPrefix, "test/production") 44 require.Equal(t, req.params, url.Values{"foo": []string{"hello"}, "val": []string{"1024"}}) 45 } 46 47 type testStruct struct { 48 Foo string `json:"foo"` 49 Bar int `json:"bar"` 50 } 51 52 func TestRequestBody(t *testing.T) { 53 // test unsupported data type 54 req := (&Request{}).WithBody(func() {}) 55 require.NotNil(t, req.err) 56 require.Nil(t, req.body) 57 58 // test data type which can be json marshalled 59 p := &testStruct{Foo: "hello", Bar: 10} 60 req = (&Request{}).WithBody(p) 61 require.Nil(t, req.err) 62 require.NotNil(t, req.body) 63 64 // test data type io.Reader 65 req = (&Request{}).WithBody(bytes.NewReader([]byte(`{"hello": "world"}`))) 66 require.Nil(t, req.err) 67 require.NotNil(t, req.body) 68 } 69 70 type clientFunc func(req *http.Request) (*http.Response, error) 71 72 func (f clientFunc) RoundTrip(req *http.Request) (*http.Response, error) { 73 return f(req) 74 } 75 76 func TestRequestHeader(t *testing.T) { 77 cli := httputil.NewTestClient(clientFunc(func(req *http.Request) (*http.Response, error) { 78 require.Equal(t, req.Header.Get("signature"), "test-header1") 79 80 return &http.Response{ 81 StatusCode: http.StatusOK, 82 Body: io.NopCloser(bytes.NewReader([]byte{})), 83 }, nil 84 })) 85 req := newRequestWithClient(&url.URL{Path: "/test"}, "", nil).WithMethod(HTTPMethodGet) 86 req.WithHeader("signature", "test-header2") 87 req.WithHeader("signature", "test-header1") 88 req.c.Client = cli 89 90 _ = req.Do(context.Background()) 91 } 92 93 func TestRequestDoContext(t *testing.T) { 94 received := make(chan struct{}) 95 blocked := make(chan struct{}) 96 testServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { 97 close(received) 98 <-blocked 99 rw.WriteHeader(http.StatusOK) 100 })) 101 defer testServer.Close() 102 defer close(blocked) 103 104 ctx, cancel := context.WithCancel(context.Background()) 105 106 go func() { 107 <-received 108 cancel() 109 }() 110 c, err := CDCRESTClientFromConfig(&Config{ 111 Host: testServer.URL, 112 APIPath: "/api", 113 Version: "v1", 114 }) 115 require.Nil(t, err) 116 err = c.Get(). 117 WithPrefix("/test"). 118 WithTimeout(time.Second). 119 Do(ctx). 120 Error() 121 require.NotNil(t, err) 122 } 123 124 func TestRequestDoContextTimeout(t *testing.T) { 125 testServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { 126 time.Sleep(2 * time.Second) 127 rw.WriteHeader(http.StatusOK) 128 })) 129 defer testServer.Close() 130 131 ctx, cancel := context.WithCancel(context.Background()) 132 defer cancel() 133 134 c, err := CDCRESTClientFromConfig(&Config{ 135 Host: testServer.URL, 136 APIPath: "/api", 137 Version: "v1", 138 }) 139 require.Nil(t, err) 140 err = c.Get(). 141 WithPrefix("/test"). 142 WithTimeout(time.Second). 143 Do(ctx). 144 Error() 145 require.NotNil(t, err) 146 } 147 148 func TestResultIntoError(t *testing.T) { 149 result := Result{err: errors.New("test-error")} 150 err := result.Into(&testStruct{}) 151 require.Equal(t, result.err, err) 152 153 result = Result{ 154 body: []byte(`{"foo": "hello", "bar": 10}`), 155 } 156 157 var res testStruct 158 err = result.Into(&res) 159 require.Nil(t, err) 160 require.Equal(t, res.Foo, "hello") 161 require.Equal(t, res.Bar, 10) 162 } 163 164 func TestResultZeroLengthBody(t *testing.T) { 165 result := Result{ 166 body: []byte{}, 167 } 168 err := result.Into(&testStruct{}) 169 require.NotNil(t, err) 170 require.Equal(t, strings.Contains(err.Error(), "0-length"), true) 171 }