Improve error handling of SonarQube client
Due to unhandled errors within the SonarQube client, users may be presented with Go panics or just don't know what the root cause of a non-working bot is. Now it is possible to identify network errors, authentication issues or an incorrect bot configuration regarding SonarQube. Fixes: #20 Signed-off-by: Steven Kriegler <sk.bunsenbrenner@gmail.com>
This commit is contained in:
parent
eb3cb301fc
commit
02ad0c0bf0
|
@ -28,6 +28,7 @@ type MeasuresComponent struct {
|
|||
type MeasuresResponse struct {
|
||||
Component MeasuresComponent `json:"component"`
|
||||
Metrics []MeasuresComponentMetric `json:"metrics"`
|
||||
Errors []Error `json:"errors"`
|
||||
}
|
||||
|
||||
func (mr *MeasuresResponse) GetRenderedMarkdownTable() string {
|
||||
|
|
|
@ -9,6 +9,7 @@ type PullRequest struct {
|
|||
|
||||
type PullsResponse struct {
|
||||
PullRequests []PullRequest `json:"pullRequests"`
|
||||
Errors []Error `json:"errors"`
|
||||
}
|
||||
|
||||
func (r *PullsResponse) GetPullRequest(name string) *PullRequest {
|
||||
|
|
|
@ -38,6 +38,38 @@ func GetRenderedQualityGate(qg string) string {
|
|||
return fmt.Sprintf("**Quality Gate**: %s", status)
|
||||
}
|
||||
|
||||
func retrieveDataFromApi(sdk *SonarQubeSdk, request *http.Request, wrapper interface{}) error {
|
||||
request.Header.Add("Authorization", sdk.basicAuth())
|
||||
rawResponse, err := sdk.client.Do(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rawResponse.StatusCode == http.StatusUnauthorized {
|
||||
return fmt.Errorf("missing or invalid API token")
|
||||
}
|
||||
|
||||
if rawResponse.Body != nil {
|
||||
defer rawResponse.Body.Close()
|
||||
}
|
||||
|
||||
body, err := sdk.bodyReader(rawResponse.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, wrapper)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Message string `json:"msg"`
|
||||
}
|
||||
|
||||
type SonarQubeSdkInterface interface {
|
||||
GetMeasures(string, string) (*MeasuresResponse, error)
|
||||
GetPullRequestUrl(string, int64) string
|
||||
|
@ -52,44 +84,49 @@ type CommentComposeData struct {
|
|||
QualityGate string
|
||||
}
|
||||
|
||||
type ClientInterface interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
type BodyReader func(io.Reader) ([]byte, error)
|
||||
type HttpRequest func(method string, target string, body io.Reader) (*http.Request, error)
|
||||
|
||||
type SonarQubeSdk struct {
|
||||
client *http.Client
|
||||
baseUrl string
|
||||
token string
|
||||
client ClientInterface
|
||||
bodyReader BodyReader
|
||||
httpRequest HttpRequest
|
||||
baseUrl string
|
||||
token string
|
||||
}
|
||||
|
||||
func (sdk *SonarQubeSdk) GetPullRequestUrl(project string, index int64) string {
|
||||
return fmt.Sprintf("%s/dashboard?id=%s&pullRequest=%s", sdk.baseUrl, project, PRNameFromIndex(index))
|
||||
}
|
||||
|
||||
func (sdk *SonarQubeSdk) fetchPullRequests(project string) *PullsResponse {
|
||||
func (sdk *SonarQubeSdk) fetchPullRequests(project string) (*PullsResponse, error) {
|
||||
url := fmt.Sprintf("%s/api/project_pull_requests/list?project=%s", sdk.baseUrl, project)
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
request, err := sdk.httpRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
log.Printf("Cannot initialize Request: %s", err.Error())
|
||||
return nil
|
||||
}
|
||||
req.Header.Add("Authorization", sdk.basicAuth())
|
||||
rawResp, _ := sdk.client.Do(req)
|
||||
if rawResp.Body != nil {
|
||||
defer rawResp.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(rawResp.Body)
|
||||
response := &PullsResponse{}
|
||||
err = json.Unmarshal(body, &response)
|
||||
err = retrieveDataFromApi(sdk, request, response)
|
||||
if err != nil {
|
||||
log.Printf("cannot parse response from SonarQube: %s", err.Error())
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response
|
||||
if len(response.Errors) != 0 {
|
||||
return nil, fmt.Errorf("%s", response.Errors[0].Message)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (sdk *SonarQubeSdk) GetPullRequest(project string, index int64) (*PullRequest, error) {
|
||||
response := sdk.fetchPullRequests(project)
|
||||
if response == nil {
|
||||
return nil, fmt.Errorf("unable to retrieve pull requests from SonarQube")
|
||||
response, err := sdk.fetchPullRequests(project)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetching pull requests failed: %w", err)
|
||||
}
|
||||
|
||||
name := PRNameFromIndex(index)
|
||||
|
@ -103,21 +140,19 @@ func (sdk *SonarQubeSdk) GetPullRequest(project string, index int64) (*PullReque
|
|||
|
||||
func (sdk *SonarQubeSdk) GetMeasures(project string, branch string) (*MeasuresResponse, error) {
|
||||
url := fmt.Sprintf("%s/api/measures/component?additionalFields=metrics&metricKeys=%s&component=%s&pullRequest=%s", sdk.baseUrl, settings.SonarQube.GetMetricsList(), project, branch)
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
request, err := sdk.httpRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot initialize Request: %w", err)
|
||||
}
|
||||
req.Header.Add("Authorization", sdk.basicAuth())
|
||||
rawResp, _ := sdk.client.Do(req)
|
||||
if rawResp.Body != nil {
|
||||
defer rawResp.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(rawResp.Body)
|
||||
response := &MeasuresResponse{}
|
||||
err = json.Unmarshal(body, &response)
|
||||
err = retrieveDataFromApi(sdk, request, response)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse response from SonarQube: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(response.Errors) != 0 {
|
||||
return nil, fmt.Errorf("%s", response.Errors[0].Message)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
|
@ -147,8 +182,10 @@ func (sdk *SonarQubeSdk) basicAuth() string {
|
|||
|
||||
func New() *SonarQubeSdk {
|
||||
return &SonarQubeSdk{
|
||||
client: &http.Client{},
|
||||
baseUrl: settings.SonarQube.Url,
|
||||
token: settings.SonarQube.Token.Value,
|
||||
client: &http.Client{},
|
||||
bodyReader: io.ReadAll,
|
||||
httpRequest: http.NewRequest,
|
||||
baseUrl: settings.SonarQube.Url,
|
||||
token: settings.SonarQube.Token.Value,
|
||||
}
|
||||
}
|
||||
|
|
489
internal/clients/sonarqube/sonarqube_test.go
Normal file
489
internal/clients/sonarqube/sonarqube_test.go
Normal file
|
@ -0,0 +1,489 @@
|
|||
package sonarqube
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"gitea-sonarqube-pr-bot/internal/settings"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type ClientMock struct {
|
||||
responseError error
|
||||
handler http.HandlerFunc
|
||||
recoder *httptest.ResponseRecorder
|
||||
}
|
||||
|
||||
func (c *ClientMock) Do(req *http.Request) (*http.Response, error) {
|
||||
c.handler.ServeHTTP(c.recoder, req)
|
||||
|
||||
return &http.Response{
|
||||
StatusCode: c.recoder.Code,
|
||||
Body: c.recoder.Result().Body,
|
||||
}, c.responseError
|
||||
}
|
||||
|
||||
func TestParsePRIndexSuccess(t *testing.T) {
|
||||
actual, _ := ParsePRIndex("PR-1337")
|
||||
assert.Equal(t, 1337, actual, "PR index parsing is broken")
|
||||
}
|
||||
|
||||
func TestParsePRIndexNonIntegerFailure(t *testing.T) {
|
||||
_, err := ParsePRIndex("PR-invalid")
|
||||
assert.EqualErrorf(t, err, "branch name 'PR-invalid' does not match regex '^PR-(\\d+)$'", "Integer parsing succeeds unexpectedly")
|
||||
}
|
||||
|
||||
func TestPRNameFromIndex(t *testing.T) {
|
||||
assert.Equal(t, "PR-1337", PRNameFromIndex(1337))
|
||||
}
|
||||
|
||||
func TestGetRenderedQualityGateSuccess(t *testing.T) {
|
||||
actual := GetRenderedQualityGate("OK")
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
func TestGetPullRequestUrl(t *testing.T) {
|
||||
sdk := &SonarQubeSdk{
|
||||
baseUrl: "https://sonarqube.example.com",
|
||||
}
|
||||
|
||||
actual := sdk.GetPullRequestUrl("test-project", 1337)
|
||||
assert.Equal(t, "https://sonarqube.example.com/dashboard?id=test-project&pullRequest=PR-1337", actual, "PR Dashboard URL building broken")
|
||||
}
|
||||
|
||||
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"}]}`))
|
||||
})
|
||||
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")
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
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{})
|
||||
|
||||
assert.Errorf(t, err, "missing or invalid API token", "Undetected unauthorized error")
|
||||
}
|
||||
|
||||
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"}]}`))
|
||||
})
|
||||
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{})
|
||||
|
||||
assert.ErrorIs(t, err, expected, "Undetected body processing error")
|
||||
}
|
||||
|
||||
func TestRetrieveDataFromApiBodyUnmarshalError(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,
|
||||
}
|
||||
|
||||
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
err := retrieveDataFromApi(sdk, request, &PullsResponse{})
|
||||
|
||||
assert.Errorf(t, err, "unexpected end of JSON input", "Undetected body unmarshal error")
|
||||
}
|
||||
|
||||
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) {
|
||||
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")
|
||||
}
|
||||
|
||||
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"}]}`))
|
||||
})
|
||||
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)
|
||||
|
||||
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) {
|
||||
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'")
|
||||
}
|
||||
|
||||
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"}]}`))
|
||||
})
|
||||
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")
|
||||
}
|
||||
|
||||
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"}]}`))
|
||||
})
|
||||
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")
|
||||
|
||||
assert.Equal(t, expected, err, "Unexpected error instance returned")
|
||||
}
|
||||
|
||||
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
|
||||
},
|
||||
}
|
||||
|
||||
_, err := sdk.GetMeasures("test-project", "PR-1")
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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"}]}`))
|
||||
})
|
||||
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) {
|
||||
settings.SonarQube = settings.SonarQubeConfig{
|
||||
Url: "http://example.com",
|
||||
Token: &settings.Token{
|
||||
Value: "test-token",
|
||||
},
|
||||
}
|
||||
assert.IsType(t, &SonarQubeSdk{}, New(), "")
|
||||
}
|
Loading…
Reference in a new issue