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  }