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) {
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("/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 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) {
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", "/ping", nil)
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) {
func TestSonarQubeAPIRoute(t *testing.T) {
t.Run("Missing project header", func(t *testing.T) {
router := New(new(GiteaHandlerMock), new(SonarQubeHandlerMock))
w := httptest.NewRecorder()
@ -114,9 +121,9 @@ func TestSonarQubeAPIRouteMissingProjectHeader(t *testing.T) {
router.Engine.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
}
})
func TestSonarQubeAPIRouteProcessing(t *testing.T) {
t.Run("Processing", func(t *testing.T) {
sonarQubeHandlerMock := new(SonarQubeHandlerMock)
sonarQubeHandlerMock.On("Handle", mock.IsType(&http.Request{}))
@ -130,9 +137,11 @@ func TestSonarQubeAPIRouteProcessing(t *testing.T) {
assert.Equal(t, http.StatusOK, w.Code)
sonarQubeHandlerMock.AssertNumberOfCalls(t, "Handle", 1)
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))
w := httptest.NewRecorder()
@ -140,9 +149,9 @@ func TestGiteaAPIRouteMissingEventHeader(t *testing.T) {
router.Engine.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
}
})
func TestGiteaAPIRouteSynchronizeProcessing(t *testing.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()
@ -158,9 +167,9 @@ func TestGiteaAPIRouteSynchronizeProcessing(t *testing.T) {
giteaHandlerMock.AssertNumberOfCalls(t, "HandleSynchronize", 1)
giteaHandlerMock.AssertNumberOfCalls(t, "HandleComment", 0)
giteaHandlerMock.AssertExpectations(t)
}
})
func TestGiteaAPIRouteCommentProcessing(t *testing.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)
@ -176,9 +185,9 @@ func TestGiteaAPIRouteCommentProcessing(t *testing.T) {
giteaHandlerMock.AssertNumberOfCalls(t, "HandleSynchronize", 0)
giteaHandlerMock.AssertNumberOfCalls(t, "HandleComment", 1)
giteaHandlerMock.AssertExpectations(t)
}
})
func TestGiteaAPIRouteUnknownEvent(t *testing.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()
@ -194,4 +203,5 @@ func TestGiteaAPIRouteUnknownEvent(t *testing.T) {
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) {
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) {
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) {
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) {
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")
// 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")
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) {
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,7 +34,8 @@ func withValidSonarQubeRequestData(t *testing.T, jsonBody []byte) (*http.Request
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{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
@ -59,9 +60,9 @@ func TestHandleSonarQubeWebhookProjectMapped(t *testing.T) {
t.Cleanup(func() {
settings.Pattern = nil
})
}
})
func TestHandleSonarQubeWebhookProjectNotMapped(t *testing.T) {
t.Run("Without mapped project", func(t *testing.T) {
settings.Projects = []settings.Project{
{
SonarQube: struct{ Key string }{
@ -74,9 +75,9 @@ func TestHandleSonarQubeWebhookProjectNotMapped(t *testing.T) {
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) {
t.Run("With invalid JSON body", func(t *testing.T) {
settings.Projects = []settings.Project{
{
SonarQube: struct{ Key string }{
@ -90,9 +91,9 @@ func TestHandleSonarQubeWebhookInvalidJSONBody(t *testing.T) {
assert.Equal(t, http.StatusUnprocessableEntity, rr.Code)
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{
Webhook: &settings.Webhook{
Secret: "sonarqube-test-webhook-secret",
@ -111,9 +112,9 @@ func TestHandleSonarQubeWebhookInvalidWebhookSignature(t *testing.T) {
assert.Equal(t, http.StatusPreconditionFailed, rr.Code)
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{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
@ -139,9 +140,9 @@ func TestHandleSonarQubeWebhookForPullRequest(t *testing.T) {
t.Cleanup(func() {
settings.Pattern = nil
})
}
})
func TestHandleSonarQubeWebhookForBranch(t *testing.T) {
t.Run("Running for branch", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
@ -167,4 +168,5 @@ func TestHandleSonarQubeWebhookForBranch(t *testing.T) {
t.Cleanup(func() {
settings.Pattern = nil
})
})
}

View file

@ -28,7 +28,8 @@ func (c *ClientMock) Do(req *http.Request) (*http.Response, error) {
}, c.responseError
}
func TestParsePRIndexSuccess(t *testing.T) {
func TestParsePRIndex(t *testing.T) {
t.Run("Success", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
@ -39,9 +40,9 @@ func TestParsePRIndexSuccess(t *testing.T) {
t.Cleanup(func() {
settings.Pattern = nil
})
}
})
func TestParsePRIndexNonIntegerFailure(t *testing.T) {
t.Run("No integer value", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
RegExp: regexp.MustCompile(`^PR-(\d+)$`),
}
@ -52,6 +53,7 @@ func TestParsePRIndexNonIntegerFailure(t *testing.T) {
t.Cleanup(func() {
settings.Pattern = nil
})
})
}
func TestPRNameFromIndex(t *testing.T) {
@ -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,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) {
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.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")
}
})
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")
sdk := &SonarQubeSdk{
token: "test-token",
@ -133,9 +134,9 @@ func TestRetrieveDataFromApiRequestError(t *testing.T) {
err := retrieveDataFromApi(sdk, request, &PullsResponse{})
assert.ErrorIs(t, err, expected, "Undetected request performing error")
}
})
func TestRetrieveDataFromApiUnauthorized(t *testing.T) {
t.Run("Unauthorized", func(t *testing.T) {
recorder := httptest.NewRecorder()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
recorder.Code = http.StatusUnauthorized
@ -154,9 +155,9 @@ func TestRetrieveDataFromApiUnauthorized(t *testing.T) {
err := retrieveDataFromApi(sdk, request, &PullsResponse{})
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) {
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{})
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) {
w.Write([]byte(`{"pullReq`))
})
@ -197,9 +198,11 @@ func TestRetrieveDataFromApiBodyUnmarshalError(t *testing.T) {
err := retrieveDataFromApi(sdk, request, &PullsResponse{})
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) {
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.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) {
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")
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) {
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")
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) {
w.Write([]byte(`{"errors":[{"msg":"Project 'test-project' not found"}]}`))
})
@ -288,9 +291,11 @@ func TestFetchPullRequestsErrorsInResponse(t *testing.T) {
_, err := sdk.fetchPullRequests("test-project")
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{
Template: "PR-%d",
}
@ -318,9 +323,9 @@ func TestGetPullRequestSuccess(t *testing.T) {
t.Cleanup(func() {
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) {
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, "Some simulated error", "Unexpected error cause")
}
})
func TestGetPullRequestUnknownPR(t *testing.T) {
t.Run("Unknown PR", func(t *testing.T) {
settings.Pattern = &settings.PatternConfig{
Template: "PR-%d",
}
@ -372,9 +377,11 @@ func TestGetPullRequestUnknownPR(t *testing.T) {
t.Cleanup(func() {
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) {
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.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) {
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")
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) {
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")
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) {
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")
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) {
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, "https://sonarqube.example.com", "Happy path [Link] 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) {
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")
})
}
func TestNew(t *testing.T) {

View file

@ -10,7 +10,8 @@ import (
"github.com/stretchr/testify/assert"
)
var defaultConfig []byte = []byte(
func defaultConfig() []byte {
return []byte(
`gitea:
url: https://example.com/gitea
token:
@ -34,6 +35,7 @@ namingPattern:
regex: "^PR-(\\d+)$"
template: "PR-%d"
`)
}
func WriteConfigFile(t *testing.T, content []byte) string {
dir := os.TempDir()
@ -48,138 +50,17 @@ func WriteConfigFile(t *testing.T, content []byte) string {
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")
}
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")
}
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) {
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) {
t.Run("File references", func(t *testing.T) {
giteaWebhookSecretFile := path.Join(os.TempDir(), "webhook-secret-gitea")
_ = ioutil.WriteFile(giteaWebhookSecretFile, []byte(`gitea-totally-secret`), 0444)
@ -252,10 +133,137 @@ projects:
os.Unsetenv("PRBOT_SONARQUBE_WEBHOOK_SECRETFILE")
os.Unsetenv("PRBOT_SONARQUBE_TOKEN_FILE")
})
})
}
func TestLoadProjectsStructure(t *testing.T) {
c := WriteConfigFile(t, defaultConfig)
func TestLoadGitea(t *testing.T) {
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)
expectedProjects := []Project{
@ -271,9 +279,9 @@ func TestLoadProjectsStructure(t *testing.T) {
}
assert.EqualValues(t, expectedProjects, Projects)
}
})
func TestLoadProjectsStructureWithNoMapping(t *testing.T) {
t.Run("Empty mapping", func(t *testing.T) {
invalidConfig := []byte(
`gitea:
url: https://example.com/gitea
@ -292,10 +300,12 @@ projects: []
c := WriteConfigFile(t, invalidConfig)
assert.Panics(t, func() { Load(c) }, "No panic for empty project mapping that is required")
})
}
func TestLoadNamingPatternStructure(t *testing.T) {
c := WriteConfigFile(t, defaultConfig)
func TestLoadNamingPattern(t *testing.T) {
t.Run("Default", func(t *testing.T) {
c := WriteConfigFile(t, defaultConfig())
Load(c)
expected := &PatternConfig{
@ -304,9 +314,9 @@ func TestLoadNamingPatternStructure(t *testing.T) {
}
assert.EqualValues(t, expected, Pattern)
}
})
func TestLoadNamingPatternStructureWithInternalDefaults(t *testing.T) {
t.Run("Internal defaults", func(t *testing.T) {
c := WriteConfigFile(t, []byte(
`gitea:
url: https://example.com/gitea
@ -332,12 +342,12 @@ projects:
}
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_TEMPLATE", "test-%d-pullrequest")
c := WriteConfigFile(t, defaultConfig)
c := WriteConfigFile(t, defaultConfig())
Load(c)
expected := &PatternConfig{
@ -351,11 +361,11 @@ func TestLoadNamingPatternStructureInjectedEnvs(t *testing.T) {
os.Unsetenv("PRBOT_NAMINGPATTERN_REGEX")
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")
c := WriteConfigFile(t, defaultConfig)
c := WriteConfigFile(t, defaultConfig())
Load(c)
expected := &PatternConfig{
@ -368,4 +378,5 @@ func TestLoadNamingPatternStructureMixedInput(t *testing.T) {
t.Cleanup(func() {
os.Unsetenv("PRBOT_NAMINGPATTERN_REGEX")
})
})
}

View file

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