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  }