github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/utils/http_test.go (about)

     1  /*
     2   * Copyright (c) 2020-present unTill Pro, Ltd.
     3   * @author Denis Gribanov
     4   */
     5  
     6  package coreutils
     7  
     8  import (
     9  	"context"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"net"
    14  	"net/http"
    15  	"net/url"
    16  	"reflect"
    17  	"runtime"
    18  	"strings"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/stretchr/testify/require"
    23  
    24  	"github.com/voedger/voedger/pkg/appdef"
    25  	ibus "github.com/voedger/voedger/staging/src/github.com/untillpro/airs-ibus"
    26  	"github.com/voedger/voedger/staging/src/github.com/untillpro/ibusmem"
    27  )
    28  
    29  func TestNewHTTPError(t *testing.T) {
    30  	require := require.New(t)
    31  	t.Run("simple", func(t *testing.T) {
    32  		sysErr := NewHTTPError(http.StatusInternalServerError, errors.New("test error"))
    33  		require.Empty(sysErr.Data)
    34  		require.Equal(http.StatusInternalServerError, sysErr.HTTPStatus)
    35  		require.Equal("test error", sysErr.Message)
    36  		require.Equal(appdef.NullQName, sysErr.QName)
    37  		require.Equal(`{"sys.Error":{"HTTPStatus":500,"Message":"test error"}}`, sysErr.ToJSON())
    38  	})
    39  
    40  	t.Run("formatted", func(t *testing.T) {
    41  		sysErr := NewHTTPErrorf(http.StatusInternalServerError, "test ", "error")
    42  		require.Empty(sysErr.Data)
    43  		require.Equal(http.StatusInternalServerError, sysErr.HTTPStatus)
    44  		require.Equal("test error", sysErr.Message)
    45  		require.Equal(appdef.NullQName, sysErr.QName)
    46  		require.Equal(`{"sys.Error":{"HTTPStatus":500,"Message":"test error"}}`, sysErr.ToJSON())
    47  	})
    48  }
    49  
    50  type testResp struct {
    51  	sender interface{}
    52  	resp   ibus.Response
    53  }
    54  
    55  type testIBus struct {
    56  	responses []testResp
    57  }
    58  
    59  func (bus *testIBus) SendRequest2(ctx context.Context, request ibus.Request, timeout time.Duration) (res ibus.Response, sections <-chan ibus.ISection, secError *error, err error) {
    60  	panic("")
    61  }
    62  
    63  func (bus *testIBus) SendResponse(sender interface{}, response ibus.Response) {
    64  	bus.responses = append(bus.responses, testResp{
    65  		sender: sender,
    66  		resp:   response,
    67  	})
    68  }
    69  
    70  func (bus *testIBus) SendParallelResponse2(sender interface{}) (rsender ibus.IResultSenderClosable) {
    71  	panic("")
    72  }
    73  
    74  func TestReply(t *testing.T) {
    75  	require := require.New(t)
    76  	busSender := "whatever"
    77  
    78  	t.Run("ReplyErr", func(t *testing.T) {
    79  		bus := &testIBus{}
    80  		err := errors.New("test error")
    81  		sender := ibusmem.NewISender(bus, busSender)
    82  		ReplyErr(sender, err)
    83  		expectedResp := ibus.Response{
    84  			ContentType: ApplicationJSON,
    85  			StatusCode:  http.StatusInternalServerError,
    86  			Data:        []byte(`{"sys.Error":{"HTTPStatus":500,"Message":"test error"}}`),
    87  		}
    88  		require.Equal(testResp{sender: "whatever", resp: expectedResp}, bus.responses[0])
    89  	})
    90  
    91  	t.Run("ReplyErrf", func(t *testing.T) {
    92  		bus := &testIBus{}
    93  		sender := ibusmem.NewISender(bus, busSender)
    94  		ReplyErrf(sender, http.StatusAccepted, "test ", "message")
    95  		expectedResp := ibus.Response{
    96  			ContentType: ApplicationJSON,
    97  			StatusCode:  http.StatusAccepted,
    98  			Data:        []byte(`{"sys.Error":{"HTTPStatus":202,"Message":"test message"}}`),
    99  		}
   100  		require.Equal(testResp{sender: "whatever", resp: expectedResp}, bus.responses[0])
   101  	})
   102  
   103  	t.Run("ReplyErrorDef", func(t *testing.T) {
   104  		t.Run("common error", func(t *testing.T) {
   105  			bus := &testIBus{}
   106  			err := errors.New("test error")
   107  			sender := ibusmem.NewISender(bus, busSender)
   108  			ReplyErrDef(sender, err, http.StatusAccepted)
   109  			expectedResp := ibus.Response{
   110  				ContentType: ApplicationJSON,
   111  				StatusCode:  http.StatusAccepted,
   112  				Data:        []byte(`{"sys.Error":{"HTTPStatus":202,"Message":"test error"}}`),
   113  			}
   114  			require.Equal(testResp{sender: "whatever", resp: expectedResp}, bus.responses[0])
   115  		})
   116  		t.Run("SysError", func(t *testing.T) {
   117  			bus := &testIBus{}
   118  			err := SysError{
   119  				HTTPStatus: http.StatusAlreadyReported,
   120  				Message:    "test error",
   121  				Data:       "dddfd",
   122  				QName:      appdef.NewQName("my", "qname"),
   123  			}
   124  			sender := ibusmem.NewISender(bus, busSender)
   125  			ReplyErrDef(sender, err, http.StatusAccepted)
   126  			expectedResp := ibus.Response{
   127  				ContentType: ApplicationJSON,
   128  				StatusCode:  http.StatusAlreadyReported,
   129  				Data:        []byte(`{"sys.Error":{"HTTPStatus":208,"Message":"test error","QName":"my.qname","Data":"dddfd"}}`),
   130  			}
   131  			require.Equal(testResp{sender: "whatever", resp: expectedResp}, bus.responses[0])
   132  		})
   133  	})
   134  
   135  	t.Run("http status helpers", func(t *testing.T) {
   136  		cases := []struct {
   137  			statusCode      int
   138  			f               func(sender ibus.ISender, message string)
   139  			expectedMessage string
   140  		}{
   141  			{f: ReplyUnauthorized, statusCode: http.StatusUnauthorized},
   142  			{f: ReplyBadRequest, statusCode: http.StatusBadRequest},
   143  			{f: ReplyAccessDeniedForbidden, statusCode: http.StatusForbidden, expectedMessage: "access denied: test message"},
   144  			{f: ReplyAccessDeniedUnauthorized, statusCode: http.StatusUnauthorized, expectedMessage: "access denied: test message"},
   145  		}
   146  
   147  		for _, c := range cases {
   148  			name := runtime.FuncForPC(reflect.ValueOf(c.f).Pointer()).Name()
   149  			name = name[strings.LastIndex(name, ".")+1:]
   150  			t.Run(name, func(t *testing.T) {
   151  				bus := &testIBus{}
   152  				busSender := "whatever"
   153  				sender := ibusmem.NewISender(bus, busSender)
   154  				c.f(sender, "test message")
   155  				expectedMessage := "test message"
   156  				if len(c.expectedMessage) > 0 {
   157  					expectedMessage = c.expectedMessage
   158  				}
   159  				expectedResp := ibus.Response{
   160  					ContentType: ApplicationJSON,
   161  					StatusCode:  c.statusCode,
   162  					Data:        []byte(fmt.Sprintf(`{"sys.Error":{"HTTPStatus":%d,"Message":"%s"}}`, c.statusCode, expectedMessage)),
   163  				}
   164  				require.Equal(testResp{sender: "whatever", resp: expectedResp}, bus.responses[0])
   165  			})
   166  		}
   167  
   168  		t.Run("ReplyInternalServerError", func(t *testing.T) {
   169  			bus := &testIBus{}
   170  			busSender := "whatever"
   171  			err := errors.New("test error")
   172  			sender := ibusmem.NewISender(bus, busSender)
   173  			ReplyInternalServerError(sender, "test", err)
   174  			expectedResp := ibus.Response{
   175  				ContentType: ApplicationJSON,
   176  				StatusCode:  http.StatusInternalServerError,
   177  				Data:        []byte(`{"sys.Error":{"HTTPStatus":500,"Message":"test: test error"}}`),
   178  			}
   179  			require.Equal(testResp{sender: "whatever", resp: expectedResp}, bus.responses[0])
   180  		})
   181  	})
   182  }
   183  
   184  func TestHTTP(t *testing.T) {
   185  	require := require.New(t)
   186  
   187  	listener, err := net.Listen("tcp", ServerAddress(0))
   188  	require.NoError(err)
   189  	var handler func(w http.ResponseWriter, r *http.Request)
   190  	server := &http.Server{
   191  		Addr: ":0",
   192  		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   193  			handler(w, r)
   194  		}),
   195  	}
   196  	done := make(chan interface{})
   197  	go func() {
   198  		defer close(done)
   199  		if err := server.Serve(listener); err != nil && err != http.ErrServerClosed {
   200  			require.NoError(err)
   201  		}
   202  	}()
   203  
   204  	port := listener.Addr().(*net.TCPAddr).Port
   205  	federationURL, err := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", port))
   206  	require.NoError(err)
   207  	httpClient, cleanup := NewIHTTPClient()
   208  	defer cleanup()
   209  
   210  	t.Run("basic", func(t *testing.T) {
   211  		handler = func(w http.ResponseWriter, r *http.Request) {
   212  			body, err := io.ReadAll(r.Body)
   213  			require.NoError(err)
   214  			w.Write([]byte(fmt.Sprintf("hello, %s", string(body))))
   215  		}
   216  		resp, err := httpClient.Req(federationURL.String()+"/test", "world")
   217  		require.NoError(err)
   218  		require.Equal("hello, world", resp.Body)
   219  		require.Equal(http.StatusOK, resp.HTTPResp.StatusCode)
   220  	})
   221  
   222  	t.Run("cookies & headers", func(t *testing.T) {
   223  		handler = func(_ http.ResponseWriter, r *http.Request) {
   224  			_, err := io.ReadAll(r.Body)
   225  			require.NoError(err)
   226  			// require.Len(r.Header, 2)
   227  			require.Equal("headerValue", r.Header["Header-Key"][0])
   228  			require.Equal("Bearer authorizationValue", r.Header["Authorization"][0])
   229  		}
   230  		resp, err := httpClient.Req(federationURL.String()+"/test", "world",
   231  			WithCookies("cookieKey", "cookieValue"),
   232  			WithHeaders("Header-Key", "headerValue"),
   233  			WithAuthorizeBy("authorizationValue"),
   234  		)
   235  		require.NoError(err)
   236  		fmt.Println(resp.Body)
   237  	})
   238  
   239  	require.NoError(server.Shutdown(context.Background()))
   240  
   241  	<-done
   242  }