Merge branch 'main' into webhook-secret-validation
This commit is contained in:
commit
71b16fa296
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -13,6 +13,11 @@ import (
|
|||
webhook "gitea-sonarqube-pr-bot/internal/webhooks/gitea"
|
||||
)
|
||||
|
||||
type GiteaWebhookHandlerInferface interface {
|
||||
HandleSynchronize(rw http.ResponseWriter, r *http.Request)
|
||||
HandleComment(rw http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
type GiteaWebhookHandler struct {
|
||||
giteaSdk giteaSdk.GiteaSdkInterface
|
||||
sqSdk sqSdk.SonarQubeSdkInterface
|
||||
|
@ -105,7 +110,7 @@ func (h *GiteaWebhookHandler) HandleComment(rw http.ResponseWriter, r *http.Requ
|
|||
w.ProcessData(h.giteaSdk, h.sqSdk)
|
||||
}
|
||||
|
||||
func NewGiteaWebhookHandler(g giteaSdk.GiteaSdkInterface, sq sqSdk.SonarQubeSdkInterface) *GiteaWebhookHandler {
|
||||
func NewGiteaWebhookHandler(g giteaSdk.GiteaSdkInterface, sq sqSdk.SonarQubeSdkInterface) GiteaWebhookHandlerInferface {
|
||||
return &GiteaWebhookHandler{
|
||||
giteaSdk: g,
|
||||
sqSdk: sq,
|
||||
|
|
|
@ -1,32 +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"
|
||||
)
|
||||
|
||||
func addPingApi(r *gin.Engine) {
|
||||
r.GET("/ping", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "pong",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type validSonarQubeEndpointHeader struct {
|
||||
SonarQubeProject string `header:"X-SonarQube-Project" binding:"required"`
|
||||
}
|
||||
|
||||
func addSonarQubeEndpoint(r *gin.Engine) {
|
||||
webhookHandler := NewSonarQubeWebhookHandler(giteaSdk.New(), sqSdk.New())
|
||||
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 {
|
||||
|
@ -34,17 +40,8 @@ func addSonarQubeEndpoint(r *gin.Engine) {
|
|||
return
|
||||
}
|
||||
|
||||
webhookHandler.Handle(c.Writer, c.Request)
|
||||
})
|
||||
}
|
||||
|
||||
type validGiteaEndpointHeader struct {
|
||||
GiteaEvent string `header:"X-Gitea-Event" binding:"required"`
|
||||
}
|
||||
|
||||
func addGiteaEndpoint(r *gin.Engine) {
|
||||
webhookHandler := NewGiteaWebhookHandler(giteaSdk.New(), sqSdk.New())
|
||||
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 {
|
||||
|
@ -54,9 +51,9 @@ func addGiteaEndpoint(r *gin.Engine) {
|
|||
|
||||
switch h.GiteaEvent {
|
||||
case "pull_request":
|
||||
webhookHandler.HandleSynchronize(c.Writer, c.Request)
|
||||
s.giteaWebhookHandler.HandleSynchronize(c.Writer, c.Request)
|
||||
case "issue_comment":
|
||||
webhookHandler.HandleComment(c.Writer, c.Request)
|
||||
s.giteaWebhookHandler.HandleComment(c.Writer, c.Request)
|
||||
default:
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "ignore unknown event",
|
||||
|
@ -65,23 +62,14 @@ func addGiteaEndpoint(r *gin.Engine) {
|
|||
})
|
||||
}
|
||||
|
||||
func Serve(c *cli.Context) error {
|
||||
fmt.Println("Hi! I'm the Gitea-SonarQube-PR bot. At your service.")
|
||||
func New(giteaHandler GiteaWebhookHandlerInferface, sonarQubeHandler SonarQubeWebhookHandlerInferface) *ApiServer {
|
||||
s := &ApiServer{
|
||||
Engine: gin.New(),
|
||||
giteaWebhookHandler: giteaHandler,
|
||||
sonarQubeWebhookHandler: sonarQubeHandler,
|
||||
}
|
||||
|
||||
r := gin.New()
|
||||
s.setup()
|
||||
|
||||
r.Use(gin.Recovery())
|
||||
r.Use(gin.LoggerWithConfig(gin.LoggerConfig{
|
||||
SkipPaths: []string{"/ping", "/favicon.ico"},
|
||||
}))
|
||||
|
||||
addPingApi(r)
|
||||
addSonarQubeEndpoint(r)
|
||||
addGiteaEndpoint(r)
|
||||
|
||||
r.GET("/favicon.ico", func(c *gin.Context) {
|
||||
c.Status(http.StatusNoContent)
|
||||
})
|
||||
|
||||
return endless.ListenAndServe(":3000", r)
|
||||
return s
|
||||
}
|
||||
|
|
|
@ -1,26 +1,41 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
giteaSdk "gitea-sonarqube-pr-bot/internal/clients/gitea"
|
||||
sqSdk "gitea-sonarqube-pr-bot/internal/clients/sonarqube"
|
||||
"gitea-sonarqube-pr-bot/internal/settings"
|
||||
webhook "gitea-sonarqube-pr-bot/internal/webhooks/sonarqube"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// Default SDK mocking
|
||||
type HandlerPartialMock struct {
|
||||
type SonarQubeHandlerMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (h *HandlerPartialMock) fetchDetails(w *webhook.Webhook) {
|
||||
h.Called(w)
|
||||
func (h *SonarQubeHandlerMock) Handle(rw http.ResponseWriter, r *http.Request) {
|
||||
h.Called(rw, r)
|
||||
}
|
||||
|
||||
type GiteaHandlerMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (h *GiteaHandlerMock) HandleSynchronize(rw http.ResponseWriter, r *http.Request) {
|
||||
h.Called(rw, r)
|
||||
}
|
||||
|
||||
func (h *GiteaHandlerMock) HandleComment(rw http.ResponseWriter, r *http.Request) {
|
||||
h.Called(rw, r)
|
||||
}
|
||||
|
||||
type GiteaSdkMock struct {
|
||||
|
@ -65,12 +80,109 @@ func (h *SQSdkMock) ComposeGiteaComment(data *sqSdk.CommentComposeData) (string,
|
|||
return "", nil
|
||||
}
|
||||
|
||||
func defaultMockPreparation(h *HandlerPartialMock) {
|
||||
h.On("fetchDetails", mock.Anything).Return(nil)
|
||||
}
|
||||
|
||||
// SETUP: mute logs
|
||||
func TestMain(m *testing.M) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
log.SetOutput(ioutil.Discard)
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestNonAPIRoutes(t *testing.T) {
|
||||
router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/favicon.ico", nil)
|
||||
router.Engine.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusNoContent, w.Code)
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "/ping", nil)
|
||||
router.Engine.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
func TestSonarQubeAPIRouteMissingProjectHeader(t *testing.T) {
|
||||
router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/hooks/sonarqube", bytes.NewBuffer([]byte(`{}`)))
|
||||
router.Engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
func TestSonarQubeAPIRouteProcessing(t *testing.T) {
|
||||
sonarQubeHandlerMock := new(SonarQubeHandlerMock)
|
||||
sonarQubeHandlerMock.On("Handle", mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
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.Engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
sonarQubeHandlerMock.AssertNumberOfCalls(t, "Handle", 1)
|
||||
}
|
||||
|
||||
func TestGiteaAPIRouteMissingEventHeader(t *testing.T) {
|
||||
router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/hooks/gitea", bytes.NewBuffer([]byte(`{}`)))
|
||||
router.Engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
func TestGiteaAPIRouteSynchronizeProcessing(t *testing.T) {
|
||||
giteaHandlerMock := new(GiteaHandlerMock)
|
||||
giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Return(nil)
|
||||
giteaHandlerMock.On("HandleComment", mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
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.Engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
giteaHandlerMock.AssertNumberOfCalls(t, "HandleSynchronize", 1)
|
||||
giteaHandlerMock.AssertNumberOfCalls(t, "HandleComment", 0)
|
||||
}
|
||||
|
||||
func TestGiteaAPIRouteCommentProcessing(t *testing.T) {
|
||||
giteaHandlerMock := new(GiteaHandlerMock)
|
||||
giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Return(nil)
|
||||
giteaHandlerMock.On("HandleComment", mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
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.Engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
giteaHandlerMock.AssertNumberOfCalls(t, "HandleSynchronize", 0)
|
||||
giteaHandlerMock.AssertNumberOfCalls(t, "HandleComment", 1)
|
||||
}
|
||||
|
||||
func TestGiteaAPIRouteUnknownEvent(t *testing.T) {
|
||||
giteaHandlerMock := new(GiteaHandlerMock)
|
||||
giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Return(nil)
|
||||
giteaHandlerMock.On("HandleComment", mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
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.Engine.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
giteaHandlerMock.AssertNumberOfCalls(t, "HandleSynchronize", 0)
|
||||
giteaHandlerMock.AssertNumberOfCalls(t, "HandleComment", 0)
|
||||
}
|
||||
|
|
|
@ -14,10 +14,13 @@ import (
|
|||
webhook "gitea-sonarqube-pr-bot/internal/webhooks/sonarqube"
|
||||
)
|
||||
|
||||
type SonarQubeWebhookHandlerInferface interface {
|
||||
Handle(rw http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
type SonarQubeWebhookHandler struct {
|
||||
fetchDetails func(w *webhook.Webhook)
|
||||
giteaSdk giteaSdk.GiteaSdkInterface
|
||||
sqSdk sqSdk.SonarQubeSdkInterface
|
||||
giteaSdk giteaSdk.GiteaSdkInterface
|
||||
sqSdk sqSdk.SonarQubeSdkInterface
|
||||
}
|
||||
|
||||
func (*SonarQubeWebhookHandler) inProjectsMapping(p []settings.Project, n string) (bool, int) {
|
||||
|
@ -31,13 +34,6 @@ func (*SonarQubeWebhookHandler) inProjectsMapping(p []settings.Project, n string
|
|||
}
|
||||
|
||||
func (h *SonarQubeWebhookHandler) processData(w *webhook.Webhook, repo settings.GiteaRepository) {
|
||||
if strings.ToLower(w.Branch.Type) != "pull_request" {
|
||||
log.Println("Ignore Hook for non-PR")
|
||||
return
|
||||
}
|
||||
|
||||
h.fetchDetails(w)
|
||||
|
||||
status := giteaSdk.StatusOK
|
||||
if w.QualityGate.Status != "OK" {
|
||||
status = giteaSdk.StatusFailure
|
||||
|
@ -104,19 +100,21 @@ func (h *SonarQubeWebhookHandler) Handle(rw http.ResponseWriter, r *http.Request
|
|||
|
||||
// Send response to SonarQube at this point to ensure being within 10 seconds limit of webhook response timeout
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
||||
if strings.ToLower(w.Branch.Type) != "pull_request" {
|
||||
io.WriteString(rw, `{"message": "Ignore Hook for non-PR analysis."}`)
|
||||
log.Println("Ignore Hook for non-PR analysis")
|
||||
return
|
||||
}
|
||||
|
||||
io.WriteString(rw, `{"message": "Processing data. See bot logs for details."}`)
|
||||
|
||||
h.processData(w, settings.Projects[pIdx].Gitea)
|
||||
}
|
||||
|
||||
func fetchDetails(w *webhook.Webhook) {
|
||||
log.Printf("This method will load additional data from SonarQube based on PR %d", w.PRIndex)
|
||||
}
|
||||
|
||||
func NewSonarQubeWebhookHandler(g giteaSdk.GiteaSdkInterface, sq sqSdk.SonarQubeSdkInterface) *SonarQubeWebhookHandler {
|
||||
func NewSonarQubeWebhookHandler(g giteaSdk.GiteaSdkInterface, sq sqSdk.SonarQubeSdkInterface) SonarQubeWebhookHandlerInferface {
|
||||
return &SonarQubeWebhookHandler{
|
||||
fetchDetails: fetchDetails,
|
||||
giteaSdk: g,
|
||||
sqSdk: sq,
|
||||
giteaSdk: g,
|
||||
sqSdk: sq,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,12 +11,8 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func withValidSonarQubeRequestData(t *testing.T, mockPreparation func(*HandlerPartialMock), jsonBody []byte) (*http.Request, *httptest.ResponseRecorder, http.HandlerFunc, *HandlerPartialMock) {
|
||||
partialMock := new(HandlerPartialMock)
|
||||
mockPreparation(partialMock)
|
||||
|
||||
func withValidSonarQubeRequestData(t *testing.T, jsonBody []byte) (*http.Request, *httptest.ResponseRecorder, http.HandlerFunc) {
|
||||
webhookHandler := NewSonarQubeWebhookHandler(new(GiteaSdkMock), new(SQSdkMock))
|
||||
webhookHandler.fetchDetails = partialMock.fetchDetails
|
||||
|
||||
req, err := http.NewRequest("POST", "/hooks/sonarqube", bytes.NewBuffer(jsonBody))
|
||||
if err != nil {
|
||||
|
@ -27,7 +23,7 @@ func withValidSonarQubeRequestData(t *testing.T, mockPreparation func(*HandlerPa
|
|||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(webhookHandler.Handle)
|
||||
|
||||
return req, rr, handler, partialMock
|
||||
return req, rr, handler
|
||||
}
|
||||
|
||||
func TestHandleSonarQubeWebhookProjectMapped(t *testing.T) {
|
||||
|
@ -43,7 +39,7 @@ func TestHandleSonarQubeWebhookProjectMapped(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
req, rr, handler, _ := withValidSonarQubeRequestData(t, defaultMockPreparation, []byte(`{ "serverUrl": "https://example.com/sonarqube", "taskId": "AXouyxDpizdp4B1K", "status": "SUCCESS", "analysedAt": "2021-05-21T12:12:07+0000", "revision": "f84442009c09b1adc278b6aa80a3853419f54007", "changedAt": "2021-05-21T12:12:07+0000", "project": { "key": "pr-bot", "name": "PR Bot", "url": "https://example.com/sonarqube/dashboard?id=pr-bot" }, "branch": { "name": "PR-1337", "type": "PULL_REQUEST", "isMain": false, "url": "https://example.com/sonarqube/dashboard?id=pr-bot&pullRequest=PR-1337" }, "qualityGate": { "name": "PR Bot", "status": "OK", "conditions": [ { "metric": "new_reliability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_maintainability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_hotspots_reviewed", "operator": "LESS_THAN", "status": "NO_VALUE", "errorThreshold": "100" } ] }, "properties": {} }`))
|
||||
req, rr, handler := withValidSonarQubeRequestData(t, []byte(`{ "serverUrl": "https://example.com/sonarqube", "taskId": "AXouyxDpizdp4B1K", "status": "SUCCESS", "analysedAt": "2021-05-21T12:12:07+0000", "revision": "f84442009c09b1adc278b6aa80a3853419f54007", "changedAt": "2021-05-21T12:12:07+0000", "project": { "key": "pr-bot", "name": "PR Bot", "url": "https://example.com/sonarqube/dashboard?id=pr-bot" }, "branch": { "name": "PR-1337", "type": "PULL_REQUEST", "isMain": false, "url": "https://example.com/sonarqube/dashboard?id=pr-bot&pullRequest=PR-1337" }, "qualityGate": { "name": "PR Bot", "status": "OK", "conditions": [ { "metric": "new_reliability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_maintainability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_hotspots_reviewed", "operator": "LESS_THAN", "status": "NO_VALUE", "errorThreshold": "100" } ] }, "properties": {} }`))
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
|
@ -58,7 +54,7 @@ func TestHandleSonarQubeWebhookProjectNotMapped(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
req, rr, handler, _ := withValidSonarQubeRequestData(t, defaultMockPreparation, []byte(`{ "serverUrl": "https://example.com/sonarqube", "taskId": "AXouyxDpizdp4B1K", "status": "SUCCESS", "analysedAt": "2021-05-21T12:12:07+0000", "revision": "f84442009c09b1adc278b6aa80a3853419f54007", "changedAt": "2021-05-21T12:12:07+0000", "project": { "key": "pr-bot", "name": "PR Bot", "url": "https://example.com/sonarqube/dashboard?id=pr-bot" }, "branch": { "name": "PR-1337", "type": "PULL_REQUEST", "isMain": false, "url": "https://example.com/sonarqube/dashboard?id=pr-bot&pullRequest=PR-1337" }, "qualityGate": { "name": "PR Bot", "status": "OK", "conditions": [ { "metric": "new_reliability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_maintainability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_hotspots_reviewed", "operator": "LESS_THAN", "status": "NO_VALUE", "errorThreshold": "100" } ] }, "properties": {} }`))
|
||||
req, rr, handler := withValidSonarQubeRequestData(t, []byte(`{ "serverUrl": "https://example.com/sonarqube", "taskId": "AXouyxDpizdp4B1K", "status": "SUCCESS", "analysedAt": "2021-05-21T12:12:07+0000", "revision": "f84442009c09b1adc278b6aa80a3853419f54007", "changedAt": "2021-05-21T12:12:07+0000", "project": { "key": "pr-bot", "name": "PR Bot", "url": "https://example.com/sonarqube/dashboard?id=pr-bot" }, "branch": { "name": "PR-1337", "type": "PULL_REQUEST", "isMain": false, "url": "https://example.com/sonarqube/dashboard?id=pr-bot&pullRequest=PR-1337" }, "qualityGate": { "name": "PR Bot", "status": "OK", "conditions": [ { "metric": "new_reliability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_maintainability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_hotspots_reviewed", "operator": "LESS_THAN", "status": "NO_VALUE", "errorThreshold": "100" } ] }, "properties": {} }`))
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
|
@ -74,7 +70,7 @@ func TestHandleSonarQubeWebhookInvalidJSONBody(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
req, rr, handler, _ := withValidSonarQubeRequestData(t, defaultMockPreparation, []byte(`{ "serverUrl": ["invalid-server-url-content"] }`))
|
||||
req, rr, handler := withValidSonarQubeRequestData(t, []byte(`{ "serverUrl": ["invalid-server-url-content"] }`))
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusUnprocessableEntity, rr.Code)
|
||||
|
@ -116,12 +112,11 @@ func TestHandleSonarQubeWebhookForPullRequest(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
req, rr, handler, partialMock := withValidSonarQubeRequestData(t, defaultMockPreparation, []byte(`{ "serverUrl": "https://example.com/sonarqube", "taskId": "AXouyxDpizdp4B1K", "status": "SUCCESS", "analysedAt": "2021-05-21T12:12:07+0000", "revision": "f84442009c09b1adc278b6aa80a3853419f54007", "changedAt": "2021-05-21T12:12:07+0000", "project": { "key": "pr-bot", "name": "PR Bot", "url": "https://example.com/sonarqube/dashboard?id=pr-bot" }, "branch": { "name": "PR-1337", "type": "PULL_REQUEST", "isMain": false, "url": "https://example.com/sonarqube/dashboard?id=pr-bot&pullRequest=PR-1337" }, "qualityGate": { "name": "PR Bot", "status": "OK", "conditions": [ { "metric": "new_reliability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_maintainability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_hotspots_reviewed", "operator": "LESS_THAN", "status": "NO_VALUE", "errorThreshold": "100" } ] }, "properties": {} }`))
|
||||
req, rr, handler := withValidSonarQubeRequestData(t, []byte(`{ "serverUrl": "https://example.com/sonarqube", "taskId": "AXouyxDpizdp4B1K", "status": "SUCCESS", "analysedAt": "2021-05-21T12:12:07+0000", "revision": "f84442009c09b1adc278b6aa80a3853419f54007", "changedAt": "2021-05-21T12:12:07+0000", "project": { "key": "pr-bot", "name": "PR Bot", "url": "https://example.com/sonarqube/dashboard?id=pr-bot" }, "branch": { "name": "PR-1337", "type": "PULL_REQUEST", "isMain": false, "url": "https://example.com/sonarqube/dashboard?id=pr-bot&pullRequest=PR-1337" }, "qualityGate": { "name": "PR Bot", "status": "OK", "conditions": [ { "metric": "new_reliability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_maintainability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_hotspots_reviewed", "operator": "LESS_THAN", "status": "NO_VALUE", "errorThreshold": "100" } ] }, "properties": {} }`))
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
assert.Equal(t, `{"message": "Processing data. See bot logs for details."}`, rr.Body.String())
|
||||
partialMock.AssertNumberOfCalls(t, "fetchDetails", 1)
|
||||
}
|
||||
|
||||
func TestHandleSonarQubeWebhookForBranch(t *testing.T) {
|
||||
|
@ -138,10 +133,9 @@ func TestHandleSonarQubeWebhookForBranch(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
req, rr, handler, partialMock := withValidSonarQubeRequestData(t, defaultMockPreparation, []byte(`{ "serverUrl": "https://example.com/sonarqube", "taskId": "AXouyxDpizdp4B1K", "status": "SUCCESS", "analysedAt": "2021-05-21T12:12:07+0000", "revision": "f84442009c09b1adc278b6aa80a3853419f54007", "changedAt": "2021-05-21T12:12:07+0000", "project": { "key": "pr-bot", "name": "PR Bot", "url": "https://example.com/sonarqube/dashboard?id=pr-bot" }, "branch": { "name": "PR-1337", "type": "BRANCH", "isMain": false, "url": "https://example.com/sonarqube/dashboard?id=pr-bot&pullRequest=PR-1337" }, "qualityGate": { "name": "PR Bot", "status": "OK", "conditions": [ { "metric": "new_reliability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_maintainability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_hotspots_reviewed", "operator": "LESS_THAN", "status": "NO_VALUE", "errorThreshold": "100" } ] }, "properties": {} }`))
|
||||
req, rr, handler := withValidSonarQubeRequestData(t, []byte(`{ "serverUrl": "https://example.com/sonarqube", "taskId": "AXouyxDpizdp4B1K", "status": "SUCCESS", "analysedAt": "2021-05-21T12:12:07+0000", "revision": "f84442009c09b1adc278b6aa80a3853419f54007", "changedAt": "2021-05-21T12:12:07+0000", "project": { "key": "pr-bot", "name": "PR Bot", "url": "https://example.com/sonarqube/dashboard?id=pr-bot" }, "branch": { "name": "PR-1337", "type": "BRANCH", "isMain": false, "url": "https://example.com/sonarqube/dashboard?id=pr-bot&pullRequest=PR-1337" }, "qualityGate": { "name": "PR Bot", "status": "OK", "conditions": [ { "metric": "new_reliability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_maintainability_rating", "operator": "GREATER_THAN", "value": "1", "status": "OK", "errorThreshold": "1" }, { "metric": "new_security_hotspots_reviewed", "operator": "LESS_THAN", "status": "NO_VALUE", "errorThreshold": "100" } ] }, "properties": {} }`))
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
assert.Equal(t, `{"message": "Processing data. See bot logs for details."}`, rr.Body.String())
|
||||
partialMock.AssertNumberOfCalls(t, "fetchDetails", 0)
|
||||
assert.Equal(t, `{"message": "Ignore Hook for non-PR analysis."}`, rr.Body.String())
|
||||
}
|
||||
|
|
|
@ -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/main.go
|
||||
|
||||
sonar.go.tests.reportPaths=test-report.out
|
||||
sonar.go.coverage.reportPaths=cover.out
|
||||
|
|
Loading…
Reference in a new issue