Introduce better test case structure

Signed-off-by: Steven Kriegler <sk.bunsenbrenner@gmail.com>
This commit is contained in:
justusbunsi 2022-07-12 11:20:08 +02:00
parent 51211d77cd
commit 54beca9c25
No known key found for this signature in database
GPG key ID: 82B29BF2507F9F8B
8 changed files with 1169 additions and 1127 deletions

View file

@ -6,12 +6,14 @@ import (
"github.com/stretchr/testify/assert"
)
func TestIsValidBotCommentForInvalidComment(t *testing.T) {
assert.False(t, IsValidBotComment(""), "Undetected missing action prefix")
assert.False(t, IsValidBotComment("/sq-bot invalid-command"), "Undetected invalid bot command")
assert.False(t, IsValidBotComment("Some context with /sq-bot review within"), "Incorrect bot prefix detected inside random comment")
}
func TestIsValidBotComment(t *testing.T) {
t.Run("Valid", func(t *testing.T) {
assert.True(t, IsValidBotComment("/sq-bot review"), "Correct bot comment not recognized")
})
func TestIsValidBotCommentForValidComment(t *testing.T) {
assert.True(t, IsValidBotComment("/sq-bot review"), "Correct bot comment not recognized")
t.Run("Invalid", func(t *testing.T) {
assert.False(t, IsValidBotComment(""), "Undetected missing action prefix")
assert.False(t, IsValidBotComment("/sq-bot invalid-command"), "Undetected invalid bot command")
assert.False(t, IsValidBotComment("Some context with /sq-bot review within"), "Incorrect bot prefix detected inside random comment")
})
}

File diff suppressed because one or more lines are too long

View file

@ -93,105 +93,115 @@ func TestMain(m *testing.M) {
}
func TestNonAPIRoutes(t *testing.T) {
router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock))
t.Run("favicon", func(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", "/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)
t.Run("ping", func(t *testing.T) {
router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock))
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))
func TestSonarQubeAPIRoute(t *testing.T) {
t.Run("Missing project header", func(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)
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/hooks/sonarqube", bytes.NewBuffer([]byte(`{}`)))
router.Engine.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
assert.Equal(t, http.StatusNotFound, w.Code)
})
t.Run("Processing", func(t *testing.T) {
sonarQubeHandlerMock := new(SonarQubeHandlerMock)
sonarQubeHandlerMock.On("Handle", mock.IsType(&http.Request{}))
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)
sonarQubeHandlerMock.AssertExpectations(t)
})
}
func TestSonarQubeAPIRouteProcessing(t *testing.T) {
sonarQubeHandlerMock := new(SonarQubeHandlerMock)
sonarQubeHandlerMock.On("Handle", mock.IsType(&http.Request{}))
func TestGiteaAPIRoute(t *testing.T) {
t.Run("Missing event header", func(t *testing.T) {
router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock))
router := New(new(GiteaHandlerMock), sonarQubeHandlerMock)
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/hooks/gitea", bytes.NewBuffer([]byte(`{}`)))
router.Engine.ServeHTTP(w, req)
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.StatusNotFound, w.Code)
})
assert.Equal(t, http.StatusOK, w.Code)
sonarQubeHandlerMock.AssertNumberOfCalls(t, "Handle", 1)
sonarQubeHandlerMock.AssertExpectations(t)
}
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).Maybe()
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)
giteaHandlerMock.AssertExpectations(t)
}
func TestGiteaAPIRouteCommentProcessing(t *testing.T) {
giteaHandlerMock := new(GiteaHandlerMock)
giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Maybe()
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)
giteaHandlerMock.AssertExpectations(t)
}
func TestGiteaAPIRouteUnknownEvent(t *testing.T) {
giteaHandlerMock := new(GiteaHandlerMock)
giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Maybe()
giteaHandlerMock.On("HandleComment", mock.Anything, mock.Anything).Maybe()
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)
giteaHandlerMock.AssertExpectations(t)
t.Run("Processing synchronize", func(t *testing.T) {
giteaHandlerMock := new(GiteaHandlerMock)
giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Return(nil)
giteaHandlerMock.On("HandleComment", mock.Anything, mock.Anything).Maybe()
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)
giteaHandlerMock.AssertExpectations(t)
})
t.Run("Processing comment", func(t *testing.T) {
giteaHandlerMock := new(GiteaHandlerMock)
giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Maybe()
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)
giteaHandlerMock.AssertExpectations(t)
})
t.Run("Unknown event", func(t *testing.T) {
giteaHandlerMock := new(GiteaHandlerMock)
giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Maybe()
giteaHandlerMock.On("HandleComment", mock.Anything, mock.Anything).Maybe()
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)
giteaHandlerMock.AssertExpectations(t)
})
}

View file

@ -10,37 +10,38 @@ func getRequestData() []byte {
return []byte(`{"serverUrl":"https://example.com","status":"SUCCESS","analysedAt":"2022-05-15T16:45:31+0000","revision":"378080777919s07657a07f7a3e2d05dc75f64edd","changedAt":"2022-05-15T16:41:39+0000","project":{"key":"gitea-sonarqube-bot","name":"Gitea SonarQube Bot","url":"https://example.com/dashboard?id=gitea-sonarqube-bot"},"branch":{"name":"PR-1822","type":"PULL_REQUEST","isMain":false,"url":"https://example.com/dashboard?id=gitea-sonarqube-bot&pullRequest=PR-1822"},"qualityGate":{"name":"GiteaSonarQubeBot","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":"OK","errorThreshold":"100"}]},"properties":{"sonar.analysis.sqbot":"378080777919s07657a07f7a3e2d05dc75f64edd"}}`)
}
func TestIsValidWebhookSuccess(t *testing.T) {
actual, _ := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "647f2395d30b1b7efcb58d9338be5b69c2addb54faf6bde6314a57ea28f45467", "test-component")
assert.True(t, actual, "Expected successful webhook signature validation")
}
func TestIsValidWebhook(t *testing.T) {
t.Run("Success", func(t *testing.T) {
actual, _ := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "647f2395d30b1b7efcb58d9338be5b69c2addb54faf6bde6314a57ea28f45467", "test-component")
assert.True(t, actual, "Expected successful webhook signature validation")
})
func TestIsValidWebhookNothingConfiguredOrProvidedSuccess(t *testing.T) {
actual, _ := isValidWebhook(getRequestData(), "", "", "test-component")
assert.True(t, actual, "Webhook signature validation not skipped")
}
t.Run("Nothing configured or provided", func(t *testing.T) {
actual, _ := isValidWebhook(getRequestData(), "", "", "test-component")
assert.True(t, actual, "Webhook signature validation not skipped")
})
func TestIsValidWebhookSignatureDecodingFailure(t *testing.T) {
actual, err := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "invalid-signature", "test-component")
assert.False(t, actual)
assert.EqualError(t, err, "Error decoding signature for test-component webhook.", "Undetected signature encoding error")
}
t.Run("Signature decoding error", func(t *testing.T) {
actual, err := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "invalid-signature", "test-component")
assert.False(t, actual)
assert.EqualError(t, err, "Error decoding signature for test-component webhook.", "Undetected signature encoding error")
})
func TestIsValidWebhookSignatureMismatchFailure(t *testing.T) {
actual, err := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "fde6a666b7a1a46c27efb1961c17b46b6cf7aa13db5560e5ac95e801a18a92f3", "test-component")
assert.False(t, actual)
assert.EqualError(t, err, "Signature header does not match the received test-component webhook content. Request rejected.", "Undetected signature mismatch")
// assert.EqualError(t, err, "Signature header received but no test-component webhook secret configured. Request rejected due to possible configuration mismatch.", "Undetected configuration mismatch (1)")
}
t.Run("Signature mismatch", func(t *testing.T) {
actual, err := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "fde6a666b7a1a46c27efb1961c17b46b6cf7aa13db5560e5ac95e801a18a92f3", "test-component")
assert.False(t, actual)
assert.EqualError(t, err, "Signature header does not match the received test-component webhook content. Request rejected.", "Undetected signature mismatch")
})
func TestIsValidWebhookEmptySecretConfigurationFailure(t *testing.T) {
actual, err := isValidWebhook(getRequestData(), "", "647f2395d30b1b7efcb58d9338be5b69c2addb54faf6bde6314a57ea28f45467", "test-component")
assert.False(t, actual)
assert.EqualError(t, err, "Signature header received but no test-component webhook secret configured. Request rejected due to possible configuration mismatch.", "Undetected configuration mismatch (1)")
}
t.Run("Empty secret configuration", func(t *testing.T) {
actual, err := isValidWebhook(getRequestData(), "", "647f2395d30b1b7efcb58d9338be5b69c2addb54faf6bde6314a57ea28f45467", "test-component")
assert.False(t, actual)
assert.EqualError(t, err, "Signature header received but no test-component webhook secret configured. Request rejected due to possible configuration mismatch.", "Undetected configuration mismatch (1)")
})
func TestIsValidWebhookEmptySignatureConfigurationFailure(t *testing.T) {
actual, err := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "", "test-component")
assert.False(t, actual)
assert.EqualError(t, err, "test-component webhook secret configured but no signature header received. Request rejected due to possible configuration mismatch.", "Undetected configuration mismatch (2)")
t.Run("Empty signature configuration", func(t *testing.T) {
actual, err := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "", "test-component")
assert.False(t, actual)
assert.EqualError(t, err, "test-component webhook secret configured but no signature header received. Request rejected due to possible configuration mismatch.", "Undetected configuration mismatch (2)")
})
}

View file

@ -34,137 +34,139 @@ func withValidSonarQubeRequestData(t *testing.T, jsonBody []byte) (*http.Request
return req, rr, handler
}
func TestHandleSonarQubeWebhookProjectMapped(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
settings.SonarQube = settings.SonarQubeConfig{
Webhook: &settings.Webhook{
Secret: "",
},
}
settings.Projects = []settings.Project{
{
SonarQube: struct{ Key string }{
Key: "pr-bot",
func TestHandleSonarQubeWebhook(t *testing.T) {
t.Run("With mapped Project", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
settings.SonarQube = settings.SonarQubeConfig{
Webhook: &settings.Webhook{
Secret: "",
},
},
}
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)
}
settings.Projects = []settings.Project{
{
SonarQube: struct{ Key string }{
Key: "pr-bot",
},
},
}
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())
assert.Equal(t, http.StatusOK, rr.Code)
assert.Equal(t, `{"message": "Processing data. See bot logs for details."}`, rr.Body.String())
t.Cleanup(func() {
settings.Pattern = nil
})
}
func TestHandleSonarQubeWebhookProjectNotMapped(t *testing.T) {
settings.Projects = []settings.Project{
{
SonarQube: struct{ Key string }{
Key: "another-project",
},
},
}
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": "Project 'pr-bot' not in configured list. Request ignored."}`, rr.Body.String())
}
func TestHandleSonarQubeWebhookInvalidJSONBody(t *testing.T) {
settings.Projects = []settings.Project{
{
SonarQube: struct{ Key string }{
Key: "pr-bot",
},
},
}
req, rr, handler := withValidSonarQubeRequestData(t, []byte(`{ "serverUrl": ["invalid-server-url-content"] }`))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusUnprocessableEntity, rr.Code)
assert.Equal(t, `{"message": "Error parsing POST body."}`, rr.Body.String())
}
func TestHandleSonarQubeWebhookInvalidWebhookSignature(t *testing.T) {
settings.SonarQube = settings.SonarQubeConfig{
Webhook: &settings.Webhook{
Secret: "sonarqube-test-webhook-secret",
},
}
settings.Projects = []settings.Project{
{
SonarQube: struct{ Key string }{
Key: "pr-bot",
},
},
}
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": {} }`))
req.Header.Set("X-Sonar-Webhook-HMAC-SHA256", "647f2395d30b1b7efcb58d9338be5b69c2addb54faf6bde6314a57ea28f45467")
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusPreconditionFailed, rr.Code)
assert.Equal(t, `{"message": "Webhook validation failed. Request rejected."}`, rr.Body.String())
}
func TestHandleSonarQubeWebhookForPullRequest(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
settings.SonarQube = settings.SonarQubeConfig{
Webhook: &settings.Webhook{
Secret: "",
},
}
settings.Projects = []settings.Project{
{
SonarQube: struct{ Key string }{
Key: "pr-bot",
},
},
}
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())
t.Cleanup(func() {
settings.Pattern = nil
})
}
func TestHandleSonarQubeWebhookForBranch(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
settings.SonarQube = settings.SonarQubeConfig{
Webhook: &settings.Webhook{
Secret: "",
},
}
settings.Projects = []settings.Project{
{
SonarQube: struct{ Key string }{
Key: "pr-bot",
},
},
}
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": "Ignore Hook for non-PR analysis."}`, rr.Body.String())
t.Cleanup(func() {
settings.Pattern = nil
t.Cleanup(func() {
settings.Pattern = nil
})
})
t.Run("Without mapped project", func(t *testing.T) {
settings.Projects = []settings.Project{
{
SonarQube: struct{ Key string }{
Key: "another-project",
},
},
}
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": "Project 'pr-bot' not in configured list. Request ignored."}`, rr.Body.String())
})
t.Run("With invalid JSON body", func(t *testing.T) {
settings.Projects = []settings.Project{
{
SonarQube: struct{ Key string }{
Key: "pr-bot",
},
},
}
req, rr, handler := withValidSonarQubeRequestData(t, []byte(`{ "serverUrl": ["invalid-server-url-content"] }`))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusUnprocessableEntity, rr.Code)
assert.Equal(t, `{"message": "Error parsing POST body."}`, rr.Body.String())
})
t.Run("With invalid webhook signature", func(t *testing.T) {
settings.SonarQube = settings.SonarQubeConfig{
Webhook: &settings.Webhook{
Secret: "sonarqube-test-webhook-secret",
},
}
settings.Projects = []settings.Project{
{
SonarQube: struct{ Key string }{
Key: "pr-bot",
},
},
}
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": {} }`))
req.Header.Set("X-Sonar-Webhook-HMAC-SHA256", "647f2395d30b1b7efcb58d9338be5b69c2addb54faf6bde6314a57ea28f45467")
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusPreconditionFailed, rr.Code)
assert.Equal(t, `{"message": "Webhook validation failed. Request rejected."}`, rr.Body.String())
})
t.Run("Running for Pull Request", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
settings.SonarQube = settings.SonarQubeConfig{
Webhook: &settings.Webhook{
Secret: "",
},
}
settings.Projects = []settings.Project{
{
SonarQube: struct{ Key string }{
Key: "pr-bot",
},
},
}
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())
t.Cleanup(func() {
settings.Pattern = nil
})
})
t.Run("Running for branch", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
settings.SonarQube = settings.SonarQubeConfig{
Webhook: &settings.Webhook{
Secret: "",
},
}
settings.Projects = []settings.Project{
{
SonarQube: struct{ Key string }{
Key: "pr-bot",
},
},
}
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": "Ignore Hook for non-PR analysis."}`, rr.Body.String())
t.Cleanup(func() {
settings.Pattern = nil
})
})
}

View file

@ -28,29 +28,31 @@ func (c *ClientMock) Do(req *http.Request) (*http.Response, error) {
}, c.responseError
}
func TestParsePRIndexSuccess(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
func TestParsePRIndex(t *testing.T) {
t.Run("Success", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
actual, _ := ParsePRIndex("PR-1337")
assert.Equal(t, 1337, actual, "PR index parsing is broken")
actual, _ := ParsePRIndex("PR-1337")
assert.Equal(t, 1337, actual, "PR index parsing is broken")
t.Cleanup(func() {
settings.Pattern = nil
t.Cleanup(func() {
settings.Pattern = nil
})
})
}
func TestParsePRIndexNonIntegerFailure(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
t.Run("No integer value", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
_, err := ParsePRIndex("PR-invalid")
assert.EqualErrorf(t, err, "branch name 'PR-invalid' does not match regex '^PR-(\\d+)$'", "Integer parsing succeeds unexpectedly")
_, err := ParsePRIndex("PR-invalid")
assert.EqualErrorf(t, err, "branch name 'PR-invalid' does not match regex '^PR-(\\d+)$'", "Integer parsing succeeds unexpectedly")
t.Cleanup(func() {
settings.Pattern = nil
t.Cleanup(func() {
settings.Pattern = nil
})
})
}
@ -66,16 +68,14 @@ func TestPRNameFromIndex(t *testing.T) {
})
}
func TestGetRenderedQualityGateSuccess(t *testing.T) {
actual := GetRenderedQualityGate("OK")
func TestGetRenderedQualityGate(t *testing.T) {
t.Run("Passed", func(t *testing.T) {
assert.Contains(t, GetRenderedQualityGate("OK"), ":white_check_mark:", "Undetected successful quality gate during status rendering")
})
assert.Contains(t, actual, ":white_check_mark:", "Undetected successful quality gate during status rendering")
}
func TestGetRenderedQualityGateFailure(t *testing.T) {
actual := GetRenderedQualityGate("ERROR")
assert.Contains(t, actual, ":x:", "Undetected failed quality gate during status rendering")
t.Run("Failed", func(t *testing.T) {
assert.Contains(t, GetRenderedQualityGate("ERROR"), ":x:", "Undetected failed quality gate during status rendering")
})
}
func TestGetPullRequestUrl(t *testing.T) {
@ -94,435 +94,445 @@ func TestGetPullRequestUrl(t *testing.T) {
})
}
func TestRetrieveDataFromApiSuccess(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
func TestRetrieveDataFromApi(t *testing.T) {
t.Run("Success", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
wrapper := &PullsResponse{}
err := retrieveDataFromApi(sdk, request, wrapper)
assert.Nil(t, err, "Successful data retrieval broken and throws error")
assert.Equal(t, "Basic dGVzdC10b2tlbjo=", request.Header.Get("Authorization"), "Authorization header not set")
assert.Equal(t, "PR-1", wrapper.PullRequests[0].Key, "Unmarshallowing into wrapper broken")
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
wrapper := &PullsResponse{}
err := retrieveDataFromApi(sdk, request, wrapper)
t.Run("Internal error", func(t *testing.T) {
expected := fmt.Errorf("This error indicates an error while performing the request")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
recoder: httptest.NewRecorder(),
responseError: expected,
},
bodyReader: io.ReadAll,
}
assert.Nil(t, err, "Successful data retrieval broken and throws error")
assert.Equal(t, "Basic dGVzdC10b2tlbjo=", request.Header.Get("Authorization"), "Authorization header not set")
assert.Equal(t, "PR-1", wrapper.PullRequests[0].Key, "Unmarshallowing into wrapper broken")
}
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
err := retrieveDataFromApi(sdk, request, &PullsResponse{})
func TestRetrieveDataFromApiRequestError(t *testing.T) {
expected := fmt.Errorf("This error indicates an error while performing the request")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
recoder: httptest.NewRecorder(),
responseError: expected,
},
bodyReader: io.ReadAll,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
err := retrieveDataFromApi(sdk, request, &PullsResponse{})
assert.ErrorIs(t, err, expected, "Undetected request performing error")
}
func TestRetrieveDataFromApiUnauthorized(t *testing.T) {
recorder := httptest.NewRecorder()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
recorder.Code = http.StatusUnauthorized
assert.ErrorIs(t, err, expected, "Undetected request performing error")
})
sdk := &SonarQubeSdk{
token: "simulated-invalid-token",
client: &ClientMock{
handler: handler,
recoder: recorder,
responseError: nil,
},
bodyReader: io.ReadAll,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
err := retrieveDataFromApi(sdk, request, &PullsResponse{})
t.Run("Unauthorized", func(t *testing.T) {
recorder := httptest.NewRecorder()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
recorder.Code = http.StatusUnauthorized
})
sdk := &SonarQubeSdk{
token: "simulated-invalid-token",
client: &ClientMock{
handler: handler,
recoder: recorder,
responseError: nil,
},
bodyReader: io.ReadAll,
}
assert.Errorf(t, err, "missing or invalid API token", "Undetected unauthorized error")
}
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
err := retrieveDataFromApi(sdk, request, &PullsResponse{})
func TestRetrieveDataFromApiBodyReadError(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
assert.Errorf(t, err, "missing or invalid API token", "Undetected unauthorized error")
})
expected := fmt.Errorf("Error reading body content")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: func(r io.Reader) ([]byte, error) {
return []byte(``), expected
},
}
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
err := retrieveDataFromApi(sdk, request, &PullsResponse{})
t.Run("Body read error", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
})
expected := fmt.Errorf("Error reading body content")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: func(r io.Reader) ([]byte, error) {
return []byte(``), expected
},
}
assert.ErrorIs(t, err, expected, "Undetected body processing error")
}
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
err := retrieveDataFromApi(sdk, request, &PullsResponse{})
func TestRetrieveDataFromApiBodyUnmarshalError(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullReq`))
assert.ErrorIs(t, err, expected, "Undetected body processing error")
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
err := retrieveDataFromApi(sdk, request, &PullsResponse{})
t.Run("Unmarshal error", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullReq`))
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
}
assert.Errorf(t, err, "unexpected end of JSON input", "Undetected body unmarshal error")
}
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
err := retrieveDataFromApi(sdk, request, &PullsResponse{})
func TestFetchPullRequestsSuccess(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
actual, err := sdk.fetchPullRequests("test-project")
assert.Nil(t, err, "Successful data retrieval broken and throws error")
assert.IsType(t, &PullsResponse{}, actual, "Happy path broken")
}
func TestFetchPullRequestsRequestBuildingFailure(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
})
expected := fmt.Errorf("Some simulated error")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return nil, expected
},
}
_, err := sdk.fetchPullRequests("test-project")
assert.Equal(t, expected, err, "Unexpected error instance returned")
}
func TestFetchPullRequestsRequestError(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
})
expected := fmt.Errorf("Some simulated error")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: expected,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
_, err := sdk.fetchPullRequests("test-project")
assert.Equal(t, expected, err)
}
func TestFetchPullRequestsErrorsInResponse(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"errors":[{"msg":"Project 'test-project' not found"}]}`))
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
_, err := sdk.fetchPullRequests("test-project")
assert.Errorf(t, err, "Project 'test-project' not found", "Response error parsing broken")
}
func TestGetPullRequestSuccess(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
Template: "PR-%d",
}
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
actual, err := sdk.GetPullRequest("test-project", 1)
assert.Nil(t, err, "Successful data retrieval broken and throws error")
assert.IsType(t, &PullRequest{}, actual, "Happy path broken")
t.Cleanup(func() {
settings.Pattern = nil
assert.Errorf(t, err, "unexpected end of JSON input", "Undetected body unmarshal error")
})
}
func TestGetPullRequestFetchError(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
func TestFetchPullRequests(t *testing.T) {
t.Run("Success", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
actual, err := sdk.fetchPullRequests("test-project")
assert.Nil(t, err, "Successful data retrieval broken and throws error")
assert.IsType(t, &PullsResponse{}, actual, "Happy path broken")
})
expected := fmt.Errorf("Some simulated error")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: expected,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
_, err := sdk.GetPullRequest("test-project", 1)
t.Run("Building failure", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
})
expected := fmt.Errorf("Some simulated error")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return nil, expected
},
}
assert.Errorf(t, err, "fetching pull requests failed", "Incorrect edge case is throwing errors")
assert.Errorf(t, err, "Some simulated error", "Unexpected error cause")
}
_, err := sdk.fetchPullRequests("test-project")
func TestGetPullRequestUnknownPR(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
Template: "PR-%d",
}
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
assert.Equal(t, expected, err, "Unexpected error instance returned")
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
_, err := sdk.GetPullRequest("test-project", 1337)
t.Run("Internal error", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
})
expected := fmt.Errorf("Some simulated error")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: expected,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
assert.Errorf(t, err, "no pull request found with name 'PR-1337'")
_, err := sdk.fetchPullRequests("test-project")
t.Cleanup(func() {
settings.Pattern = nil
assert.Equal(t, expected, err)
})
t.Run("Errors in response", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"errors":[{"msg":"Project 'test-project' not found"}]}`))
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
_, err := sdk.fetchPullRequests("test-project")
assert.Errorf(t, err, "Project 'test-project' not found", "Response error parsing broken")
})
}
func TestGetMeasuresSuccess(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"component":{"key":"test-project","name":"Test Project","qualifier":"TRK","measures":[{"metric":"bugs","value":"0","bestValue":true}],"pullRequest":"PR-1"},"metrics":[{"key":"bugs","name":"Bugs","description":"Bugs","domain":"Reliability","type":"INT","higherValuesAreBetter":false,"qualitative":false,"hidden":false,"custom":false,"bestValue":"0"}]}`))
func TestGetPullRequest(t *testing.T) {
t.Run("Success", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
Template: "PR-%d",
}
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
actual, err := sdk.GetPullRequest("test-project", 1)
assert.Nil(t, err, "Successful data retrieval broken and throws error")
assert.IsType(t, &PullRequest{}, actual, "Happy path broken")
t.Cleanup(func() {
settings.Pattern = nil
})
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
actual, err := sdk.GetMeasures("test-project", "PR-1")
t.Run("Fetch error", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
})
expected := fmt.Errorf("Some simulated error")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: expected,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
assert.Nil(t, err, "Successful data retrieval broken and throws error")
assert.IsType(t, &MeasuresResponse{}, actual, "Happy path broken")
_, err := sdk.GetPullRequest("test-project", 1)
assert.Errorf(t, err, "fetching pull requests failed", "Incorrect edge case is throwing errors")
assert.Errorf(t, err, "Some simulated error", "Unexpected error cause")
})
t.Run("Unknown PR", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
Template: "PR-%d",
}
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullRequests":[{"key":"PR-1","title":"pr-branch","branch":"pr-branch","base":"main","status":{"qualityGateStatus":"OK","bugs":0,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2022-06-12T11:23:09+0000","target":"main"}]}`))
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
_, err := sdk.GetPullRequest("test-project", 1337)
assert.Errorf(t, err, "no pull request found with name 'PR-1337'")
t.Cleanup(func() {
settings.Pattern = nil
})
})
}
func TestGetMeasuresRequestBuildingFailure(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"component":{"key":"test-project","name":"Test Project","qualifier":"TRK","measures":[{"metric":"bugs","value":"0","bestValue":true}],"pullRequest":"PR-1"},"metrics":[{"key":"bugs","name":"Bugs","description":"Bugs","domain":"Reliability","type":"INT","higherValuesAreBetter":false,"qualitative":false,"hidden":false,"custom":false,"bestValue":"0"}]}`))
func TestGetMeasures(t *testing.T) {
t.Run("Success", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"component":{"key":"test-project","name":"Test Project","qualifier":"TRK","measures":[{"metric":"bugs","value":"0","bestValue":true}],"pullRequest":"PR-1"},"metrics":[{"key":"bugs","name":"Bugs","description":"Bugs","domain":"Reliability","type":"INT","higherValuesAreBetter":false,"qualitative":false,"hidden":false,"custom":false,"bestValue":"0"}]}`))
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
actual, err := sdk.GetMeasures("test-project", "PR-1")
assert.Nil(t, err, "Successful data retrieval broken and throws error")
assert.IsType(t, &MeasuresResponse{}, actual, "Happy path broken")
})
expected := fmt.Errorf("Some simulated error")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return nil, expected
},
}
_, err := sdk.GetMeasures("test-project", "PR-1")
t.Run("Building failure", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"component":{"key":"test-project","name":"Test Project","qualifier":"TRK","measures":[{"metric":"bugs","value":"0","bestValue":true}],"pullRequest":"PR-1"},"metrics":[{"key":"bugs","name":"Bugs","description":"Bugs","domain":"Reliability","type":"INT","higherValuesAreBetter":false,"qualitative":false,"hidden":false,"custom":false,"bestValue":"0"}]}`))
})
expected := fmt.Errorf("Some simulated error")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return nil, expected
},
}
assert.Equal(t, expected, err, "Unexpected error instance returned")
_, err := sdk.GetMeasures("test-project", "PR-1")
assert.Equal(t, expected, err, "Unexpected error instance returned")
})
t.Run("Request error", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"component":{"key":"test-project","name":"Test Project","qualifier":"TRK","measures":[{"metric":"bugs","value":"0","bestValue":true}],"pullRequest":"PR-1"},"metrics":[{"key":"bugs","name":"Bugs","description":"Bugs","domain":"Reliability","type":"INT","higherValuesAreBetter":false,"qualitative":false,"hidden":false,"custom":false,"bestValue":"0"}]}`))
})
expected := fmt.Errorf("Some simulated error")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: expected,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
_, err := sdk.GetMeasures("test-project", "PR-1")
assert.Equal(t, expected, err)
})
t.Run("Errors in response", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"errors":[{"msg":"Component 'non-existing-project' of pull request 'PR-1' not found"}]}`))
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
_, err := sdk.GetMeasures("non-existing-project", "PR-1")
assert.Errorf(t, err, "Component 'non-existing-project' of pull request 'PR-1' not found", "Response error parsing broken")
})
}
func TestGetMeasuresRequestError(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"component":{"key":"test-project","name":"Test Project","qualifier":"TRK","measures":[{"metric":"bugs","value":"0","bestValue":true}],"pullRequest":"PR-1"},"metrics":[{"key":"bugs","name":"Bugs","description":"Bugs","domain":"Reliability","type":"INT","higherValuesAreBetter":false,"qualitative":false,"hidden":false,"custom":false,"bestValue":"0"}]}`))
})
expected := fmt.Errorf("Some simulated error")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: expected,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
func TestComposeGiteaComment(t *testing.T) {
t.Run("Success", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"component":{"key":"test-project","name":"Test Project","qualifier":"TRK","measures":[{"metric":"bugs","value":"10","bestValue":false}],"pullRequest":"PR-1"},"metrics":[{"key":"bugs","name":"Bugs","description":"Bugs","domain":"Reliability","type":"INT","higherValuesAreBetter":false,"qualitative":false,"hidden":false,"custom":false,"bestValue":"0"}]}`))
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
_, err := sdk.GetMeasures("test-project", "PR-1")
actual, err := sdk.ComposeGiteaComment(&CommentComposeData{
Key: "test-project",
PRName: "PR-1",
Url: "https://sonarqube.example.com",
QualityGate: "OK",
})
assert.Equal(t, expected, err)
}
func TestGetMeasuresErrorsInResponse(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"errors":[{"msg":"Component 'non-existing-project' of pull request 'PR-1' not found"}]}`))
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
_, err := sdk.GetMeasures("non-existing-project", "PR-1")
assert.Errorf(t, err, "Component 'non-existing-project' of pull request 'PR-1' not found", "Response error parsing broken")
}
func TestComposeGiteaCommentSuccess(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"component":{"key":"test-project","name":"Test Project","qualifier":"TRK","measures":[{"metric":"bugs","value":"10","bestValue":false}],"pullRequest":"PR-1"},"metrics":[{"key":"bugs","name":"Bugs","description":"Bugs","domain":"Reliability","type":"INT","higherValuesAreBetter":false,"qualitative":false,"hidden":false,"custom":false,"bestValue":"0"}]}`))
})
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return httptest.NewRequest(method, target, body), nil
},
}
actual, err := sdk.ComposeGiteaComment(&CommentComposeData{
Key: "test-project",
PRName: "PR-1",
Url: "https://sonarqube.example.com",
QualityGate: "OK",
assert.Nil(t, err, "Successful comment composing throwing errors")
assert.Contains(t, actual, ":white_check_mark:", "Happy path [Quality Gate] broken")
assert.Contains(t, actual, "| Metric | Current |", "Happy path [Metrics Header] broken")
assert.Contains(t, actual, "| Bugs | 10 |", "Happy path [Metrics Values] broken")
assert.Contains(t, actual, "https://sonarqube.example.com", "Happy path [Link] broken")
assert.Contains(t, actual, "/sq-bot review", "Happy path [Command] broken")
})
assert.Nil(t, err, "Successful comment composing throwing errors")
assert.Contains(t, actual, ":white_check_mark:", "Happy path [Quality Gate] broken")
assert.Contains(t, actual, "| Metric | Current |", "Happy path [Metrics Header] broken")
assert.Contains(t, actual, "| Bugs | 10 |", "Happy path [Metrics Values] broken")
assert.Contains(t, actual, "https://sonarqube.example.com", "Happy path [Link] broken")
assert.Contains(t, actual, "/sq-bot review", "Happy path [Command] broken")
}
t.Run("Error", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"component":{"key":"test-project","name":"Test Project","qualifier":"TRK","measures":[{"metric":"bugs","value":"10","bestValue":false}],"pullRequest":"PR-1"},"metrics":[{"key":"bugs","name":"Bugs","description":"Bugs","domain":"Reliability","type":"INT","higherValuesAreBetter":false,"qualitative":false,"hidden":false,"custom":false,"bestValue":"0"}]}`))
})
expected := fmt.Errorf("Expected error from GetMeasures")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return nil, expected
},
}
func TestComposeGiteaCommentError(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"component":{"key":"test-project","name":"Test Project","qualifier":"TRK","measures":[{"metric":"bugs","value":"10","bestValue":false}],"pullRequest":"PR-1"},"metrics":[{"key":"bugs","name":"Bugs","description":"Bugs","domain":"Reliability","type":"INT","higherValuesAreBetter":false,"qualitative":false,"hidden":false,"custom":false,"bestValue":"0"}]}`))
_, err := sdk.ComposeGiteaComment(&CommentComposeData{
Key: "test-project",
PRName: "PR-1",
Url: "https://sonarqube.example.com",
QualityGate: "OK",
})
assert.Errorf(t, err, expected.Error(), "Undetected error while composing comment")
})
expected := fmt.Errorf("Expected error from GetMeasures")
sdk := &SonarQubeSdk{
token: "test-token",
client: &ClientMock{
handler: handler,
recoder: httptest.NewRecorder(),
responseError: nil,
},
bodyReader: io.ReadAll,
httpRequest: func(method, target string, body io.Reader) (*http.Request, error) {
return nil, expected
},
}
_, err := sdk.ComposeGiteaComment(&CommentComposeData{
Key: "test-project",
PRName: "PR-1",
Url: "https://sonarqube.example.com",
QualityGate: "OK",
})
assert.Errorf(t, err, expected.Error(), "Undetected error while composing comment")
}
func TestNew(t *testing.T) {

View file

@ -10,8 +10,9 @@ import (
"github.com/stretchr/testify/assert"
)
var defaultConfig []byte = []byte(
`gitea:
func defaultConfig() []byte {
return []byte(
`gitea:
url: https://example.com/gitea
token:
value: d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565
@ -34,6 +35,7 @@ namingPattern:
regex: "^PR-(\\d+)$"
template: "PR-%d"
`)
}
func WriteConfigFile(t *testing.T, content []byte) string {
dir := os.TempDir()
@ -48,78 +50,157 @@ func WriteConfigFile(t *testing.T, content []byte) string {
return config
}
func TestLoadWithMissingFile(t *testing.T) {
assert.Panics(t, func() { Load(path.Join(os.TempDir(), "config.yaml")) }, "No panic while reading missing file")
}
func TestLoad(t *testing.T) {
t.Run("Missing file", func(t *testing.T) {
assert.Panics(t, func() { Load(path.Join(os.TempDir(), "config.yaml")) }, "No panic while reading missing file")
})
func TestLoadWithExistingFile(t *testing.T) {
c := WriteConfigFile(t, defaultConfig)
t.Run("Existing file", func(t *testing.T) {
c := WriteConfigFile(t, defaultConfig())
assert.NotPanics(t, func() { Load(c) }, "Unexpected panic while reading existing file")
})
assert.NotPanics(t, func() { Load(c) }, "Unexpected panic while reading existing file")
}
t.Run("File references", func(t *testing.T) {
giteaWebhookSecretFile := path.Join(os.TempDir(), "webhook-secret-gitea")
_ = ioutil.WriteFile(giteaWebhookSecretFile, []byte(`gitea-totally-secret`), 0444)
func TestLoadGiteaStructure(t *testing.T) {
c := WriteConfigFile(t, defaultConfig)
Load(c)
giteaTokenFile := path.Join(os.TempDir(), "token-secret-gitea")
_ = ioutil.WriteFile(giteaTokenFile, []byte(`d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565`), 0444)
expected := GiteaConfig{
Url: "https://example.com/gitea",
Token: &Token{
Value: "d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565",
},
Webhook: &Webhook{
Secret: "haxxor-gitea-secret",
},
}
sonarqubeWebhookSecretFile := path.Join(os.TempDir(), "webhook-secret-sonarqube")
_ = ioutil.WriteFile(sonarqubeWebhookSecretFile, []byte(`sonarqube-totally-secret`), 0444)
assert.EqualValues(t, expected, Gitea)
}
sonarqubeTokenFile := path.Join(os.TempDir(), "token-secret-sonarqube")
_ = ioutil.WriteFile(sonarqubeTokenFile, []byte(`a09eb5785b25bb2cbacf48808a677a0709f02d8e`), 0444)
func TestLoadGiteaStructureInjectedEnvs(t *testing.T) {
os.Setenv("PRBOT_GITEA_WEBHOOK_SECRET", "injected-webhook-secret")
os.Setenv("PRBOT_GITEA_TOKEN_VALUE", "injected-token")
c := WriteConfigFile(t, defaultConfig)
Load(c)
c := WriteConfigFile(t, []byte(
`gitea:
url: https://example.com/gitea
token:
value: fake-gitea-token
sonarqube:
url: https://example.com/sonarqube
token:
value: fake-sonarqube-token
projects:
- sonarqube:
key: gitea-sonarqube-bot
gitea:
owner: example-organization
name: pr-bot
`))
os.Setenv("PRBOT_GITEA_WEBHOOK_SECRETFILE", giteaWebhookSecretFile)
os.Setenv("PRBOT_GITEA_TOKEN_FILE", giteaTokenFile)
os.Setenv("PRBOT_SONARQUBE_WEBHOOK_SECRETFILE", sonarqubeWebhookSecretFile)
os.Setenv("PRBOT_SONARQUBE_TOKEN_FILE", sonarqubeTokenFile)
expected := GiteaConfig{
Url: "https://example.com/gitea",
Token: &Token{
Value: "injected-token",
},
Webhook: &Webhook{
Secret: "injected-webhook-secret",
},
}
expectedGitea := GiteaConfig{
Url: "https://example.com/gitea",
Token: &Token{
Value: "d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565",
file: giteaTokenFile,
},
Webhook: &Webhook{
Secret: "gitea-totally-secret",
secretFile: giteaWebhookSecretFile,
},
}
assert.EqualValues(t, expected, Gitea)
expectedSonarQube := SonarQubeConfig{
Url: "https://example.com/sonarqube",
Token: &Token{
Value: "a09eb5785b25bb2cbacf48808a677a0709f02d8e",
file: sonarqubeTokenFile,
},
Webhook: &Webhook{
Secret: "sonarqube-totally-secret",
secretFile: sonarqubeWebhookSecretFile,
},
AdditionalMetrics: []string{},
}
t.Cleanup(func() {
os.Unsetenv("PRBOT_GITEA_WEBHOOK_SECRET")
os.Unsetenv("PRBOT_GITEA_TOKEN_VALUE")
Load(c)
assert.EqualValues(t, expectedGitea, Gitea)
assert.EqualValues(t, expectedSonarQube, SonarQube)
t.Cleanup(func() {
os.Remove(giteaWebhookSecretFile)
os.Remove(giteaTokenFile)
os.Remove(sonarqubeWebhookSecretFile)
os.Remove(sonarqubeTokenFile)
os.Unsetenv("PRBOT_GITEA_WEBHOOK_SECRETFILE")
os.Unsetenv("PRBOT_GITEA_TOKEN_FILE")
os.Unsetenv("PRBOT_SONARQUBE_WEBHOOK_SECRETFILE")
os.Unsetenv("PRBOT_SONARQUBE_TOKEN_FILE")
})
})
}
func TestLoadSonarQubeStructure(t *testing.T) {
c := WriteConfigFile(t, defaultConfig)
Load(c)
func TestLoadGitea(t *testing.T) {
t.Run("Default", func(t *testing.T) {
c := WriteConfigFile(t, defaultConfig())
Load(c)
expected := SonarQubeConfig{
Url: "https://example.com/sonarqube",
Token: &Token{
Value: "a09eb5785b25bb2cbacf48808a677a0709f02d8e",
},
Webhook: &Webhook{
Secret: "haxxor-sonarqube-secret",
},
}
expected := GiteaConfig{
Url: "https://example.com/gitea",
Token: &Token{
Value: "d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565",
},
Webhook: &Webhook{
Secret: "haxxor-gitea-secret",
},
}
assert.EqualValues(t, expected, SonarQube)
assert.EqualValues(t, expected.GetMetricsList(), "bugs,vulnerabilities,code_smells")
assert.EqualValues(t, expected, Gitea)
})
t.Run("Injected envs", func(t *testing.T) {
os.Setenv("PRBOT_GITEA_WEBHOOK_SECRET", "injected-webhook-secret")
os.Setenv("PRBOT_GITEA_TOKEN_VALUE", "injected-token")
c := WriteConfigFile(t, defaultConfig())
Load(c)
expected := GiteaConfig{
Url: "https://example.com/gitea",
Token: &Token{
Value: "injected-token",
},
Webhook: &Webhook{
Secret: "injected-webhook-secret",
},
}
assert.EqualValues(t, expected, Gitea)
t.Cleanup(func() {
os.Unsetenv("PRBOT_GITEA_WEBHOOK_SECRET")
os.Unsetenv("PRBOT_GITEA_TOKEN_VALUE")
})
})
}
func TestLoadSonarQubeStructureWithAdditionalMetrics(t *testing.T) {
c := WriteConfigFile(t, []byte(
`gitea:
func TestLoadSonarQube(t *testing.T) {
t.Run("Default", func(t *testing.T) {
c := WriteConfigFile(t, defaultConfig())
Load(c)
expected := SonarQubeConfig{
Url: "https://example.com/sonarqube",
Token: &Token{
Value: "a09eb5785b25bb2cbacf48808a677a0709f02d8e",
},
Webhook: &Webhook{
Secret: "haxxor-sonarqube-secret",
},
}
assert.EqualValues(t, expected, SonarQube)
assert.EqualValues(t, expected.GetMetricsList(), "bugs,vulnerabilities,code_smells")
})
t.Run("Additional metrics", func(t *testing.T) {
c := WriteConfigFile(t, []byte(
`gitea:
url: https://example.com/gitea
token:
value: fake-gitea-token
@ -135,147 +216,74 @@ projects:
owner: example-organization
name: pr-bot
`))
Load(c)
Load(c)
expected := SonarQubeConfig{
Url: "https://example.com/sonarqube",
Token: &Token{
Value: "fake-sonarqube-token",
},
Webhook: &Webhook{
Secret: "",
},
AdditionalMetrics: []string{
"new_security_hotspots",
},
}
expected := SonarQubeConfig{
Url: "https://example.com/sonarqube",
Token: &Token{
Value: "fake-sonarqube-token",
},
Webhook: &Webhook{
Secret: "",
},
AdditionalMetrics: []string{
"new_security_hotspots",
},
}
assert.EqualValues(t, expected, SonarQube)
assert.EqualValues(t, expected.AdditionalMetrics, []string{"new_security_hotspots"})
assert.EqualValues(t, "bugs,vulnerabilities,code_smells,new_security_hotspots", SonarQube.GetMetricsList())
}
assert.EqualValues(t, expected, SonarQube)
assert.EqualValues(t, expected.AdditionalMetrics, []string{"new_security_hotspots"})
assert.EqualValues(t, "bugs,vulnerabilities,code_smells,new_security_hotspots", SonarQube.GetMetricsList())
})
func TestLoadSonarQubeStructureInjectedEnvs(t *testing.T) {
os.Setenv("PRBOT_SONARQUBE_WEBHOOK_SECRET", "injected-webhook-secret")
os.Setenv("PRBOT_SONARQUBE_TOKEN_VALUE", "injected-token")
c := WriteConfigFile(t, defaultConfig)
Load(c)
t.Run("Injected envs", func(t *testing.T) {
os.Setenv("PRBOT_SONARQUBE_WEBHOOK_SECRET", "injected-webhook-secret")
os.Setenv("PRBOT_SONARQUBE_TOKEN_VALUE", "injected-token")
c := WriteConfigFile(t, defaultConfig())
Load(c)
expected := SonarQubeConfig{
Url: "https://example.com/sonarqube",
Token: &Token{
Value: "injected-token",
},
Webhook: &Webhook{
Secret: "injected-webhook-secret",
},
}
expected := SonarQubeConfig{
Url: "https://example.com/sonarqube",
Token: &Token{
Value: "injected-token",
},
Webhook: &Webhook{
Secret: "injected-webhook-secret",
},
}
assert.EqualValues(t, expected, SonarQube)
assert.EqualValues(t, expected, SonarQube)
t.Cleanup(func() {
os.Unsetenv("PRBOT_SONARQUBE_WEBHOOK_SECRET")
os.Unsetenv("PRBOT_SONARQUBE_TOKEN_VALUE")
t.Cleanup(func() {
os.Unsetenv("PRBOT_SONARQUBE_WEBHOOK_SECRET")
os.Unsetenv("PRBOT_SONARQUBE_TOKEN_VALUE")
})
})
}
func TestLoadStructureWithFileReferenceResolving(t *testing.T) {
giteaWebhookSecretFile := path.Join(os.TempDir(), "webhook-secret-gitea")
_ = ioutil.WriteFile(giteaWebhookSecretFile, []byte(`gitea-totally-secret`), 0444)
func TestLoadProjects(t *testing.T) {
t.Run("Default", func(t *testing.T) {
c := WriteConfigFile(t, defaultConfig())
Load(c)
giteaTokenFile := path.Join(os.TempDir(), "token-secret-gitea")
_ = ioutil.WriteFile(giteaTokenFile, []byte(`d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565`), 0444)
expectedProjects := []Project{
{
SonarQube: struct{ Key string }{
Key: "gitea-sonarqube-bot",
},
Gitea: GiteaRepository{
Owner: "example-organization",
Name: "pr-bot",
},
},
}
sonarqubeWebhookSecretFile := path.Join(os.TempDir(), "webhook-secret-sonarqube")
_ = ioutil.WriteFile(sonarqubeWebhookSecretFile, []byte(`sonarqube-totally-secret`), 0444)
sonarqubeTokenFile := path.Join(os.TempDir(), "token-secret-sonarqube")
_ = ioutil.WriteFile(sonarqubeTokenFile, []byte(`a09eb5785b25bb2cbacf48808a677a0709f02d8e`), 0444)
c := WriteConfigFile(t, []byte(
`gitea:
url: https://example.com/gitea
token:
value: fake-gitea-token
sonarqube:
url: https://example.com/sonarqube
token:
value: fake-sonarqube-token
projects:
- sonarqube:
key: gitea-sonarqube-bot
gitea:
owner: example-organization
name: pr-bot
`))
os.Setenv("PRBOT_GITEA_WEBHOOK_SECRETFILE", giteaWebhookSecretFile)
os.Setenv("PRBOT_GITEA_TOKEN_FILE", giteaTokenFile)
os.Setenv("PRBOT_SONARQUBE_WEBHOOK_SECRETFILE", sonarqubeWebhookSecretFile)
os.Setenv("PRBOT_SONARQUBE_TOKEN_FILE", sonarqubeTokenFile)
expectedGitea := GiteaConfig{
Url: "https://example.com/gitea",
Token: &Token{
Value: "d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565",
file: giteaTokenFile,
},
Webhook: &Webhook{
Secret: "gitea-totally-secret",
secretFile: giteaWebhookSecretFile,
},
}
expectedSonarQube := SonarQubeConfig{
Url: "https://example.com/sonarqube",
Token: &Token{
Value: "a09eb5785b25bb2cbacf48808a677a0709f02d8e",
file: sonarqubeTokenFile,
},
Webhook: &Webhook{
Secret: "sonarqube-totally-secret",
secretFile: sonarqubeWebhookSecretFile,
},
AdditionalMetrics: []string{},
}
Load(c)
assert.EqualValues(t, expectedGitea, Gitea)
assert.EqualValues(t, expectedSonarQube, SonarQube)
t.Cleanup(func() {
os.Remove(giteaWebhookSecretFile)
os.Remove(giteaTokenFile)
os.Remove(sonarqubeWebhookSecretFile)
os.Remove(sonarqubeTokenFile)
os.Unsetenv("PRBOT_GITEA_WEBHOOK_SECRETFILE")
os.Unsetenv("PRBOT_GITEA_TOKEN_FILE")
os.Unsetenv("PRBOT_SONARQUBE_WEBHOOK_SECRETFILE")
os.Unsetenv("PRBOT_SONARQUBE_TOKEN_FILE")
assert.EqualValues(t, expectedProjects, Projects)
})
}
func TestLoadProjectsStructure(t *testing.T) {
c := WriteConfigFile(t, defaultConfig)
Load(c)
expectedProjects := []Project{
{
SonarQube: struct{ Key string }{
Key: "gitea-sonarqube-bot",
},
Gitea: GiteaRepository{
Owner: "example-organization",
Name: "pr-bot",
},
},
}
assert.EqualValues(t, expectedProjects, Projects)
}
func TestLoadProjectsStructureWithNoMapping(t *testing.T) {
invalidConfig := []byte(
`gitea:
t.Run("Empty mapping", func(t *testing.T) {
invalidConfig := []byte(
`gitea:
url: https://example.com/gitea
token:
value: d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565
@ -289,26 +297,28 @@ sonarqube:
secret: haxxor-sonarqube-secret
projects: []
`)
c := WriteConfigFile(t, invalidConfig)
c := WriteConfigFile(t, invalidConfig)
assert.Panics(t, func() { Load(c) }, "No panic for empty project mapping that is required")
assert.Panics(t, func() { Load(c) }, "No panic for empty project mapping that is required")
})
}
func TestLoadNamingPatternStructure(t *testing.T) {
c := WriteConfigFile(t, defaultConfig)
Load(c)
func TestLoadNamingPattern(t *testing.T) {
t.Run("Default", func(t *testing.T) {
c := WriteConfigFile(t, defaultConfig())
Load(c)
expected := &PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
Template: "PR-%d",
}
expected := &PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
Template: "PR-%d",
}
assert.EqualValues(t, expected, Pattern)
}
assert.EqualValues(t, expected, Pattern)
})
func TestLoadNamingPatternStructureWithInternalDefaults(t *testing.T) {
c := WriteConfigFile(t, []byte(
`gitea:
t.Run("Internal defaults", func(t *testing.T) {
c := WriteConfigFile(t, []byte(
`gitea:
url: https://example.com/gitea
token:
value: fake-gitea-token
@ -324,48 +334,49 @@ projects:
owner: example-organization
name: pr-bot
`))
Load(c)
Load(c)
expected := &PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
Template: "PR-%d",
}
expected := &PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
Template: "PR-%d",
}
assert.EqualValues(t, expected, Pattern)
}
assert.EqualValues(t, expected, Pattern)
})
func TestLoadNamingPatternStructureInjectedEnvs(t *testing.T) {
os.Setenv("PRBOT_NAMINGPATTERN_REGEX", "test-(\\d+)-pullrequest")
os.Setenv("PRBOT_NAMINGPATTERN_TEMPLATE", "test-%d-pullrequest")
c := WriteConfigFile(t, defaultConfig)
Load(c)
t.Run("Injected envs", func(t *testing.T) {
os.Setenv("PRBOT_NAMINGPATTERN_REGEX", "test-(\\d+)-pullrequest")
os.Setenv("PRBOT_NAMINGPATTERN_TEMPLATE", "test-%d-pullrequest")
c := WriteConfigFile(t, defaultConfig())
Load(c)
expected := &PatternConfig{
RegExp: regexp.MustCompile(`test-(\d+)-pullrequest`),
Template: "test-%d-pullrequest",
}
expected := &PatternConfig{
RegExp: regexp.MustCompile(`test-(\d+)-pullrequest`),
Template: "test-%d-pullrequest",
}
assert.EqualValues(t, expected, Pattern)
assert.EqualValues(t, expected, Pattern)
t.Cleanup(func() {
os.Unsetenv("PRBOT_NAMINGPATTERN_REGEX")
os.Unsetenv("PRBOT_NAMINGPATTERN_TEMPLATE")
})
}
func TestLoadNamingPatternStructureMixedInput(t *testing.T) {
os.Setenv("PRBOT_NAMINGPATTERN_REGEX", "test-(\\d+)-pullrequest")
c := WriteConfigFile(t, defaultConfig)
Load(c)
expected := &PatternConfig{
RegExp: regexp.MustCompile(`test-(\d+)-pullrequest`),
Template: "PR-%d",
}
assert.EqualValues(t, expected, Pattern)
t.Cleanup(func() {
os.Unsetenv("PRBOT_NAMINGPATTERN_REGEX")
t.Cleanup(func() {
os.Unsetenv("PRBOT_NAMINGPATTERN_REGEX")
os.Unsetenv("PRBOT_NAMINGPATTERN_TEMPLATE")
})
})
t.Run("Mixed input", func(t *testing.T) {
os.Setenv("PRBOT_NAMINGPATTERN_REGEX", "test-(\\d+)-pullrequest")
c := WriteConfigFile(t, defaultConfig())
Load(c)
expected := &PatternConfig{
RegExp: regexp.MustCompile(`test-(\d+)-pullrequest`),
Template: "PR-%d",
}
assert.EqualValues(t, expected, Pattern)
t.Cleanup(func() {
os.Unsetenv("PRBOT_NAMINGPATTERN_REGEX")
})
})
}

View file

@ -10,47 +10,49 @@ import (
)
func TestNewWebhook(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
t.Run("Success", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
raw := []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": { "sonar.analysis.sqbot": "a84442009c09b1adc278b6bb80a3853419f54007" } }`)
response, ok := New(raw)
raw := []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": { "sonar.analysis.sqbot": "a84442009c09b1adc278b6bb80a3853419f54007" } }`)
response, ok := New(raw)
assert.NotNil(t, response)
assert.Equal(t, 1337, response.PRIndex)
assert.Equal(t, "a84442009c09b1adc278b6bb80a3853419f54007", response.Properties.OriginalCommit)
assert.True(t, ok)
assert.NotNil(t, response)
assert.Equal(t, 1337, response.PRIndex)
assert.Equal(t, "a84442009c09b1adc278b6bb80a3853419f54007", response.Properties.OriginalCommit)
assert.True(t, ok)
t.Cleanup(func() {
settings.Pattern = nil
t.Cleanup(func() {
settings.Pattern = nil
})
})
}
func TestNewWebhookInvalidJSON(t *testing.T) {
raw := []byte(`{ "serverUrl": ["invalid-server-url-content"] }`)
_, ok := New(raw)
t.Run("Invalid JSON", func(t *testing.T) {
raw := []byte(`{ "serverUrl": ["invalid-server-url-content"] }`)
_, ok := New(raw)
assert.False(t, ok)
}
assert.False(t, ok)
})
func TestNewWebhookInvalidBranchName(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
t.Run("Invalid branch name", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
raw := []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": "invalid", "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": {} }`)
_, ok := New(raw)
raw := []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": "invalid", "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": {} }`)
_, ok := New(raw)
assert.False(t, ok)
assert.False(t, ok)
t.Cleanup(func() {
settings.Pattern = nil
t.Cleanup(func() {
settings.Pattern = nil
})
})
}
func TestWebhookGetRevision(t *testing.T) {
t.Run("Default revision", func(t *testing.T) {
t.Run("Default", func(t *testing.T) {
w := Webhook{
Revision: "225fa0306c0ab83297d0cb5db0717b194ccb2e76",
}
@ -58,7 +60,7 @@ func TestWebhookGetRevision(t *testing.T) {
assert.Equal(t, w.Revision, w.GetRevision())
})
t.Run("Default revision due to incomplete properties", func(t *testing.T) {
t.Run("Incomplete properties", func(t *testing.T) {
w := Webhook{
Revision: "225fa0306c0ab83297d0cb5db0717b194ccb2e76",
Properties: &properties{},