Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generalize parameter assertions to be more reusable. #118

Merged
merged 1 commit into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
// limitations under the License.
//

//go:build go1.16

package github

import (
Expand Down
103 changes: 54 additions & 49 deletions github/github_mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,49 +14,63 @@
// limitations under the License.
//

//go:build go1.16

package github

import (
"context"
"net/http"
"os"
"reflect"
"testing"

"github.com/google/go-github/v64/github"
"github.com/stretchr/testify/assert"
)

type assertParams struct {
/* number of index/times the production function has been called by production code, zero based. */
callIndex int
/* assertParameters is a slice of booleans that determine whether to assert the parameters passed to the function for
each call to the production function. If the value at the index of the callIndex is true, the actual parameters will be
asserted against the expected parameters. */
assertParameters []bool
/* expectedParameters is a slice of any types of slices that contains the expected parameters that should be passed
to the production function during the running of test. */
expectedParameters []any
}

func (a *assertParams) doAssert(t assert.TestingT, actualParameters []any) {
for i, expParams := range a.expectedParameters {
refExpParams := reflect.ValueOf(expParams)
assert.Equal(t, reflect.Slice, refExpParams.Kind(), "expected parameters must be a slice")
epForThisCall := refExpParams.Index(a.callIndex).Interface()
assert.Equal(t, epForThisCall, actualParameters[i])
}
}

// RepositoriesMock mocks RepositoriesService
type RepositoriesMock struct {
t *testing.T
/* callCount is the number of times the CreateStatus function has been called by production code. */
callCount int
/* assertParameters is a slice of booleans that determine whether to assert the parameters passed to the function for
each call to the CreateStatus function. If the value at the index of the callCount is true, the parameters will be
asserted.
*/
assertParameters []bool
expectedCtx []context.Context
expectedOwner, expectedRepo, expectedRef, expectedUser []string
// expectedOpts *github.ListOptions
expectedCreateStatusRepoStatus []*github.RepoStatus
createStatusRepoStatus []*github.RepoStatus
createStatusResponse []*github.Response
createStatusError []error
isCollaboratorResult bool
isCollaboratorResp *github.Response
isCollaboratorErr error
t *testing.T
assertParamsCreateStatus assertParams
createStatusRepoStatus []*github.RepoStatus
createStatusResponse []*github.Response
createStatusError []error
isCollaboratorResult bool
isCollaboratorResp *github.Response
isCollaboratorErr error
}

var _ RepositoriesService = (*RepositoriesMock)(nil)

func setupMockRepositoriesService(t *testing.T, assertParameters []bool) (mock *RepositoriesMock) {
func setupMockRepositoriesService(t *testing.T, assertParameters []bool, expectedParams []any) (mock *RepositoriesMock) {
mock = &RepositoriesMock{
t: t,
assertParameters: assertParameters,
t: t,
assertParamsCreateStatus: assertParams{
assertParameters: assertParameters,
expectedParameters: expectedParams,
},
}

return mock
}

Expand All @@ -67,35 +81,22 @@ func (r *RepositoriesMock) ListStatuses(ctx context.Context, owner, repo, ref st
}

func (r *RepositoriesMock) CreateStatus(ctx context.Context, owner, repo, ref string, status *github.RepoStatus) (retRepoStatus *github.RepoStatus, createStatusResponse *github.Response, createStatusError error) {
defer func() { r.callCount++ }()
if r.assertParameters != nil && r.assertParameters[r.callCount] {
if r.expectedCtx != nil {
assert.Equal(r.t, r.expectedCtx[r.callCount], ctx)
}
if r.expectedOwner != nil {
assert.Equal(r.t, r.expectedOwner[r.callCount], owner)
}
if r.expectedRepo != nil {
assert.Equal(r.t, r.expectedRepo[r.callCount], repo)
}
if r.expectedRef != nil {
assert.Equal(r.t, r.expectedRef[r.callCount], ref)
}
if r.expectedCreateStatusRepoStatus != nil {
assert.Equal(r.t, r.expectedCreateStatusRepoStatus[r.callCount], status)
}
defer func() { r.assertParamsCreateStatus.callIndex++ }()
if r.assertParamsCreateStatus.assertParameters != nil && r.assertParamsCreateStatus.assertParameters[r.assertParamsCreateStatus.callIndex] {
actualParameters := []any{ctx, owner, repo, ref, status}
r.assertParamsCreateStatus.doAssert(r.t, actualParameters)
}

if r.createStatusRepoStatus != nil {
retRepoStatus = r.createStatusRepoStatus[r.callCount]
retRepoStatus = r.createStatusRepoStatus[r.assertParamsCreateStatus.callIndex]
}

if r.createStatusRepoStatus != nil {
createStatusResponse = r.createStatusResponse[r.callCount]
createStatusResponse = r.createStatusResponse[r.assertParamsCreateStatus.callIndex]
}

if r.createStatusError != nil {
createStatusError = r.createStatusError[r.callCount]
createStatusError = r.createStatusError[r.assertParamsCreateStatus.callIndex]
}
return
}
Expand All @@ -113,13 +114,8 @@ func (r *RepositoriesMock) Get(context.Context, string, string) (*github.Reposit
}, nil, nil
}

//goland:noinspection GoUnusedParameter
func (r *RepositoriesMock) IsCollaborator(ctx context.Context, owner, repo, user string) (bool, *github.Response, error) {
if r.assertParameters != nil && r.assertParameters[r.callCount] {
assert.Equal(r.t, r.expectedCtx[r.callCount], ctx)
assert.Equal(r.t, r.expectedOwner[r.callCount], owner)
assert.Equal(r.t, r.expectedRepo[r.callCount], repo)
assert.Equal(r.t, r.expectedUser[r.callCount], user)
}
return r.isCollaboratorResult, r.isCollaboratorResp, r.isCollaboratorErr
}

Expand Down Expand Up @@ -152,6 +148,8 @@ func (p *PullRequestsMock) ListCommits(ctx context.Context, owner string, repo s
}

type IssuesMock struct {
t *testing.T
assertParamsCreateComment assertParams
mockGetLabel *github.Label
MockGetLabelResponse *github.Response
mockGetLabelError error
Expand Down Expand Up @@ -203,6 +201,11 @@ func (i *IssuesMock) RemoveLabelForIssue(ctx context.Context, owner string, repo

//goland:noinspection GoUnusedParameter
func (i *IssuesMock) CreateComment(ctx context.Context, owner string, repo string, number int, comment *github.IssueComment) (*github.IssueComment, *github.Response, error) {
defer func() { i.assertParamsCreateComment.callIndex++ }()
if i.assertParamsCreateComment.assertParameters != nil && i.assertParamsCreateComment.assertParameters[i.assertParamsCreateComment.callIndex] {
actualParameters := []any{ctx, owner, repo, number, comment}
i.assertParamsCreateComment.doAssert(i.t, actualParameters)
}
return i.mockComment, i.mockCreateCommentResponse, i.mockCreateCommentError
}

Expand Down Expand Up @@ -290,6 +293,8 @@ func (g *GHInterfaceMock) NewClient(httpClient *http.Client) GHClient {
mockResponse: g.PullRequestsMock.mockResponse,
},
Issues: &IssuesMock{
t: g.IssuesMock.t,
assertParamsCreateComment: g.IssuesMock.assertParamsCreateComment,
mockGetLabel: g.IssuesMock.mockGetLabel,
MockGetLabelResponse: g.IssuesMock.MockGetLabelResponse,
mockGetLabelError: g.IssuesMock.mockGetLabelError,
Expand Down
60 changes: 42 additions & 18 deletions github/github_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
// limitations under the License.
//

//go:build go1.16

package github

import (
Expand Down Expand Up @@ -351,7 +349,7 @@ func TestWithFullEnvironment(t *testing.T) {
t.Run("TestHandlePullRequestListCommitsError", func(t *testing.T) {
forcedError := fmt.Errorf("forced ListCommits error")
GHImpl = &GHInterfaceMock{
RepositoriesMock: *setupMockRepositoriesService(t, []bool{false}),
RepositoriesMock: *setupMockRepositoriesService(t, []bool{false}, nil),
PullRequestsMock: PullRequestsMock{
mockListCommitsError: forcedError,
},
Expand Down Expand Up @@ -390,21 +388,26 @@ func TestWithFullEnvironment(t *testing.T) {
t.Run("TestHandlePullRequestListCommitsUnsignedCommit", func(t *testing.T) {
authors := []string{"john", "doe"}

repositoriesMock := *setupMockRepositoriesService(t, []bool{true, true})
repositoriesMock.expectedCtx = []context.Context{context.Background(), context.Background()}

repositoriesMock.expectedCreateStatusRepoStatus = []*github.RepoStatus{
{
State: github.String("pending"),
Description: github.String("Paul Botsco, the CLA verifier is running"),
Context: &MockAppSlug,
},
{
State: github.String("failure"),
Description: github.String("One or more commits haven't met our Quality requirements."),
Context: &MockAppSlug,
},
}
repositoriesMock := *setupMockRepositoriesService(t,
[]bool{true, true},
[]any{
[]context.Context{context.Background(), context.Background()}, // ctx
[]string{"", ""}, // owner
[]string{"", ""}, // repo
[]string{"", ""}, // sha
[]*github.RepoStatus{
{
State: github.String("pending"),
Description: github.String("Paul Botsco, the CLA verifier is running"),
Context: &MockAppSlug,
},
{
State: github.String("failure"),
Description: github.String("One or more commits haven't met our Quality requirements."),
Context: &MockAppSlug,
},
},
})

GHImpl = getGHMock(getMockRepositoryCommits(authors, false), nil, &repositoriesMock)
mockDB, logger := setupMockDB(t, false)
Expand Down Expand Up @@ -542,6 +545,27 @@ func TestHandlePullRequestListCommitsNoAuthor(t *testing.T) {
mockRepositoryCommits: mockRepositoryCommits,
},
IssuesMock: IssuesMock{
t: t,
assertParamsCreateComment: assertParams{
assertParameters: []bool{true},
expectedParameters: []any{
[]context.Context{context.Background()}, // ctx
[]string{""}, // owner
[]string{""}, // repo
[]int{0}, // number
[]*github.IssueComment{
{Body: github.String(
`Thanks for the contribution. Unfortunately some of your commits don't meet our standards. All commits must be signed and have author information set.

The commits to review are:

- <a href="https://github.com">johnSHA</a> - missing author :cop:
- <a href="https://github.com">johnSHA</a> - unsigned commit :key:
`,
)},
}, // comment
},
},
mockGetLabel: &github.Label{},
MockGetLabelResponse: &github.Response{
Response: &http.Response{},
Expand Down
18 changes: 9 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
module github.com/sonatype-nexus-community/the-cla

go 1.21
go 1.22.0

toolchain go1.21.7
toolchain go1.22.7

require (
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/bradleyfalzon/ghinstallation/v2 v2.11.0
github.com/golang-migrate/migrate/v4 v4.17.1
github.com/golang-migrate/migrate/v4 v4.18.1
github.com/google/go-github/v64 v64.0.0
github.com/google/uuid v1.6.0
github.com/joho/godotenv v1.5.1
github.com/labstack/echo/v4 v4.12.0
github.com/stretchr/testify v1.9.0
go.uber.org/zap v1.27.0
golang.org/x/oauth2 v0.21.0
golang.org/x/oauth2 v0.23.0
gopkg.in/go-playground/webhooks.v5 v5.17.0
)

Expand All @@ -35,10 +35,10 @@ require (
github.com/valyala/fasttemplate v1.2.2 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/time v0.6.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading