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" "github.com/stretchr/testify/assert"
) )
func TestIsValidBotCommentForInvalidComment(t *testing.T) { func TestIsValidBotComment(t *testing.T) {
t.Run("Valid", func(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(""), "Undetected missing action prefix")
assert.False(t, IsValidBotComment("/sq-bot invalid-command"), "Undetected invalid bot command") 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") assert.False(t, IsValidBotComment("Some context with /sq-bot review within"), "Incorrect bot prefix detected inside random comment")
} })
func TestIsValidBotCommentForValidComment(t *testing.T) {
assert.True(t, IsValidBotComment("/sq-bot review"), "Correct bot comment not recognized")
} }

File diff suppressed because one or more lines are too long

View file

@ -93,20 +93,27 @@ func TestMain(m *testing.M) {
} }
func TestNonAPIRoutes(t *testing.T) { func TestNonAPIRoutes(t *testing.T) {
t.Run("favicon", func(t *testing.T) {
router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock)) router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock))
w := httptest.NewRecorder() w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/favicon.ico", nil) req, _ := http.NewRequest("GET", "/favicon.ico", nil)
router.Engine.ServeHTTP(w, req) router.Engine.ServeHTTP(w, req)
assert.Equal(t, http.StatusNoContent, w.Code) assert.Equal(t, http.StatusNoContent, w.Code)
})
w = httptest.NewRecorder() t.Run("ping", func(t *testing.T) {
req, _ = http.NewRequest("GET", "/ping", nil) router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock))
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/ping", nil)
router.Engine.ServeHTTP(w, req) router.Engine.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
})
} }
func TestSonarQubeAPIRouteMissingProjectHeader(t *testing.T) { func TestSonarQubeAPIRoute(t *testing.T) {
t.Run("Missing project header", func(t *testing.T) {
router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock)) router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock))
w := httptest.NewRecorder() w := httptest.NewRecorder()
@ -114,9 +121,9 @@ func TestSonarQubeAPIRouteMissingProjectHeader(t *testing.T) {
router.Engine.ServeHTTP(w, req) router.Engine.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code) assert.Equal(t, http.StatusNotFound, w.Code)
} })
func TestSonarQubeAPIRouteProcessing(t *testing.T) { t.Run("Processing", func(t *testing.T) {
sonarQubeHandlerMock := new(SonarQubeHandlerMock) sonarQubeHandlerMock := new(SonarQubeHandlerMock)
sonarQubeHandlerMock.On("Handle", mock.IsType(&http.Request{})) sonarQubeHandlerMock.On("Handle", mock.IsType(&http.Request{}))
@ -130,9 +137,11 @@ func TestSonarQubeAPIRouteProcessing(t *testing.T) {
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
sonarQubeHandlerMock.AssertNumberOfCalls(t, "Handle", 1) sonarQubeHandlerMock.AssertNumberOfCalls(t, "Handle", 1)
sonarQubeHandlerMock.AssertExpectations(t) sonarQubeHandlerMock.AssertExpectations(t)
})
} }
func TestGiteaAPIRouteMissingEventHeader(t *testing.T) { func TestGiteaAPIRoute(t *testing.T) {
t.Run("Missing event header", func(t *testing.T) {
router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock)) router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock))
w := httptest.NewRecorder() w := httptest.NewRecorder()
@ -140,9 +149,9 @@ func TestGiteaAPIRouteMissingEventHeader(t *testing.T) {
router.Engine.ServeHTTP(w, req) router.Engine.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code) assert.Equal(t, http.StatusNotFound, w.Code)
} })
func TestGiteaAPIRouteSynchronizeProcessing(t *testing.T) { t.Run("Processing synchronize", func(t *testing.T) {
giteaHandlerMock := new(GiteaHandlerMock) giteaHandlerMock := new(GiteaHandlerMock)
giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Return(nil) giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Return(nil)
giteaHandlerMock.On("HandleComment", mock.Anything, mock.Anything).Maybe() giteaHandlerMock.On("HandleComment", mock.Anything, mock.Anything).Maybe()
@ -158,9 +167,9 @@ func TestGiteaAPIRouteSynchronizeProcessing(t *testing.T) {
giteaHandlerMock.AssertNumberOfCalls(t, "HandleSynchronize", 1) giteaHandlerMock.AssertNumberOfCalls(t, "HandleSynchronize", 1)
giteaHandlerMock.AssertNumberOfCalls(t, "HandleComment", 0) giteaHandlerMock.AssertNumberOfCalls(t, "HandleComment", 0)
giteaHandlerMock.AssertExpectations(t) giteaHandlerMock.AssertExpectations(t)
} })
func TestGiteaAPIRouteCommentProcessing(t *testing.T) { t.Run("Processing comment", func(t *testing.T) {
giteaHandlerMock := new(GiteaHandlerMock) giteaHandlerMock := new(GiteaHandlerMock)
giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Maybe() giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Maybe()
giteaHandlerMock.On("HandleComment", mock.Anything, mock.Anything).Return(nil) giteaHandlerMock.On("HandleComment", mock.Anything, mock.Anything).Return(nil)
@ -176,9 +185,9 @@ func TestGiteaAPIRouteCommentProcessing(t *testing.T) {
giteaHandlerMock.AssertNumberOfCalls(t, "HandleSynchronize", 0) giteaHandlerMock.AssertNumberOfCalls(t, "HandleSynchronize", 0)
giteaHandlerMock.AssertNumberOfCalls(t, "HandleComment", 1) giteaHandlerMock.AssertNumberOfCalls(t, "HandleComment", 1)
giteaHandlerMock.AssertExpectations(t) giteaHandlerMock.AssertExpectations(t)
} })
func TestGiteaAPIRouteUnknownEvent(t *testing.T) { t.Run("Unknown event", func(t *testing.T) {
giteaHandlerMock := new(GiteaHandlerMock) giteaHandlerMock := new(GiteaHandlerMock)
giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Maybe() giteaHandlerMock.On("HandleSynchronize", mock.Anything, mock.Anything).Maybe()
giteaHandlerMock.On("HandleComment", mock.Anything, mock.Anything).Maybe() giteaHandlerMock.On("HandleComment", mock.Anything, mock.Anything).Maybe()
@ -194,4 +203,5 @@ func TestGiteaAPIRouteUnknownEvent(t *testing.T) {
giteaHandlerMock.AssertNumberOfCalls(t, "HandleSynchronize", 0) giteaHandlerMock.AssertNumberOfCalls(t, "HandleSynchronize", 0)
giteaHandlerMock.AssertNumberOfCalls(t, "HandleComment", 0) giteaHandlerMock.AssertNumberOfCalls(t, "HandleComment", 0)
giteaHandlerMock.AssertExpectations(t) 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"}}`) 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) { func TestIsValidWebhook(t *testing.T) {
t.Run("Success", func(t *testing.T) {
actual, _ := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "647f2395d30b1b7efcb58d9338be5b69c2addb54faf6bde6314a57ea28f45467", "test-component") actual, _ := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "647f2395d30b1b7efcb58d9338be5b69c2addb54faf6bde6314a57ea28f45467", "test-component")
assert.True(t, actual, "Expected successful webhook signature validation") assert.True(t, actual, "Expected successful webhook signature validation")
} })
func TestIsValidWebhookNothingConfiguredOrProvidedSuccess(t *testing.T) { t.Run("Nothing configured or provided", func(t *testing.T) {
actual, _ := isValidWebhook(getRequestData(), "", "", "test-component") actual, _ := isValidWebhook(getRequestData(), "", "", "test-component")
assert.True(t, actual, "Webhook signature validation not skipped") assert.True(t, actual, "Webhook signature validation not skipped")
} })
func TestIsValidWebhookSignatureDecodingFailure(t *testing.T) { t.Run("Signature decoding error", func(t *testing.T) {
actual, err := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "invalid-signature", "test-component") actual, err := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "invalid-signature", "test-component")
assert.False(t, actual) assert.False(t, actual)
assert.EqualError(t, err, "Error decoding signature for test-component webhook.", "Undetected signature encoding error") assert.EqualError(t, err, "Error decoding signature for test-component webhook.", "Undetected signature encoding error")
} })
func TestIsValidWebhookSignatureMismatchFailure(t *testing.T) { t.Run("Signature mismatch", func(t *testing.T) {
actual, err := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "fde6a666b7a1a46c27efb1961c17b46b6cf7aa13db5560e5ac95e801a18a92f3", "test-component") actual, err := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "fde6a666b7a1a46c27efb1961c17b46b6cf7aa13db5560e5ac95e801a18a92f3", "test-component")
assert.False(t, actual) 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 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)") })
}
func TestIsValidWebhookEmptySecretConfigurationFailure(t *testing.T) { t.Run("Empty secret configuration", func(t *testing.T) {
actual, err := isValidWebhook(getRequestData(), "", "647f2395d30b1b7efcb58d9338be5b69c2addb54faf6bde6314a57ea28f45467", "test-component") actual, err := isValidWebhook(getRequestData(), "", "647f2395d30b1b7efcb58d9338be5b69c2addb54faf6bde6314a57ea28f45467", "test-component")
assert.False(t, actual) 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)") 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) { t.Run("Empty signature configuration", func(t *testing.T) {
actual, err := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "", "test-component") actual, err := isValidWebhook(getRequestData(), "sonarqube-test-webhook-secret", "", "test-component")
assert.False(t, actual) 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)") 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,7 +34,8 @@ func withValidSonarQubeRequestData(t *testing.T, jsonBody []byte) (*http.Request
return req, rr, handler return req, rr, handler
} }
func TestHandleSonarQubeWebhookProjectMapped(t *testing.T) { func TestHandleSonarQubeWebhook(t *testing.T) {
t.Run("With mapped Project", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{ settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`), RegExp: regexp.MustCompile(`^PR-(\d+)$`),
} }
@ -59,9 +60,9 @@ func TestHandleSonarQubeWebhookProjectMapped(t *testing.T) {
t.Cleanup(func() { t.Cleanup(func() {
settings.Pattern = nil settings.Pattern = nil
}) })
} })
func TestHandleSonarQubeWebhookProjectNotMapped(t *testing.T) { t.Run("Without mapped project", func(t *testing.T) {
settings.Projects = []settings.Project{ settings.Projects = []settings.Project{
{ {
SonarQube: struct{ Key string }{ SonarQube: struct{ Key string }{
@ -74,9 +75,9 @@ func TestHandleSonarQubeWebhookProjectNotMapped(t *testing.T) {
assert.Equal(t, http.StatusOK, rr.Code) assert.Equal(t, http.StatusOK, rr.Code)
assert.Equal(t, `{"message": "Project 'pr-bot' not in configured list. Request ignored."}`, rr.Body.String()) assert.Equal(t, `{"message": "Project 'pr-bot' not in configured list. Request ignored."}`, rr.Body.String())
} })
func TestHandleSonarQubeWebhookInvalidJSONBody(t *testing.T) { t.Run("With invalid JSON body", func(t *testing.T) {
settings.Projects = []settings.Project{ settings.Projects = []settings.Project{
{ {
SonarQube: struct{ Key string }{ SonarQube: struct{ Key string }{
@ -90,9 +91,9 @@ func TestHandleSonarQubeWebhookInvalidJSONBody(t *testing.T) {
assert.Equal(t, http.StatusUnprocessableEntity, rr.Code) assert.Equal(t, http.StatusUnprocessableEntity, rr.Code)
assert.Equal(t, `{"message": "Error parsing POST body."}`, rr.Body.String()) assert.Equal(t, `{"message": "Error parsing POST body."}`, rr.Body.String())
} })
func TestHandleSonarQubeWebhookInvalidWebhookSignature(t *testing.T) { t.Run("With invalid webhook signature", func(t *testing.T) {
settings.SonarQube = settings.SonarQubeConfig{ settings.SonarQube = settings.SonarQubeConfig{
Webhook: &settings.Webhook{ Webhook: &settings.Webhook{
Secret: "sonarqube-test-webhook-secret", Secret: "sonarqube-test-webhook-secret",
@ -111,9 +112,9 @@ func TestHandleSonarQubeWebhookInvalidWebhookSignature(t *testing.T) {
assert.Equal(t, http.StatusPreconditionFailed, rr.Code) assert.Equal(t, http.StatusPreconditionFailed, rr.Code)
assert.Equal(t, `{"message": "Webhook validation failed. Request rejected."}`, rr.Body.String()) assert.Equal(t, `{"message": "Webhook validation failed. Request rejected."}`, rr.Body.String())
} })
func TestHandleSonarQubeWebhookForPullRequest(t *testing.T) { t.Run("Running for Pull Request", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{ settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`), RegExp: regexp.MustCompile(`^PR-(\d+)$`),
} }
@ -139,9 +140,9 @@ func TestHandleSonarQubeWebhookForPullRequest(t *testing.T) {
t.Cleanup(func() { t.Cleanup(func() {
settings.Pattern = nil settings.Pattern = nil
}) })
} })
func TestHandleSonarQubeWebhookForBranch(t *testing.T) { t.Run("Running for branch", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{ settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`), RegExp: regexp.MustCompile(`^PR-(\d+)$`),
} }
@ -167,4 +168,5 @@ func TestHandleSonarQubeWebhookForBranch(t *testing.T) {
t.Cleanup(func() { t.Cleanup(func() {
settings.Pattern = nil settings.Pattern = nil
}) })
})
} }

View file

@ -28,7 +28,8 @@ func (c *ClientMock) Do(req *http.Request) (*http.Response, error) {
}, c.responseError }, c.responseError
} }
func TestParsePRIndexSuccess(t *testing.T) { func TestParsePRIndex(t *testing.T) {
t.Run("Success", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{ settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`), RegExp: regexp.MustCompile(`^PR-(\d+)$`),
} }
@ -39,9 +40,9 @@ func TestParsePRIndexSuccess(t *testing.T) {
t.Cleanup(func() { t.Cleanup(func() {
settings.Pattern = nil settings.Pattern = nil
}) })
} })
func TestParsePRIndexNonIntegerFailure(t *testing.T) { t.Run("No integer value", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{ settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`), RegExp: regexp.MustCompile(`^PR-(\d+)$`),
} }
@ -52,6 +53,7 @@ func TestParsePRIndexNonIntegerFailure(t *testing.T) {
t.Cleanup(func() { t.Cleanup(func() {
settings.Pattern = nil settings.Pattern = nil
}) })
})
} }
func TestPRNameFromIndex(t *testing.T) { func TestPRNameFromIndex(t *testing.T) {
@ -66,16 +68,14 @@ func TestPRNameFromIndex(t *testing.T) {
}) })
} }
func TestGetRenderedQualityGateSuccess(t *testing.T) { func TestGetRenderedQualityGate(t *testing.T) {
actual := GetRenderedQualityGate("OK") 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") t.Run("Failed", func(t *testing.T) {
} assert.Contains(t, GetRenderedQualityGate("ERROR"), ":x:", "Undetected failed 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")
} }
func TestGetPullRequestUrl(t *testing.T) { func TestGetPullRequestUrl(t *testing.T) {
@ -94,7 +94,8 @@ func TestGetPullRequestUrl(t *testing.T) {
}) })
} }
func TestRetrieveDataFromApiSuccess(t *testing.T) { func TestRetrieveDataFromApi(t *testing.T) {
t.Run("Success", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 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"}]}`)) 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"}]}`))
}) })
@ -115,9 +116,9 @@ func TestRetrieveDataFromApiSuccess(t *testing.T) {
assert.Nil(t, err, "Successful data retrieval broken and throws error") 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, "Basic dGVzdC10b2tlbjo=", request.Header.Get("Authorization"), "Authorization header not set")
assert.Equal(t, "PR-1", wrapper.PullRequests[0].Key, "Unmarshallowing into wrapper broken") assert.Equal(t, "PR-1", wrapper.PullRequests[0].Key, "Unmarshallowing into wrapper broken")
} })
func TestRetrieveDataFromApiRequestError(t *testing.T) { t.Run("Internal error", func(t *testing.T) {
expected := fmt.Errorf("This error indicates an error while performing the request") expected := fmt.Errorf("This error indicates an error while performing the request")
sdk := &SonarQubeSdk{ sdk := &SonarQubeSdk{
token: "test-token", token: "test-token",
@ -133,9 +134,9 @@ func TestRetrieveDataFromApiRequestError(t *testing.T) {
err := retrieveDataFromApi(sdk, request, &PullsResponse{}) err := retrieveDataFromApi(sdk, request, &PullsResponse{})
assert.ErrorIs(t, err, expected, "Undetected request performing error") assert.ErrorIs(t, err, expected, "Undetected request performing error")
} })
func TestRetrieveDataFromApiUnauthorized(t *testing.T) { t.Run("Unauthorized", func(t *testing.T) {
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
recorder.Code = http.StatusUnauthorized recorder.Code = http.StatusUnauthorized
@ -154,9 +155,9 @@ func TestRetrieveDataFromApiUnauthorized(t *testing.T) {
err := retrieveDataFromApi(sdk, request, &PullsResponse{}) err := retrieveDataFromApi(sdk, request, &PullsResponse{})
assert.Errorf(t, err, "missing or invalid API token", "Undetected unauthorized error") assert.Errorf(t, err, "missing or invalid API token", "Undetected unauthorized error")
} })
func TestRetrieveDataFromApiBodyReadError(t *testing.T) { t.Run("Body read error", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 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"}]}`)) 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"}]}`))
}) })
@ -177,9 +178,9 @@ func TestRetrieveDataFromApiBodyReadError(t *testing.T) {
err := retrieveDataFromApi(sdk, request, &PullsResponse{}) err := retrieveDataFromApi(sdk, request, &PullsResponse{})
assert.ErrorIs(t, err, expected, "Undetected body processing error") assert.ErrorIs(t, err, expected, "Undetected body processing error")
} })
func TestRetrieveDataFromApiBodyUnmarshalError(t *testing.T) { t.Run("Unmarshal error", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"pullReq`)) w.Write([]byte(`{"pullReq`))
}) })
@ -197,9 +198,11 @@ func TestRetrieveDataFromApiBodyUnmarshalError(t *testing.T) {
err := retrieveDataFromApi(sdk, request, &PullsResponse{}) err := retrieveDataFromApi(sdk, request, &PullsResponse{})
assert.Errorf(t, err, "unexpected end of JSON input", "Undetected body unmarshal error") assert.Errorf(t, err, "unexpected end of JSON input", "Undetected body unmarshal error")
})
} }
func TestFetchPullRequestsSuccess(t *testing.T) { func TestFetchPullRequests(t *testing.T) {
t.Run("Success", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 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"}]}`)) 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"}]}`))
}) })
@ -220,9 +223,9 @@ func TestFetchPullRequestsSuccess(t *testing.T) {
assert.Nil(t, err, "Successful data retrieval broken and throws error") assert.Nil(t, err, "Successful data retrieval broken and throws error")
assert.IsType(t, &PullsResponse{}, actual, "Happy path broken") assert.IsType(t, &PullsResponse{}, actual, "Happy path broken")
} })
func TestFetchPullRequestsRequestBuildingFailure(t *testing.T) { t.Run("Building failure", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 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"}]}`)) 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"}]}`))
}) })
@ -243,9 +246,9 @@ func TestFetchPullRequestsRequestBuildingFailure(t *testing.T) {
_, err := sdk.fetchPullRequests("test-project") _, err := sdk.fetchPullRequests("test-project")
assert.Equal(t, expected, err, "Unexpected error instance returned") assert.Equal(t, expected, err, "Unexpected error instance returned")
} })
func TestFetchPullRequestsRequestError(t *testing.T) { t.Run("Internal error", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 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"}]}`)) 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"}]}`))
}) })
@ -266,9 +269,9 @@ func TestFetchPullRequestsRequestError(t *testing.T) {
_, err := sdk.fetchPullRequests("test-project") _, err := sdk.fetchPullRequests("test-project")
assert.Equal(t, expected, err) assert.Equal(t, expected, err)
} })
func TestFetchPullRequestsErrorsInResponse(t *testing.T) { t.Run("Errors in response", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"errors":[{"msg":"Project 'test-project' not found"}]}`)) w.Write([]byte(`{"errors":[{"msg":"Project 'test-project' not found"}]}`))
}) })
@ -288,9 +291,11 @@ func TestFetchPullRequestsErrorsInResponse(t *testing.T) {
_, err := sdk.fetchPullRequests("test-project") _, err := sdk.fetchPullRequests("test-project")
assert.Errorf(t, err, "Project 'test-project' not found", "Response error parsing broken") assert.Errorf(t, err, "Project 'test-project' not found", "Response error parsing broken")
})
} }
func TestGetPullRequestSuccess(t *testing.T) { func TestGetPullRequest(t *testing.T) {
t.Run("Success", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{ settings.Pattern = &settings.PatternConfig{
Template: "PR-%d", Template: "PR-%d",
} }
@ -318,9 +323,9 @@ func TestGetPullRequestSuccess(t *testing.T) {
t.Cleanup(func() { t.Cleanup(func() {
settings.Pattern = nil settings.Pattern = nil
}) })
} })
func TestGetPullRequestFetchError(t *testing.T) { t.Run("Fetch error", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 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"}]}`)) 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"}]}`))
}) })
@ -342,9 +347,9 @@ func TestGetPullRequestFetchError(t *testing.T) {
assert.Errorf(t, err, "fetching pull requests failed", "Incorrect edge case is throwing errors") assert.Errorf(t, err, "fetching pull requests failed", "Incorrect edge case is throwing errors")
assert.Errorf(t, err, "Some simulated error", "Unexpected error cause") assert.Errorf(t, err, "Some simulated error", "Unexpected error cause")
} })
func TestGetPullRequestUnknownPR(t *testing.T) { t.Run("Unknown PR", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{ settings.Pattern = &settings.PatternConfig{
Template: "PR-%d", Template: "PR-%d",
} }
@ -372,9 +377,11 @@ func TestGetPullRequestUnknownPR(t *testing.T) {
t.Cleanup(func() { t.Cleanup(func() {
settings.Pattern = nil settings.Pattern = nil
}) })
})
} }
func TestGetMeasuresSuccess(t *testing.T) { func TestGetMeasures(t *testing.T) {
t.Run("Success", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 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"}]}`)) 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"}]}`))
}) })
@ -395,9 +402,9 @@ func TestGetMeasuresSuccess(t *testing.T) {
assert.Nil(t, err, "Successful data retrieval broken and throws error") assert.Nil(t, err, "Successful data retrieval broken and throws error")
assert.IsType(t, &MeasuresResponse{}, actual, "Happy path broken") assert.IsType(t, &MeasuresResponse{}, actual, "Happy path broken")
} })
func TestGetMeasuresRequestBuildingFailure(t *testing.T) { t.Run("Building failure", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 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"}]}`)) 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"}]}`))
}) })
@ -418,9 +425,9 @@ func TestGetMeasuresRequestBuildingFailure(t *testing.T) {
_, err := sdk.GetMeasures("test-project", "PR-1") _, err := sdk.GetMeasures("test-project", "PR-1")
assert.Equal(t, expected, err, "Unexpected error instance returned") assert.Equal(t, expected, err, "Unexpected error instance returned")
} })
func TestGetMeasuresRequestError(t *testing.T) { t.Run("Request error", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 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"}]}`)) 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"}]}`))
}) })
@ -441,9 +448,9 @@ func TestGetMeasuresRequestError(t *testing.T) {
_, err := sdk.GetMeasures("test-project", "PR-1") _, err := sdk.GetMeasures("test-project", "PR-1")
assert.Equal(t, expected, err) assert.Equal(t, expected, err)
} })
func TestGetMeasuresErrorsInResponse(t *testing.T) { t.Run("Errors in response", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 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"}]}`)) w.Write([]byte(`{"errors":[{"msg":"Component 'non-existing-project' of pull request 'PR-1' not found"}]}`))
}) })
@ -463,9 +470,11 @@ func TestGetMeasuresErrorsInResponse(t *testing.T) {
_, err := sdk.GetMeasures("non-existing-project", "PR-1") _, 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") assert.Errorf(t, err, "Component 'non-existing-project' of pull request 'PR-1' not found", "Response error parsing broken")
})
} }
func TestComposeGiteaCommentSuccess(t *testing.T) { func TestComposeGiteaComment(t *testing.T) {
t.Run("Success", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 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"}]}`)) 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"}]}`))
}) })
@ -495,9 +504,9 @@ func TestComposeGiteaCommentSuccess(t *testing.T) {
assert.Contains(t, actual, "| Bugs | 10 |", "Happy path [Metrics Values] 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, "https://sonarqube.example.com", "Happy path [Link] broken")
assert.Contains(t, actual, "/sq-bot review", "Happy path [Command] broken") assert.Contains(t, actual, "/sq-bot review", "Happy path [Command] broken")
} })
func TestComposeGiteaCommentError(t *testing.T) { t.Run("Error", func(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 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"}]}`)) 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"}]}`))
}) })
@ -523,6 +532,7 @@ func TestComposeGiteaCommentError(t *testing.T) {
}) })
assert.Errorf(t, err, expected.Error(), "Undetected error while composing comment") assert.Errorf(t, err, expected.Error(), "Undetected error while composing comment")
})
} }
func TestNew(t *testing.T) { func TestNew(t *testing.T) {

View file

@ -10,7 +10,8 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
var defaultConfig []byte = []byte( func defaultConfig() []byte {
return []byte(
`gitea: `gitea:
url: https://example.com/gitea url: https://example.com/gitea
token: token:
@ -34,6 +35,7 @@ namingPattern:
regex: "^PR-(\\d+)$" regex: "^PR-(\\d+)$"
template: "PR-%d" template: "PR-%d"
`) `)
}
func WriteConfigFile(t *testing.T, content []byte) string { func WriteConfigFile(t *testing.T, content []byte) string {
dir := os.TempDir() dir := os.TempDir()
@ -48,138 +50,17 @@ func WriteConfigFile(t *testing.T, content []byte) string {
return config return config
} }
func TestLoadWithMissingFile(t *testing.T) { 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") 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")
}
func TestLoadGiteaStructure(t *testing.T) {
c := WriteConfigFile(t, defaultConfig)
Load(c)
expected := GiteaConfig{
Url: "https://example.com/gitea",
Token: &Token{
Value: "d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565",
},
Webhook: &Webhook{
Secret: "haxxor-gitea-secret",
},
}
assert.EqualValues(t, expected, Gitea)
}
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)
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 TestLoadSonarQubeStructure(t *testing.T) { t.Run("File references", 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")
}
func TestLoadSonarQubeStructureWithAdditionalMetrics(t *testing.T) {
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
additionalMetrics: "new_security_hotspots"
projects:
- sonarqube:
key: gitea-sonarqube-bot
gitea:
owner: example-organization
name: pr-bot
`))
Load(c)
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())
}
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)
expected := SonarQubeConfig{
Url: "https://example.com/sonarqube",
Token: &Token{
Value: "injected-token",
},
Webhook: &Webhook{
Secret: "injected-webhook-secret",
},
}
assert.EqualValues(t, expected, SonarQube)
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") giteaWebhookSecretFile := path.Join(os.TempDir(), "webhook-secret-gitea")
_ = ioutil.WriteFile(giteaWebhookSecretFile, []byte(`gitea-totally-secret`), 0444) _ = ioutil.WriteFile(giteaWebhookSecretFile, []byte(`gitea-totally-secret`), 0444)
@ -252,10 +133,137 @@ projects:
os.Unsetenv("PRBOT_SONARQUBE_WEBHOOK_SECRETFILE") os.Unsetenv("PRBOT_SONARQUBE_WEBHOOK_SECRETFILE")
os.Unsetenv("PRBOT_SONARQUBE_TOKEN_FILE") os.Unsetenv("PRBOT_SONARQUBE_TOKEN_FILE")
}) })
})
} }
func TestLoadProjectsStructure(t *testing.T) { func TestLoadGitea(t *testing.T) {
c := WriteConfigFile(t, defaultConfig) t.Run("Default", func(t *testing.T) {
c := WriteConfigFile(t, defaultConfig())
Load(c)
expected := GiteaConfig{
Url: "https://example.com/gitea",
Token: &Token{
Value: "d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565",
},
Webhook: &Webhook{
Secret: "haxxor-gitea-secret",
},
}
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 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
sonarqube:
url: https://example.com/sonarqube
token:
value: fake-sonarqube-token
additionalMetrics: "new_security_hotspots"
projects:
- sonarqube:
key: gitea-sonarqube-bot
gitea:
owner: example-organization
name: pr-bot
`))
Load(c)
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())
})
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",
},
}
assert.EqualValues(t, expected, SonarQube)
t.Cleanup(func() {
os.Unsetenv("PRBOT_SONARQUBE_WEBHOOK_SECRET")
os.Unsetenv("PRBOT_SONARQUBE_TOKEN_VALUE")
})
})
}
func TestLoadProjects(t *testing.T) {
t.Run("Default", func(t *testing.T) {
c := WriteConfigFile(t, defaultConfig())
Load(c) Load(c)
expectedProjects := []Project{ expectedProjects := []Project{
@ -271,9 +279,9 @@ func TestLoadProjectsStructure(t *testing.T) {
} }
assert.EqualValues(t, expectedProjects, Projects) assert.EqualValues(t, expectedProjects, Projects)
} })
func TestLoadProjectsStructureWithNoMapping(t *testing.T) { t.Run("Empty mapping", func(t *testing.T) {
invalidConfig := []byte( invalidConfig := []byte(
`gitea: `gitea:
url: https://example.com/gitea url: https://example.com/gitea
@ -292,10 +300,12 @@ 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) { func TestLoadNamingPattern(t *testing.T) {
c := WriteConfigFile(t, defaultConfig) t.Run("Default", func(t *testing.T) {
c := WriteConfigFile(t, defaultConfig())
Load(c) Load(c)
expected := &PatternConfig{ expected := &PatternConfig{
@ -304,9 +314,9 @@ func TestLoadNamingPatternStructure(t *testing.T) {
} }
assert.EqualValues(t, expected, Pattern) assert.EqualValues(t, expected, Pattern)
} })
func TestLoadNamingPatternStructureWithInternalDefaults(t *testing.T) { t.Run("Internal defaults", func(t *testing.T) {
c := WriteConfigFile(t, []byte( c := WriteConfigFile(t, []byte(
`gitea: `gitea:
url: https://example.com/gitea url: https://example.com/gitea
@ -332,12 +342,12 @@ projects:
} }
assert.EqualValues(t, expected, Pattern) assert.EqualValues(t, expected, Pattern)
} })
func TestLoadNamingPatternStructureInjectedEnvs(t *testing.T) { t.Run("Injected envs", func(t *testing.T) {
os.Setenv("PRBOT_NAMINGPATTERN_REGEX", "test-(\\d+)-pullrequest") os.Setenv("PRBOT_NAMINGPATTERN_REGEX", "test-(\\d+)-pullrequest")
os.Setenv("PRBOT_NAMINGPATTERN_TEMPLATE", "test-%d-pullrequest") os.Setenv("PRBOT_NAMINGPATTERN_TEMPLATE", "test-%d-pullrequest")
c := WriteConfigFile(t, defaultConfig) c := WriteConfigFile(t, defaultConfig())
Load(c) Load(c)
expected := &PatternConfig{ expected := &PatternConfig{
@ -351,11 +361,11 @@ func TestLoadNamingPatternStructureInjectedEnvs(t *testing.T) {
os.Unsetenv("PRBOT_NAMINGPATTERN_REGEX") os.Unsetenv("PRBOT_NAMINGPATTERN_REGEX")
os.Unsetenv("PRBOT_NAMINGPATTERN_TEMPLATE") os.Unsetenv("PRBOT_NAMINGPATTERN_TEMPLATE")
}) })
} })
func TestLoadNamingPatternStructureMixedInput(t *testing.T) { t.Run("Mixed input", func(t *testing.T) {
os.Setenv("PRBOT_NAMINGPATTERN_REGEX", "test-(\\d+)-pullrequest") os.Setenv("PRBOT_NAMINGPATTERN_REGEX", "test-(\\d+)-pullrequest")
c := WriteConfigFile(t, defaultConfig) c := WriteConfigFile(t, defaultConfig())
Load(c) Load(c)
expected := &PatternConfig{ expected := &PatternConfig{
@ -368,4 +378,5 @@ func TestLoadNamingPatternStructureMixedInput(t *testing.T) {
t.Cleanup(func() { t.Cleanup(func() {
os.Unsetenv("PRBOT_NAMINGPATTERN_REGEX") os.Unsetenv("PRBOT_NAMINGPATTERN_REGEX")
}) })
})
} }

View file

@ -10,6 +10,7 @@ import (
) )
func TestNewWebhook(t *testing.T) { func TestNewWebhook(t *testing.T) {
t.Run("Success", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{ settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`), RegExp: regexp.MustCompile(`^PR-(\d+)$`),
} }
@ -25,16 +26,16 @@ func TestNewWebhook(t *testing.T) {
t.Cleanup(func() { t.Cleanup(func() {
settings.Pattern = nil settings.Pattern = nil
}) })
} })
func TestNewWebhookInvalidJSON(t *testing.T) { t.Run("Invalid JSON", func(t *testing.T) {
raw := []byte(`{ "serverUrl": ["invalid-server-url-content"] }`) raw := []byte(`{ "serverUrl": ["invalid-server-url-content"] }`)
_, ok := New(raw) _, ok := New(raw)
assert.False(t, ok) assert.False(t, ok)
} })
func TestNewWebhookInvalidBranchName(t *testing.T) { t.Run("Invalid branch name", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{ settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`), RegExp: regexp.MustCompile(`^PR-(\d+)$`),
} }
@ -47,10 +48,11 @@ func TestNewWebhookInvalidBranchName(t *testing.T) {
t.Cleanup(func() { t.Cleanup(func() {
settings.Pattern = nil settings.Pattern = nil
}) })
})
} }
func TestWebhookGetRevision(t *testing.T) { func TestWebhookGetRevision(t *testing.T) {
t.Run("Default revision", func(t *testing.T) { t.Run("Default", func(t *testing.T) {
w := Webhook{ w := Webhook{
Revision: "225fa0306c0ab83297d0cb5db0717b194ccb2e76", Revision: "225fa0306c0ab83297d0cb5db0717b194ccb2e76",
} }
@ -58,7 +60,7 @@ func TestWebhookGetRevision(t *testing.T) {
assert.Equal(t, w.Revision, w.GetRevision()) 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{ w := Webhook{
Revision: "225fa0306c0ab83297d0cb5db0717b194ccb2e76", Revision: "225fa0306c0ab83297d0cb5db0717b194ccb2e76",
Properties: &properties{}, Properties: &properties{},