package api import ( "bytes" "encoding/json" "fmt" "io" "net/http" "net/http/httptest" "os" "sync/atomic" "github.com/aptly-dev/aptly/utils" "github.com/gin-gonic/gin" . "gopkg.in/check.v1" ) type MiddlewareSuite struct { router http.Handler context *gin.Context logReader *os.File logWriter *os.File } var _ = Suite(&MiddlewareSuite{}) func (s *MiddlewareSuite) SetUpTest(c *C) { r, w, err := os.Pipe() c.Assert(err, IsNil) utils.SetupJSONLogger("debug", w) mw := JSONLogger() router := gin.New() router.UseRawPath = true router.Use(mw) router.Use(gin.Recovery(), gin.ErrorLogger()) root := router.Group("/api") isReady := &atomic.Value{} isReady.Store(false) root.GET("/ready", apiReady(isReady)) root.GET("/healthy", apiHealthy) s.router = router s.logReader = r s.logWriter = w } func (s *MiddlewareSuite) TearDownTest(c *C) { s.router = nil s.context = nil s.logReader = nil s.logWriter = nil } func (s *MiddlewareSuite) HTTPRequest(method string, url string, body io.Reader) { recorder := httptest.NewRecorder() s.context, _ = gin.CreateTestContext(recorder) req, _ := http.NewRequestWithContext(s.context, method, url, body) s.context.Request = req req.Header.Add("Content-Type", "application/json") s.router.ServeHTTP(httptest.NewRecorder(), req) } func (s *MiddlewareSuite) TestJSONMiddleware4xx(c *C) { outC := make(chan string) go func() { var buf bytes.Buffer io.Copy(&buf, s.logReader) fmt.Println(buf.String()) outC <- buf.String() }() s.HTTPRequest(http.MethodGet, "/", nil) s.logWriter.Close() capturedOutput := <-outC var jsonMap map[string]interface{} json.Unmarshal([]byte(capturedOutput), &jsonMap) if val, ok := jsonMap["level"]; ok { c.Check(val, Equals, "warn") } else { c.Errorf("Log message didn't have a 'level' key, obtained %s", capturedOutput) } if val, ok := jsonMap["method"]; ok { c.Check(val, Equals, "GET") } else { c.Errorf("Log message didn't have a 'method' key, obtained %s", capturedOutput) } if val, ok := jsonMap["path"]; ok { c.Check(val, Equals, "/") } else { c.Errorf("Log message didn't have a 'path' key, obtained %s", capturedOutput) } if val, ok := jsonMap["protocol"]; ok { c.Check(val, Equals, "HTTP/1.1") } else { c.Errorf("Log message didn't have a 'protocol' key, obtained %s", capturedOutput) } if val, ok := jsonMap["code"]; ok { c.Check(val, Equals, "404") } else { c.Errorf("Log message didn't have a 'code' key, obtained %s", capturedOutput) } if _, ok := jsonMap["remote"]; !ok { c.Errorf("Log message didn't have a 'remote' key, obtained %s", capturedOutput) } if _, ok := jsonMap["latency"]; !ok { c.Errorf("Log message didn't have a 'latency' key, obtained %s", capturedOutput) } if _, ok := jsonMap["agent"]; !ok { c.Errorf("Log message didn't have a 'agent' key, obtained %s", capturedOutput) } if _, ok := jsonMap["time"]; !ok { c.Errorf("Log message didn't have a 'time' key, obtained %s", capturedOutput) } } func (s *MiddlewareSuite) TestJSONMiddleware2xx(c *C) { outC := make(chan string) go func() { var buf bytes.Buffer io.Copy(&buf, s.logReader) fmt.Println(buf.String()) outC <- buf.String() }() s.HTTPRequest(http.MethodGet, "/api/healthy", nil) s.logWriter.Close() capturedOutput := <-outC var jsonMap map[string]interface{} json.Unmarshal([]byte(capturedOutput), &jsonMap) if val, ok := jsonMap["level"]; ok { c.Check(val, Equals, "info") } else { c.Errorf("Log message didn't have a 'level' key, obtained %s", capturedOutput) } } func (s *MiddlewareSuite) TestJSONMiddleware5xx(c *C) { outC := make(chan string) go func() { var buf bytes.Buffer io.Copy(&buf, s.logReader) fmt.Println(buf.String()) outC <- buf.String() }() s.HTTPRequest(http.MethodGet, "/api/ready", nil) s.logWriter.Close() capturedOutput := <-outC var jsonMap map[string]interface{} json.Unmarshal([]byte(capturedOutput), &jsonMap) if val, ok := jsonMap["level"]; ok { c.Check(val, Equals, "error") } else { c.Errorf("Log message didn't have a 'level' key, obtained %s", capturedOutput) } } func (s *MiddlewareSuite) TestJSONMiddlewareRaw(c *C) { outC := make(chan string) go func() { var buf bytes.Buffer io.Copy(&buf, s.logReader) fmt.Println(buf.String()) outC <- buf.String() }() s.HTTPRequest(http.MethodGet, "/api/healthy?test=raw", nil) s.logWriter.Close() capturedOutput := <-outC var jsonMap map[string]interface{} json.Unmarshal([]byte(capturedOutput), &jsonMap) fmt.Println(capturedOutput) if val, ok := jsonMap["level"]; ok { c.Check(val, Equals, "info") } else { c.Errorf("Log message didn't have a 'level' key, obtained %s", capturedOutput) } } func (s *MiddlewareSuite) TestGetBasePath(c *C) { s.HTTPRequest(http.MethodGet, "", nil) path := getBasePath(s.context) c.Check(path, Equals, "/") s.HTTPRequest(http.MethodGet, "/", nil) path = getBasePath(s.context) c.Check(path, Equals, "/") s.HTTPRequest(http.MethodGet, "/api", nil) path = getBasePath(s.context) c.Check(path, Equals, "/api") s.HTTPRequest(http.MethodGet, "/api/repos/testRepo", nil) path = getBasePath(s.context) c.Check(path, Equals, "/api/repos") } func (s *MiddlewareSuite) TestGetURLSegment(c *C) { url := "/" segment, err := getURLSegment(url, 0) if err != nil { c.Error(err) } c.Check(*segment, Equals, "/") _, err = getURLSegment(url, 1) if err == nil { c.Error("Invalid return value") } url = "/api" segment, err = getURLSegment(url, 0) if err != nil { c.Error(err) } c.Check(*segment, Equals, "/api") _, err = getURLSegment(url, 1) if err == nil { c.Error("Invalid return value") } url = "/api/repos/testRepo" segment, err = getURLSegment(url, 0) if err != nil { c.Error(err) } c.Check(*segment, Equals, "/api") segment, err = getURLSegment(url, 1) if err != nil { c.Error(err) } c.Check(*segment, Equals, "/repos") }