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 }