github.com/GuanceCloud/cliutils@v1.1.21/network/http/err_test.go (about) 1 // Unless explicitly stated otherwise all files in this repository are licensed 2 // under the MIT License. 3 // This product includes software developed at Guance Cloud (https://www.guance.com/). 4 // Copyright 2021-present Guance, Inc. 5 6 package http 7 8 import ( 9 "encoding/json" 10 "errors" 11 "fmt" 12 "io" 13 "net/http" 14 "net/http/httptest" 15 "testing" 16 "time" 17 18 tu "github.com/GuanceCloud/cliutils/testutil" 19 "github.com/gin-gonic/gin" 20 "github.com/stretchr/testify/assert" 21 ) 22 23 func TestBytesBody(t *testing.T) { 24 errOK := NewNamespaceErr(nil, http.StatusOK, "") 25 bytesBody := "this is bytes response body" 26 27 router := gin.New() 28 g := router.Group("") 29 g.GET("/bytes-body", func(c *gin.Context) { 30 c.Writer.Header().Set("Content-Type", "application/octet-stream") 31 c.Writer.Header().Set("X-Latest-Time", time.Now().String()) 32 errOK.HttpBody(c, []byte(bytesBody)) 33 }) 34 35 ts := httptest.NewServer(router) 36 37 defer ts.Close() 38 39 time.Sleep(time.Second) 40 41 resp, err := http.Get(fmt.Sprintf("%s%s", ts.URL, "/bytes-body")) 42 if err != nil { 43 t.Error(err) 44 } 45 46 defer resp.Body.Close() 47 48 b, err := io.ReadAll(resp.Body) 49 if err != nil { 50 t.Error(err) 51 } 52 53 for k, v := range resp.Header { 54 t.Logf("%s: %v", k, v) 55 } 56 57 tu.Equals(t, bytesBody, string(b)) 58 } 59 60 func TestNoSniff(t *testing.T) { 61 errOK := NewNamespaceErr(nil, http.StatusOK, "") 62 bytesBody := "this is bytes response body" 63 64 router := gin.New() 65 g := router.Group("") 66 g.GET("/bytes-body", func(c *gin.Context) { 67 c.Writer.Header().Set("Content-Type", "application/octet-stream") 68 c.Writer.Header().Set("X-Latest-Time", time.Now().String()) 69 errOK.HttpBody(c, []byte(bytesBody)) 70 }) 71 72 g.GET("/obj-body", func(c *gin.Context) { 73 errOK.HttpBody(c, map[string]string{}) 74 }) 75 76 g.GET("/err-body", func(c *gin.Context) { 77 HttpErr(c, fmt.Errorf("mocked error")) 78 }) 79 80 ts := httptest.NewServer(router) 81 defer ts.Close() 82 time.Sleep(time.Second) 83 84 // -------------------- 85 86 for _, x := range []string{ 87 "/bytes-body", "/obj-body", "/err-body", 88 } { 89 t.Run(x, func(t *testing.T) { 90 resp, err := http.Get(fmt.Sprintf("%s%s", ts.URL, x)) 91 if err != nil { 92 t.Error(err) 93 } 94 assert.Equal(t, "nosniff", resp.Header.Get("X-Content-Type-Options")) 95 for k := range resp.Header { 96 t.Logf("%s: %s", k, resp.Header.Get(k)) 97 } 98 body, _ := io.ReadAll(resp.Body) 99 t.Logf("body: %s", body) 100 resp.Body.Close() 101 }) 102 } 103 } 104 105 func TestHTTPErr(t *testing.T) { 106 errTest := NewNamespaceErr(errors.New("test error"), http.StatusForbidden, "testing") 107 errOK := NewNamespaceErr(nil, http.StatusOK, "") 108 109 DefaultNamespace = "testing2" 110 errTest2 := NewErr(errors.New("test error2"), http.StatusForbidden) 111 112 router := gin.New() 113 g := router.Group("") 114 115 okbody := map[string]interface{}{ 116 "data1": 1, 117 "data2": "abc", 118 } 119 120 g.GET("/err", func(c *gin.Context) { HttpErr(c, errTest) }) 121 g.GET("/err2", func(c *gin.Context) { HttpErr(c, errTest2) }) 122 g.GET("/err3", func(c *gin.Context) { HttpErr(c, fmt.Errorf("500 error")) }) 123 g.GET("/errf", func(c *gin.Context) { HttpErrf(c, errTest, "%s: %s", "this is a test error", "ignore me") }) 124 g.GET("/ok", func(c *gin.Context) { errOK.WriteBody(c, okbody) }) 125 g.GET("/ok2", func(c *gin.Context) { errOK.HttpBody(c, okbody) }) 126 g.GET("/oknilbody", func(c *gin.Context) { errOK.HttpBody(c, nil) }) 127 g.GET("/errmsg", func(c *gin.Context) { err := Error(errTest, "this is a error with specific message"); HttpErr(c, err) }) 128 g.GET("/errfmsg", func(c *gin.Context) { 129 err := Errorf(errTest, "%s: %v", "this is a message with fmt", map[string]int{"abc": 123}) 130 HttpErr(c, err) 131 }) 132 g.GET("/errfmsg-with-nil-args", func(c *gin.Context) { 133 err := Errorf(ErrTooManyRequest, "Errorf without args") 134 HttpErr(c, err) 135 }) 136 137 srv := http.Server{ 138 Addr: ":8090", 139 Handler: router, 140 } 141 142 go func() { 143 if e := srv.ListenAndServe(); e != nil && errors.Is(e, http.ErrServerClosed) { 144 t.Log(e) 145 } 146 }() 147 148 time.Sleep(time.Second) 149 defer srv.Close() 150 151 cases := []struct { 152 u string 153 expect string 154 }{ 155 { 156 u: "http://localhost:8090/errmsg", 157 expect: `{"error_code":"testing.testError","message":"this is a error with specific message"}`, 158 }, 159 160 { 161 u: "http://localhost:8090/errfmsg", 162 expect: `{"error_code":"testing.testError","message":"this is a message with fmt: map[abc:123]"}`, 163 }, 164 165 { 166 u: "http://localhost:8090/err", 167 expect: func() string { 168 j, err := json.Marshal(errTest) 169 if err != nil { 170 t.Fatal(err) 171 } 172 return string(j) 173 }(), 174 }, 175 { 176 u: "http://localhost:8090/err2", 177 expect: func() string { 178 j, err := json.Marshal(errTest2) 179 if err != nil { 180 t.Fatal(err) 181 } 182 return string(j) 183 }(), 184 }, 185 { 186 u: "http://localhost:8090/err3", 187 expect: `{"error_code":"testing2.500Error"}`, 188 }, 189 { 190 u: "http://localhost:8090/errf", 191 expect: `{"error_code":"testing.testError","message":"this is a test error: ignore me"}`, 192 }, 193 { 194 u: "http://localhost:8090/ok", 195 expect: func() string { 196 j, err := json.Marshal(okbody) 197 if err != nil { 198 t.Fatal(err) 199 } 200 return string(j) 201 }(), 202 }, 203 204 { 205 u: "http://localhost:8090/ok2", 206 expect: func() string { 207 x := struct { 208 Content interface{} `json:"content"` 209 }{ 210 Content: okbody, 211 } 212 213 j, err := json.Marshal(x) 214 if err != nil { 215 t.Fatal(err) 216 } 217 return string(j) 218 }(), 219 }, 220 221 { 222 u: "http://localhost:8090/oknilbody", 223 expect: "", 224 }, 225 { 226 u: "http://localhost:8090/errfmsg-with-nil-args", 227 expect: func() string { 228 msg := `{"error_code":"reachMaxAPIRateLimit","message":"Errorf without args"}` 229 var x interface{} 230 if err := json.Unmarshal([]byte(msg), &x); err != nil { 231 t.Fatal(err) 232 } 233 j, err := json.Marshal(x) 234 if err != nil { 235 t.Fatal(err) 236 } 237 return string(j) 238 }(), 239 }, 240 } 241 242 for _, tc := range cases { 243 t.Run(tc.u, func(t *testing.T) { 244 resp, err := http.Get(tc.u) 245 if err != nil { 246 t.Logf("get error: %s, ignored", err) 247 return 248 } 249 250 for k, v := range resp.Header { 251 t.Logf("%s: %v", k, v) 252 } 253 254 if resp.Body != nil { 255 body, err := io.ReadAll(resp.Body) 256 if err != nil { 257 t.Error(err) 258 return 259 } 260 261 tu.Equals(t, tc.expect, string(body)) 262 resp.Body.Close() 263 } else { 264 t.Error("body should not be nil") 265 } 266 }) 267 } 268 } 269 270 func TestErrorf(t *testing.T) { 271 var nilArgs []interface{} = nil 272 format := "sprintf with nil args" 273 str := fmt.Sprintf(format, nilArgs...) 274 fmt.Println(str) 275 276 errWithEmptyFmt := Errorf(NewErr(errors.New("bad gateway"), http.StatusBadGateway), "") 277 assert.Nil(t, errWithEmptyFmt.Args) 278 279 errWithoutArgs := Errorf(ErrUnexpectedInternalServerError, "Errorf without args") 280 assert.Nil(t, errWithoutArgs.Args) 281 282 errWithArgs := Errorf(ErrTooManyRequest, "Errorf with args: %s", "###ERROR###") 283 assert.NotNil(t, errWithArgs.Args) 284 285 g := gin.New() 286 287 type testCase struct { 288 name string 289 url string 290 err *MsgError 291 expectedMsg string 292 } 293 294 cases := []testCase{ 295 { 296 name: "Errorf With Empty fmt", 297 url: "/errorf-with-empty-fmt", 298 err: errWithEmptyFmt, 299 expectedMsg: "", 300 }, 301 { 302 name: "Errorf without args", 303 url: "/errorf-without-args", 304 err: errWithoutArgs, 305 expectedMsg: "Errorf without args", 306 }, 307 { 308 name: "Errorf with args", 309 url: "/errorf-with-args", 310 err: errWithArgs, 311 expectedMsg: "Errorf with args: ###ERROR###", 312 }, 313 } 314 315 for i := range cases { 316 tc := cases[i] 317 g.GET(tc.url, func(c *gin.Context) { 318 HttpErr(c, tc.err) 319 }) 320 } 321 322 type response struct { 323 ErrCode string `json:"error_code"` 324 Message string `json:"message"` 325 } 326 327 for _, tc := range cases { 328 t.Run(tc.name, func(t *testing.T) { 329 resp := httptest.NewRecorder() 330 req := httptest.NewRequest(http.MethodGet, tc.url, nil) 331 g.ServeHTTP(resp, req) 332 333 assert.Equal(t, resp.Result().StatusCode, tc.err.HttpCode) 334 335 var rp response 336 decoder := json.NewDecoder(resp.Body) 337 err := decoder.Decode(&rp) 338 assert.NoError(t, err) 339 340 assert.NotEmpty(t, rp.ErrCode) 341 assert.Equal(t, tc.expectedMsg, rp.Message) 342 }) 343 } 344 }