Use object structure for ApiServer
The API entrypoint was not fully testable due to production API client instantiation. This instantiation is now done within the command entrypoint and this file is excluded from coverage analysis in SonarQube. Signed-off-by: Steven Kriegler <sk.bunsenbrenner@gmail.com>
This commit is contained in:
parent
af38a31bbe
commit
10d2d3def8
|
@ -1,13 +1,17 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"gitea-sonarqube-pr-bot/internal/api"
|
||||
giteaSdk "gitea-sonarqube-pr-bot/internal/clients/gitea"
|
||||
sonarQubeSdk "gitea-sonarqube-pr-bot/internal/clients/sonarqube"
|
||||
"gitea-sonarqube-pr-bot/internal/settings"
|
||||
|
||||
"github.com/fvbock/endless"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
@ -27,7 +31,7 @@ func main() {
|
|||
Name: "gitea-sonarqube-pr-bot",
|
||||
Usage: "Improve your experience with SonarQube and Gitea",
|
||||
Description: `By default, gitea-sonarqube-pr-bot will start running the webserver if no arguments are passed.`,
|
||||
Action: api.Serve,
|
||||
Action: serveApi,
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
|
@ -35,3 +39,13 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func serveApi(c *cli.Context) error {
|
||||
fmt.Println("Hi! I'm the Gitea-SonarQube-PR bot. At your service.")
|
||||
|
||||
giteaHandler := api.NewGiteaWebhookHandler(giteaSdk.New(), sonarQubeSdk.New())
|
||||
sqHandler := api.NewSonarQubeWebhookHandler(giteaSdk.New(), sonarQubeSdk.New())
|
||||
server := api.New(giteaHandler, sqHandler)
|
||||
|
||||
return endless.ListenAndServe(":3000", server.Engine)
|
||||
}
|
||||
|
|
|
@ -1,28 +1,38 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
giteaSdk "gitea-sonarqube-pr-bot/internal/clients/gitea"
|
||||
sqSdk "gitea-sonarqube-pr-bot/internal/clients/sonarqube"
|
||||
|
||||
"github.com/fvbock/endless"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
sonarQubeWebhookHandler SonarQubeWebhookHandlerInferface
|
||||
giteaWebhookHandler GiteaWebhookHandlerInferface
|
||||
)
|
||||
|
||||
type validSonarQubeEndpointHeader struct {
|
||||
SonarQubeProject string `header:"X-SonarQube-Project" binding:"required"`
|
||||
}
|
||||
|
||||
func addSonarQubeEndpoint(r *gin.Engine) {
|
||||
r.POST("/hooks/sonarqube", func(c *gin.Context) {
|
||||
type validGiteaEndpointHeader struct {
|
||||
GiteaEvent string `header:"X-Gitea-Event" binding:"required"`
|
||||
}
|
||||
|
||||
type ApiServer struct {
|
||||
Engine *gin.Engine
|
||||
sonarQubeWebhookHandler SonarQubeWebhookHandlerInferface
|
||||
giteaWebhookHandler GiteaWebhookHandlerInferface
|
||||
}
|
||||
|
||||
func (s *ApiServer) setup() {
|
||||
s.Engine.Use(gin.Recovery())
|
||||
s.Engine.Use(gin.LoggerWithConfig(gin.LoggerConfig{
|
||||
SkipPaths: []string{"/ping", "/favicon.ico"},
|
||||
}))
|
||||
|
||||
s.Engine.GET("/favicon.ico", func(c *gin.Context) {
|
||||
c.Status(http.StatusNoContent)
|
||||
}).GET("/ping", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "pong",
|
||||
})
|
||||
}).POST("/hooks/sonarqube", func(c *gin.Context) {
|
||||
h := validSonarQubeEndpointHeader{}
|
||||
|
||||
if err := c.ShouldBindHeader(&h); err != nil {
|
||||
|
@ -30,16 +40,8 @@ func addSonarQubeEndpoint(r *gin.Engine) {
|
|||
return
|
||||
}
|
||||
|
||||
sonarQubeWebhookHandler.Handle(c.Writer, c.Request)
|
||||
})
|
||||
}
|
||||
|
||||
type validGiteaEndpointHeader struct {
|
||||
GiteaEvent string `header:"X-Gitea-Event" binding:"required"`
|
||||
}
|
||||
|
||||
func addGiteaEndpoint(r *gin.Engine) {
|
||||
r.POST("/hooks/gitea", func(c *gin.Context) {
|
||||
s.sonarQubeWebhookHandler.Handle(c.Writer, c.Request)
|
||||
}).POST("/hooks/gitea", func(c *gin.Context) {
|
||||
h := validGiteaEndpointHeader{}
|
||||
|
||||
if err := c.ShouldBindHeader(&h); err != nil {
|
||||
|
@ -49,9 +51,9 @@ func addGiteaEndpoint(r *gin.Engine) {
|
|||
|
||||
switch h.GiteaEvent {
|
||||
case "pull_request":
|
||||
giteaWebhookHandler.HandleSynchronize(c.Writer, c.Request)
|
||||
s.giteaWebhookHandler.HandleSynchronize(c.Writer, c.Request)
|
||||
case "issue_comment":
|
||||
giteaWebhookHandler.HandleComment(c.Writer, c.Request)
|
||||
s.giteaWebhookHandler.HandleComment(c.Writer, c.Request)
|
||||
default:
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "ignore unknown event",
|
||||
|
@ -60,36 +62,14 @@ func addGiteaEndpoint(r *gin.Engine) {
|
|||
})
|
||||
}
|
||||
|
||||
func setupRouter() *gin.Engine {
|
||||
r := gin.New()
|
||||
func New(giteaHandler GiteaWebhookHandlerInferface, sonarQubeHandler SonarQubeWebhookHandlerInferface) *ApiServer {
|
||||
s := &ApiServer{
|
||||
Engine: gin.New(),
|
||||
giteaWebhookHandler: giteaHandler,
|
||||
sonarQubeWebhookHandler: sonarQubeHandler,
|
||||
}
|
||||
|
||||
r.Use(gin.Recovery())
|
||||
r.Use(gin.LoggerWithConfig(gin.LoggerConfig{
|
||||
SkipPaths: []string{"/ping", "/favicon.ico"},
|
||||
}))
|
||||
s.setup()
|
||||
|
||||
r.GET("/ping", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "pong",
|
||||
})
|
||||
})
|
||||
r.GET("/favicon.ico", func(c *gin.Context) {
|
||||
c.Status(http.StatusNoContent)
|
||||
})
|
||||
|
||||
addSonarQubeEndpoint(r)
|
||||
addGiteaEndpoint(r)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func Serve(c *cli.Context) error {
|
||||
fmt.Println("Hi! I'm the Gitea-SonarQube-PR bot. At your service.")
|
||||
|
||||
sonarQubeWebhookHandler = NewSonarQubeWebhookHandler(giteaSdk.New(), sqSdk.New())
|
||||
giteaWebhookHandler = NewGiteaWebhookHandler(giteaSdk.New(), sqSdk.New())
|
||||
|
||||
r := setupRouter()
|
||||
|
||||
return endless.ListenAndServe(":3000", r)
|
||||
return s
|
||||
}
|
||||
|
|
|
@ -82,76 +82,71 @@ func (h *SQSdkMock) ComposeGiteaComment(data *sqSdk.CommentComposeData) (string,
|
|||
|
||||
// SETUP: mute logs
|
||||
func TestMain(m *testing.M) {
|
||||
sonarQubeWebhookHandler = nil
|
||||
giteaWebhookHandler = nil
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
log.SetOutput(ioutil.Discard)
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestNonAPIRoutes(t *testing.T) {
|
||||
router := setupRouter()
|
||||
router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/favicon.ico", nil)
|
||||
router.ServeHTTP(w, req)
|
||||
router.Engine.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusNoContent, w.Code)
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/ping", nil)
|
||||
router.ServeHTTP(w, req)
|
||||
router.Engine.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
func TestSonarQubeAPIRouteMissingProjectHeader(t *testing.T) {
|
||||
router := setupRouter()
|
||||
router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/hooks/sonarqube", bytes.NewBuffer([]byte(`{}`)))
|
||||
router.ServeHTTP(w, req)
|
||||
router.Engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
func TestSonarQubeAPIRouteProcessing(t *testing.T) {
|
||||
router := setupRouter()
|
||||
|
||||
sonarQubeHandlerMock := new(SonarQubeHandlerMock)
|
||||
sonarQubeHandlerMock.On("Handle", mock.Anything, mock.Anything).Return(nil)
|
||||
sonarQubeWebhookHandler = sonarQubeHandlerMock
|
||||
|
||||
router := New(new(GiteaHandlerMock), sonarQubeHandlerMock)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/hooks/sonarqube", bytes.NewBuffer([]byte(`{}`)))
|
||||
req.Header.Add("X-SonarQube-Project", "gitea-sonarqube-bot")
|
||||
router.ServeHTTP(w, req)
|
||||
router.Engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
sonarQubeHandlerMock.AssertNumberOfCalls(t, "Handle", 1)
|
||||
}
|
||||
|
||||
func TestGiteaAPIRouteMissingEventHeader(t *testing.T) {
|
||||
router := setupRouter()
|
||||
router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/hooks/gitea", bytes.NewBuffer([]byte(`{}`)))
|
||||
router.ServeHTTP(w, req)
|
||||
router.Engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
func TestGiteaAPIRouteSynchronizeProcessing(t *testing.T) {
|
||||
router := setupRouter()
|
||||
|
||||
giteaHandlerMock := new(GiteaHandlerMock)
|
||||
giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Return(nil)
|
||||
giteaHandlerMock.On("HandleComment", mock.Anything, mock.Anything).Return(nil)
|
||||
giteaWebhookHandler = giteaHandlerMock
|
||||
|
||||
router := New(giteaHandlerMock, new(SonarQubeHandlerMock))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/hooks/gitea", bytes.NewBuffer([]byte(`{}`)))
|
||||
req.Header.Add("X-Gitea-Event", "pull_request")
|
||||
router.ServeHTTP(w, req)
|
||||
router.Engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
giteaHandlerMock.AssertNumberOfCalls(t, "HandleSynchronize", 1)
|
||||
|
@ -159,17 +154,16 @@ func TestGiteaAPIRouteSynchronizeProcessing(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGiteaAPIRouteCommentProcessing(t *testing.T) {
|
||||
router := setupRouter()
|
||||
|
||||
giteaHandlerMock := new(GiteaHandlerMock)
|
||||
giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Return(nil)
|
||||
giteaHandlerMock.On("HandleComment", mock.Anything, mock.Anything).Return(nil)
|
||||
giteaWebhookHandler = giteaHandlerMock
|
||||
|
||||
router := New(giteaHandlerMock, new(SonarQubeHandlerMock))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/hooks/gitea", bytes.NewBuffer([]byte(`{}`)))
|
||||
req.Header.Add("X-Gitea-Event", "issue_comment")
|
||||
router.ServeHTTP(w, req)
|
||||
router.Engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
giteaHandlerMock.AssertNumberOfCalls(t, "HandleSynchronize", 0)
|
||||
|
@ -177,17 +171,16 @@ func TestGiteaAPIRouteCommentProcessing(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGiteaAPIRouteUnknownEvent(t *testing.T) {
|
||||
router := setupRouter()
|
||||
|
||||
giteaHandlerMock := new(GiteaHandlerMock)
|
||||
giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Return(nil)
|
||||
giteaHandlerMock.On("HandleComment", mock.Anything, mock.Anything).Return(nil)
|
||||
giteaWebhookHandler = giteaHandlerMock
|
||||
|
||||
router := New(giteaHandlerMock, new(SonarQubeHandlerMock))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/hooks/gitea", bytes.NewBuffer([]byte(`{}`)))
|
||||
req.Header.Add("X-Gitea-Event", "unknown")
|
||||
router.ServeHTTP(w, req)
|
||||
router.Engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
giteaHandlerMock.AssertNumberOfCalls(t, "HandleSynchronize", 0)
|
||||
|
|
|
@ -11,5 +11,8 @@ sonar.exclusions=**/*_test.go,contrib/**,docker/**,docs/**,helm/**
|
|||
sonar.tests=.
|
||||
sonar.test.inclusions=**/*_test.go
|
||||
|
||||
# Entrypoint of the application and not properly testable
|
||||
sonar.coverage.exclusions=cmd/gitea-sonarqube-bot.go
|
||||
|
||||
sonar.go.tests.reportPaths=test-report.out
|
||||
sonar.go.coverage.reportPaths=cover.out
|
||||
|
|
Loading…
Reference in a new issue