package gogithub_test import ( "context" "errors" "fmt" "net/http" "net/url" "reflect" "testing" "github.com/golang/mock/gomock" "github.com/google/go-github/v35/github" . "github.com/onsi/gomega" "github.com/aws/eks-anywhere/pkg/git" "github.com/aws/eks-anywhere/pkg/git/gogithub" mockGoGithub "github.com/aws/eks-anywhere/pkg/git/gogithub/mocks" ) const repoPermissions = "repo" func TestGoGithubCreateRepo(t *testing.T) { type fields struct { opts gogithub.Options } type args struct { opts git.CreateRepoOpts } tests := []struct { name string fields fields args args wantErr bool }{ { name: "create public repo with organizational owner", fields: fields{ opts: gogithub.Options{}, }, args: args{ opts: git.CreateRepoOpts{ Name: "testrepo", Description: "unit test repo", Owner: "testorganization", Personal: false, }, }, }, { name: "create personal repo", fields: fields{ opts: gogithub.Options{}, }, args: args{ opts: git.CreateRepoOpts{ Name: "testrepo", Description: "unit test repo", Owner: "testuser", Personal: true, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() mockCtrl := gomock.NewController(t) client := mockGoGithub.NewMockClient(mockCtrl) returnRepo := &github.Repository{ Name: &tt.args.opts.Name, CloneURL: &tt.args.opts.Description, Owner: &github.User{ Name: &tt.args.opts.Owner, }, Organization: &github.Organization{ Name: &tt.args.opts.Name, }, } client.EXPECT().CreateRepo(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(arg0 context.Context, org string, arg2 *github.Repository) { returnRepo.Organization.Name = &org }).Return( returnRepo, nil, nil) g := &gogithub.GoGithub{ Opts: tt.fields.opts, Client: client, } gotRepository, err := g.CreateRepo(ctx, tt.args.opts) if (err != nil) != tt.wantErr { t.Errorf("CreateRepo() error = %v, wantErr %v", err, tt.wantErr) return } wantRepository := &git.Repository{ Name: returnRepo.GetName(), Organization: returnRepo.GetOrganization().GetName(), CloneUrl: returnRepo.GetCloneURL(), Owner: returnRepo.GetOwner().GetName(), } if tt.args.opts.Personal && gotRepository.Organization != "" { t.Errorf("for personal account org should be empty") } if !reflect.DeepEqual(gotRepository, wantRepository) { t.Errorf("CreateRepo() gotRepository = %v, want %v", gotRepository, wantRepository) } }) } } func TestGoGithubGetRepo(t *testing.T) { type fields struct { opts gogithub.Options } type args struct { opts git.GetRepoOpts name string cloneURL string orgName string } tests := []struct { name string fields fields args args wantErr bool throwError error matchError error }{ { name: "Repo no error", args: args{ opts: git.GetRepoOpts{ Owner: "owner1", Repository: "repo1", }, cloneURL: "url1", name: "repo1", orgName: "org1", }, wantErr: false, }, { name: "github client threw generic error", args: args{ opts: git.GetRepoOpts{ Owner: "owner1", Repository: "repo1", }, cloneURL: "url1", name: "repo1", orgName: "org1", }, wantErr: true, throwError: fmt.Errorf("github client threw error"), matchError: fmt.Errorf("unexpected error when describing repository %s: %w", "repo1", fmt.Errorf("github client threw error")), }, { name: "github threw 404 error", args: args{ opts: git.GetRepoOpts{ Owner: "owner1", Repository: "repo1", }, cloneURL: "url1", name: "repo1", orgName: "org1", }, wantErr: true, throwError: notFoundError(), matchError: &git.RepositoryDoesNotExistError{Err: fmt.Errorf("GET : 404 Not found []")}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() mockCtrl := gomock.NewController(t) client := mockGoGithub.NewMockClient(mockCtrl) returnRepo := &github.Repository{ Name: &tt.args.name, CloneURL: &tt.args.cloneURL, Owner: &github.User{ Name: &tt.args.opts.Owner, }, Organization: &github.Organization{ Name: &tt.args.orgName, }, } client.EXPECT().Repo(ctx, tt.args.opts.Owner, tt.args.opts.Repository). Return(returnRepo, nil, tt.throwError) g := &gogithub.GoGithub{ Opts: tt.fields.opts, Client: client, } got, err := g.GetRepo(ctx, tt.args.opts) wantRepository := &git.Repository{ Name: tt.args.name, Organization: tt.args.orgName, CloneUrl: tt.args.cloneURL, Owner: tt.args.opts.Owner, } if (err != nil) != tt.wantErr { t.Errorf("Repo() error = %v, wantErr %v", err, tt.wantErr) return } if tt.wantErr { switch tt.matchError.(type) { case *git.RepositoryDoesNotExistError: _, typeMatches := err.(*git.RepositoryDoesNotExistError) if !typeMatches || err.Error() != tt.matchError.Error() { t.Errorf("Repo() error = %v, matchError %v", err, tt.matchError) } default: if !reflect.DeepEqual(err, tt.matchError) { t.Errorf("Repo() error = %v, matchError %v", err, tt.matchError) } } } if !tt.wantErr && !reflect.DeepEqual(got, wantRepository) { t.Errorf("Repo() got = %v, want %v", got, wantRepository) } }) } } func TestGoGithubDeleteRepoSuccess(t *testing.T) { type fields struct { opts gogithub.Options } tests := []struct { name string fields fields args git.DeleteRepoOpts wantErr error }{ { name: "github repo deleted successfully", args: git.DeleteRepoOpts{ Owner: "owner1", Repository: "repo1", }, wantErr: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() mockCtrl := gomock.NewController(t) client := mockGoGithub.NewMockClient(mockCtrl) client.EXPECT().DeleteRepo(ctx, tt.args.Owner, tt.args.Repository).Return(nil, tt.wantErr) g := &gogithub.GoGithub{ Opts: tt.fields.opts, Client: client, } err := g.DeleteRepo(ctx, tt.args) if err != tt.wantErr { t.Errorf("DeleteRepo() got error: %v want error: %v", err, tt.wantErr) } }) } } func TestGoGithubDeleteRepoFail(t *testing.T) { type fields struct { opts gogithub.Options } tests := []struct { name string fields fields args git.DeleteRepoOpts wantErr error throwErr error }{ { name: "github repo delete fail", args: git.DeleteRepoOpts{ Owner: "owner1", Repository: "repo1", }, wantErr: fmt.Errorf("deleting repository repo1: github client threw error"), throwErr: fmt.Errorf("github client threw error"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() mockCtrl := gomock.NewController(t) client := mockGoGithub.NewMockClient(mockCtrl) client.EXPECT().DeleteRepo(ctx, tt.args.Owner, tt.args.Repository).Return(nil, tt.throwErr) g := &gogithub.GoGithub{ Opts: tt.fields.opts, Client: client, } err := g.DeleteRepo(ctx, tt.args) if !reflect.DeepEqual(err, tt.wantErr) { t.Errorf("DeleteRepo() got error: %v want error: %v", err, tt.wantErr) } }) } } func TestGoGithubAddDeployKeySuccess(t *testing.T) { type fields struct { opts gogithub.Options } tests := []struct { name string fields fields args git.AddDeployKeyOpts wantErr error }{ { name: "github repo deleted successfully", args: git.AddDeployKeyOpts{ Owner: "owner1", Repository: "repo1", Key: "KEY STRING YO", Title: "My test key", ReadOnly: false, }, wantErr: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() mockCtrl := gomock.NewController(t) client := mockGoGithub.NewMockClient(mockCtrl) k := &github.Key{ Key: &tt.args.Key, Title: &tt.args.Title, ReadOnly: &tt.args.ReadOnly, } client.EXPECT().AddDeployKeyToRepo(ctx, tt.args.Owner, tt.args.Repository, k).Return(tt.wantErr) g := &gogithub.GoGithub{ Opts: tt.fields.opts, Client: client, } err := g.AddDeployKeyToRepo(ctx, tt.args) if err != tt.wantErr { t.Errorf("AddDeployKeyToRepo() got error: %v want error: %v", err, tt.wantErr) } }) } } func TestGoGithub_CheckAccessTokenPermissions(t *testing.T) { type fields struct { opts gogithub.Options } tests := []struct { name string allPermissions string fields fields wantErr error }{ { name: "token with repo permissions", allPermissions: "admin, repo", fields: fields{ opts: gogithub.Options{}, }, wantErr: nil, }, { name: "token without repo permissions", allPermissions: "admin, workflow", fields: fields{ opts: gogithub.Options{}, }, wantErr: errors.New("github access token does not have repo permissions"), }, { name: "token with repo permissions", allPermissions: "", fields: fields{ opts: gogithub.Options{}, }, wantErr: errors.New("github access token does not have repo permissions"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockCtrl := gomock.NewController(t) client := mockGoGithub.NewMockClient(mockCtrl) g := &gogithub.GoGithub{ Opts: tt.fields.opts, Client: client, } gotError := g.CheckAccessTokenPermissions(repoPermissions, tt.allPermissions) if !reflect.DeepEqual(gotError, tt.wantErr) { t.Errorf("Test %v\n got %v\n want %v", tt.name, gotError, tt.wantErr) } }) } } func TestPathExistsError(t *testing.T) { tt := newTest(t) owner, repo, branch, path := pathArgs() tt.client.EXPECT().GetContents( tt.ctx, owner, repo, path, &github.RepositoryContentGetOptions{Ref: branch}, ).Return(nil, nil, nil, errors.New("can't get content")) _, err := tt.g.PathExists(tt.ctx, owner, repo, branch, path) tt.Expect(err).To(HaveOccurred()) } func TestPathExistsItDoes(t *testing.T) { tt := newTest(t) owner, repo, branch, path := pathArgs() tt.client.EXPECT().GetContents( tt.ctx, owner, repo, path, &github.RepositoryContentGetOptions{Ref: branch}, ).Return(nil, nil, nil, notFoundError()) tt.Expect(tt.g.PathExists(tt.ctx, owner, repo, branch, path)).To(BeFalse()) } func TestPathExistsItDoesNot(t *testing.T) { tt := newTest(t) owner, repo, branch, path := pathArgs() tt.client.EXPECT().GetContents( tt.ctx, owner, repo, path, &github.RepositoryContentGetOptions{Ref: branch}, ).Return(nil, nil, nil, nil) tt.Expect(tt.g.PathExists(tt.ctx, owner, repo, branch, path)).To(BeTrue()) } type gogithubTest struct { *WithT g *gogithub.GoGithub opts gogithub.Options client *mockGoGithub.MockClient ctx context.Context } func newTest(t *testing.T) *gogithubTest { withT := NewWithT(t) ctx := context.Background() ctrl := gomock.NewController(t) client := mockGoGithub.NewMockClient(ctrl) opts := gogithub.Options{ Auth: git.TokenAuth{ Username: "user", Token: "token", }, } g := &gogithub.GoGithub{ Opts: opts, Client: client, } return &gogithubTest{ WithT: withT, g: g, opts: opts, client: client, ctx: ctx, } } func pathArgs() (owner, repo, branch, path string) { path = "fluxFolder" branch = "main" owner = "aws" repo = "eksa-gitops" return owner, repo, path, branch } func notFoundError() error { return &github.ErrorResponse{ Message: "Not found", Response: &http.Response{ StatusCode: http.StatusNotFound, Request: &http.Request{ Method: "GET", URL: &url.URL{}, }, }, } }