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:
justusbunsi 2022-06-17 20:19:59 +02:00
parent eb3cb301fc
commit 02ad0c0bf0
No known key found for this signature in database
GPG key ID: 82B29BF2507F9F8B
4 changed files with 561 additions and 33 deletions

View file

@ -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 {

View file

@ -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 {

View file

@ -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,
}
}

View 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(), "")
}