From b632381c908a178791354ab5ef5f4d5a8a7f11c2 Mon Sep 17 00:00:00 2001 From: justusbunsi Date: Tue, 12 Jul 2022 18:31:25 +0200 Subject: [PATCH] Write tests for Gitea client Signed-off-by: Steven Kriegler Reviewed-on: https://codeberg.org/justusbunsi/gitea-sonarqube-bot/pulls/36 --- Makefile | 8 +- cmd/gitea-sonarqube-bot/main.go | 5 +- internal/clients/gitea/gitea.go | 12 +- internal/clients/gitea/gitea_test.go | 198 +++++++++++++++++++++++++++ internal/clients/gitea/main_test.go | 14 ++ 5 files changed, 231 insertions(+), 6 deletions(-) create mode 100644 internal/clients/gitea/gitea_test.go create mode 100644 internal/clients/gitea/main_test.go diff --git a/Makefile b/Makefile index 87bd590..98cee96 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,8 @@ help: @echo " - test Run full test suite" @echo " - test p=./path/to/package Run test suite for specific package" @echo " - test\#SpecificTestName Run a specific" - @echo " - coverage Run full test suite and generates coverage report as HTML file" + @echo " - coverage Run full test suite and generate coverage report as HTML file" + @echo " - coverage p=./path/to/package Run test suite for specific package and generate coverage report as HTML file" @echo " - helm-params Auto-generates 'Parameters' section of 'helm/README.md' based on comments in values.yaml" @echo " - helm-pack Prepares Helm Chart release artifacts for pushing to 'charts' branch" @echo " - dep Dependency maintenance (tidy, vendor, verify)" @@ -44,8 +45,13 @@ test-ci: go test -mod=vendor -coverprofile=cover.out -json ./... > test-report.out coverage: +ifdef p + go test -coverprofile=cover.out $(p) + go tool cover -html=cover.out -o cover.html +else go test -coverprofile=cover.out ./... go tool cover -html=cover.out -o cover.html +endif helm-params: npm install diff --git a/cmd/gitea-sonarqube-bot/main.go b/cmd/gitea-sonarqube-bot/main.go index 49739e9..371f966 100644 --- a/cmd/gitea-sonarqube-bot/main.go +++ b/cmd/gitea-sonarqube-bot/main.go @@ -16,6 +16,7 @@ import ( sonarQubeSdk "codeberg.org/justusbunsi/gitea-sonarqube-bot/internal/clients/sonarqube" "codeberg.org/justusbunsi/gitea-sonarqube-bot/internal/settings" + "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" ) @@ -61,8 +62,8 @@ func serveApi(c *cli.Context) error { log.Println("Hi! I'm Gitea SonarQube Bot. At your service.") log.Println("Config file in use:", config) - giteaHandler := api.NewGiteaWebhookHandler(giteaSdk.New(), sonarQubeSdk.New(&settings.SonarQube)) - sqHandler := api.NewSonarQubeWebhookHandler(giteaSdk.New(), sonarQubeSdk.New(&settings.SonarQube)) + giteaHandler := api.NewGiteaWebhookHandler(giteaSdk.New(&settings.Gitea, gitea.NewClient), sonarQubeSdk.New(&settings.SonarQube)) + sqHandler := api.NewSonarQubeWebhookHandler(giteaSdk.New(&settings.Gitea, gitea.NewClient), sonarQubeSdk.New(&settings.SonarQube)) server := api.New(giteaHandler, sqHandler) srv := &http.Server{ diff --git a/internal/clients/gitea/gitea.go b/internal/clients/gitea/gitea.go index 92f837a..0bda95f 100644 --- a/internal/clients/gitea/gitea.go +++ b/internal/clients/gitea/gitea.go @@ -14,8 +14,14 @@ type GiteaSdkInterface interface { DetermineHEAD(settings.GiteaRepository, int64) (string, error) } +type ClientInterface interface { + CreateIssueComment(owner, repo string, index int64, opt gitea.CreateIssueCommentOption) (*gitea.Comment, *gitea.Response, error) + CreateStatus(owner, repo, sha string, opts gitea.CreateStatusOption) (*gitea.Status, *gitea.Response, error) + GetPullRequest(owner, repo string, index int64) (*gitea.PullRequest, *gitea.Response, error) +} + type GiteaSdk struct { - client *gitea.Client + client ClientInterface } func (sdk *GiteaSdk) PostComment(repo settings.GiteaRepository, idx int, msg string) error { @@ -53,8 +59,8 @@ func (sdk *GiteaSdk) DetermineHEAD(repo settings.GiteaRepository, idx int64) (st return pr.Head.Sha, nil } -func New() *GiteaSdk { - client, err := gitea.NewClient(settings.Gitea.Url, gitea.SetToken(settings.Gitea.Token.Value)) +func New[T ClientInterface](configuration *settings.GiteaConfig, newClient func(url string, options ...gitea.ClientOption) (T, error)) *GiteaSdk { + client, err := newClient(configuration.Url, gitea.SetToken(configuration.Token.Value)) if err != nil { panic(fmt.Errorf("cannot initialize Gitea client: %w", err)) } diff --git a/internal/clients/gitea/gitea_test.go b/internal/clients/gitea/gitea_test.go new file mode 100644 index 0000000..a094245 --- /dev/null +++ b/internal/clients/gitea/gitea_test.go @@ -0,0 +1,198 @@ +package gitea + +import ( + "errors" + "net/http" + "testing" + + "code.gitea.io/sdk/gitea" + "codeberg.org/justusbunsi/gitea-sonarqube-bot/internal/settings" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +type SdkMock struct { + simulatedError error + mock.Mock +} + +func (m *SdkMock) CreateIssueComment(owner, repo string, index int64, opt gitea.CreateIssueCommentOption) (*gitea.Comment, *gitea.Response, error) { + m.Called(owner, repo, index, opt) + return nil, nil, m.simulatedError +} +func (m *SdkMock) CreateStatus(owner, repo, sha string, opts gitea.CreateStatusOption) (*gitea.Status, *gitea.Response, error) { + m.Called(owner, repo, sha, opts) + r := &gitea.Response{ + Response: &http.Response{ + StatusCode: http.StatusOK, + }, + } + if m.simulatedError != nil { + r.StatusCode = http.StatusInternalServerError + } + return nil, r, m.simulatedError +} +func (m *SdkMock) GetPullRequest(owner, repo string, index int64) (*gitea.PullRequest, *gitea.Response, error) { + m.Called(owner, repo, index) + return &gitea.PullRequest{ + Head: &gitea.PRBranchInfo{ + Sha: "a1aada0b7b19e58ae539b4812d960bca35ev78cb", + }, + }, nil, m.simulatedError +} + +func TestNew(t *testing.T) { + t.Run("Success", func(t *testing.T) { + config := &settings.GiteaConfig{ + Url: "http://example.com", + Token: &settings.Token{ + Value: "test-token", + }, + } + + callback := func(url string, options ...gitea.ClientOption) (*SdkMock, error) { + return &SdkMock{}, nil + } + assert.IsType(t, &GiteaSdk{}, New(config, callback), "") + }) + + t.Run("Initialization errors", func(t *testing.T) { + config := &settings.GiteaConfig{ + Url: "http://example.com", + Token: &settings.Token{ + Value: "test-token", + }, + } + + callback := func(url string, options ...gitea.ClientOption) (*SdkMock, error) { + return nil, errors.New("Simulated initialization error") + } + assert.PanicsWithError(t, "cannot initialize Gitea client: Simulated initialization error", func() { New(config, callback) }) + }) +} + +func TestDetermineHEAD(t *testing.T) { + t.Run("Success", func(t *testing.T) { + clientMock := &SdkMock{} + clientMock.On("GetPullRequest", "test-owner", "test-repo", int64(1)).Once() + + sdk := GiteaSdk{ + client: clientMock, + } + sha, err := sdk.DetermineHEAD(settings.GiteaRepository{ + Owner: "test-owner", + Name: "test-repo", + }, 1) + + assert.Nil(t, err) + assert.Equal(t, "a1aada0b7b19e58ae539b4812d960bca35ev78cb", sha) + clientMock.AssertExpectations(t) + }) + + t.Run("API error", func(t *testing.T) { + clientMock := &SdkMock{ + simulatedError: errors.New("Simulated error"), + } + clientMock.On("GetPullRequest", "test-owner", "test-repo", int64(1)).Once() + + sdk := GiteaSdk{ + client: clientMock, + } + + _, err := sdk.DetermineHEAD(settings.GiteaRepository{ + Owner: "test-owner", + Name: "test-repo", + }, 1) + + assert.Errorf(t, err, "Simulated error") + clientMock.AssertExpectations(t) + }) +} + +func TestUpdateStatus(t *testing.T) { + t.Run("Success", func(t *testing.T) { + clientMock := &SdkMock{} + clientMock.On("CreateStatus", "test-owner", "test-repo", "a1aada0b7b19e58ae539b4812d960bca35ev78cb", mock.Anything).Once() + sdk := GiteaSdk{ + client: clientMock, + } + + err := sdk.UpdateStatus(settings.GiteaRepository{ + Owner: "test-owner", + Name: "test-repo", + }, "a1aada0b7b19e58ae539b4812d960bca35ev78cb", StatusDetails{ + Url: "http://example.com", + Message: "expected message", + State: StatusOK, + }) + + assert.Nil(t, err) + clientMock.AssertExpectations(t) + + actualStatusOption := clientMock.Calls[0].Arguments[3].(gitea.CreateStatusOption) + assert.Equal(t, "http://example.com", actualStatusOption.TargetURL) + assert.Equal(t, "expected message", actualStatusOption.Description) + assert.Equal(t, gitea.StatusSuccess, actualStatusOption.State) + }) + + t.Run("API error", func(t *testing.T) { + clientMock := &SdkMock{ + simulatedError: errors.New("Simulated error"), + } + clientMock.On("CreateStatus", "test-owner", "test-repo", "a1aada0b7b19e58ae539b4812d960bca35ev78cb", mock.Anything).Once() + sdk := GiteaSdk{ + client: clientMock, + } + + err := sdk.UpdateStatus(settings.GiteaRepository{ + Owner: "test-owner", + Name: "test-repo", + }, "a1aada0b7b19e58ae539b4812d960bca35ev78cb", StatusDetails{ + Url: "http://example.com", + Message: "expected message", + State: StatusOK, + }) + + assert.Errorf(t, err, "Simulated error") + clientMock.AssertExpectations(t) + }) +} + +func TestPostComment(t *testing.T) { + t.Run("Success", func(t *testing.T) { + clientMock := &SdkMock{} + clientMock.On("CreateIssueComment", "test-owner", "test-repo", int64(1), mock.Anything).Once() + sdk := GiteaSdk{ + client: clientMock, + } + + err := sdk.PostComment(settings.GiteaRepository{ + Owner: "test-owner", + Name: "test-repo", + }, 1, "test post comment") + + assert.Nil(t, err) + clientMock.AssertExpectations(t) + + actualCommentOption := clientMock.Calls[0].Arguments[3].(gitea.CreateIssueCommentOption) + assert.Equal(t, "test post comment", actualCommentOption.Body) + }) + + t.Run("API error", func(t *testing.T) { + clientMock := &SdkMock{ + simulatedError: errors.New("Simulated error"), + } + clientMock.On("CreateIssueComment", "test-owner", "test-repo", int64(1), mock.Anything).Once() + sdk := GiteaSdk{ + client: clientMock, + } + + err := sdk.PostComment(settings.GiteaRepository{ + Owner: "test-owner", + Name: "test-repo", + }, 1, "test post comment") + + assert.Errorf(t, err, "Simulated error") + clientMock.AssertExpectations(t) + }) +} diff --git a/internal/clients/gitea/main_test.go b/internal/clients/gitea/main_test.go new file mode 100644 index 0000000..1355cbb --- /dev/null +++ b/internal/clients/gitea/main_test.go @@ -0,0 +1,14 @@ +package gitea + +import ( + "io/ioutil" + "log" + "os" + "testing" +) + +// SETUP: mute logs +func TestMain(m *testing.M) { + log.SetOutput(ioutil.Discard) + os.Exit(m.Run()) +}