github.com/mendersoftware/go-lib-micro@v0.0.0-20240304135804-e8e39c59b148/accesslog/middleware_gin_test.go (about) 1 // Copyright 2023 Northern.tech AS 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package accesslog 16 17 import ( 18 "bytes" 19 "net/http" 20 "net/http/httptest" 21 "testing" 22 23 "github.com/gin-gonic/gin" 24 "github.com/mendersoftware/go-lib-micro/log" 25 "github.com/pkg/errors" 26 "github.com/sirupsen/logrus" 27 "github.com/stretchr/testify/assert" 28 ) 29 30 func TestMiddleware(t *testing.T) { 31 testCases := []struct { 32 Name string 33 34 HandlerFunc gin.HandlerFunc 35 36 Fields []string 37 ExpectedBody string 38 }{{ 39 Name: "ok", 40 41 HandlerFunc: func(c *gin.Context) { 42 c.Status(http.StatusNoContent) 43 }, 44 Fields: []string{ 45 "status=204", 46 `path=/test`, 47 `qs="foo=bar"`, 48 "method=GET", 49 "useragent=tester", 50 "responsetime=", 51 "ts=", 52 }, 53 }, { 54 Name: "ok, pushed error", 55 56 HandlerFunc: func(c *gin.Context) { 57 err := errors.New("internal error") 58 _ = c.Error(err) 59 c.Status(http.StatusInternalServerError) 60 _, _ = c.Writer.Write([]byte(err.Error())) 61 }, 62 Fields: []string{ 63 "status=500", 64 `path=/test`, 65 `qs="foo=bar"`, 66 "method=GET", 67 "responsetime=", 68 "byteswritten=14", 69 "ts=", 70 `error="internal error"`, 71 }, 72 }, { 73 Name: "ok, pushed multiple errors", 74 75 HandlerFunc: func(c *gin.Context) { 76 err := errors.New("internal error 1") 77 _ = c.Error(err) 78 err = errors.New("internal error 2") 79 _ = c.Error(err) 80 c.Status(http.StatusInternalServerError) 81 c.Writer.Write([]byte(err.Error())) 82 }, 83 Fields: []string{ 84 "status=500", 85 `path=/test`, 86 `qs="foo=bar"`, 87 "useragent=tester", 88 "method=GET", 89 "responsetime=", 90 "byteswritten=16", 91 "ts=", 92 `error="#01: internal error 1\\n#02: internal error 2\\n"`, 93 }, 94 }, { 95 Name: "error, panic in handler", 96 97 HandlerFunc: func(c *gin.Context) { 98 panic("!!!!!") 99 }, 100 101 Fields: []string{ 102 "status=500", 103 `path=/test`, 104 `qs="foo=bar"`, 105 "method=GET", 106 "responsetime=", 107 "useragent=tester", 108 "ts=", 109 // First three entries in the trace should match this: 110 `trace=".+TestMiddleware\.func[0-9]*@middleware_gin_test\.go:[0-9]+\\n`, 111 }, 112 ExpectedBody: `{"error": "internal error"}`, 113 }} 114 115 gin.SetMode(gin.ReleaseMode) 116 for i := range testCases { 117 tc := testCases[i] 118 t.Run(tc.Name, func(t *testing.T) { 119 var logBuf = bytes.NewBuffer(nil) 120 router := gin.New() 121 router.Use(func(c *gin.Context) { 122 logger := log.NewEmpty() 123 logger.Logger.SetLevel(logrus.InfoLevel) 124 logger.Logger.SetOutput(logBuf) 125 logger.Logger.SetFormatter(&logrus.TextFormatter{ 126 DisableColors: true, 127 FullTimestamp: true, 128 }) 129 ctx := c.Request.Context() 130 ctx = log.WithContext(ctx, logger) 131 c.Request = c.Request.WithContext(ctx) 132 }) 133 router.Use(Middleware()) 134 router.GET("/test", tc.HandlerFunc) 135 136 w := httptest.NewRecorder() 137 req, _ := http.NewRequest( 138 http.MethodGet, 139 "http://localhost/test?foo=bar", 140 nil, 141 ) 142 req.Header.Set("User-Agent", "tester") 143 144 router.ServeHTTP(w, req) 145 146 logEntry := logBuf.String() 147 for _, field := range tc.Fields { 148 assert.Regexp(t, field, logEntry) 149 } 150 if tc.Fields == nil { 151 assert.Empty(t, logEntry) 152 } 153 if tc.ExpectedBody != "" { 154 if assert.NotNil(t, w.Body) { 155 assert.JSONEq(t, tc.ExpectedBody, w.Body.String()) 156 } 157 } 158 }) 159 } 160 }