From dcefe340ada6fa887b90f581f5632853f616bd4b Mon Sep 17 00:00:00 2001 From: vaindil Date: Wed, 5 Jul 2023 13:17:50 -0400 Subject: [PATCH] add rs check tests --- pkg/cmd/ruleset/check/check.go | 18 +- pkg/cmd/ruleset/check/check_test.go | 233 ++++++++++++++++++ .../ruleset/check/fixtures/rulesetCheck.json | 62 +++++ 3 files changed, 305 insertions(+), 8 deletions(-) create mode 100644 pkg/cmd/ruleset/check/check_test.go create mode 100644 pkg/cmd/ruleset/check/fixtures/rulesetCheck.json diff --git a/pkg/cmd/ruleset/check/check.go b/pkg/cmd/ruleset/check/check.go index 53a73efb1..3007fe36a 100644 --- a/pkg/cmd/ruleset/check/check.go +++ b/pkg/cmd/ruleset/check/check.go @@ -46,12 +46,12 @@ func NewCmdCheck(f *cmdutil.Factory, runF func(*CheckOptions) error) *cobra.Comm Long: heredoc.Doc(` View information about GitHub rules that apply to a given branch. - The provided branch name does not need to exist, rules will be displayed that would apply + The provided branch name does not need to exist; rules will be displayed that would apply to a branch with that name. All rules are returned regardless of where they are configured. If no branch name is provided, then the current branch will be used. - The --default flag can be used to view rules that apply to the default branch of the current + The --default flag can be used to view rules that apply to the default branch of the repository. `), Example: heredoc.Doc(` @@ -96,6 +96,7 @@ func NewCmdCheck(f *cmdutil.Factory, runF func(*CheckOptions) error) *cobra.Comm } cmd.Flags().BoolVar(&opts.Default, "default", false, "Check rules on default branch") + cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "Open the branch rules page in a web browser") return cmd } @@ -109,7 +110,7 @@ func checkRun(opts *CheckOptions) error { repoI, err := opts.BaseRepo() if err != nil { - return err + return fmt.Errorf("could not determine repo to use: %w", err) } git := opts.Git @@ -129,15 +130,16 @@ func checkRun(opts *CheckOptions) error { } } - rawPath := fmt.Sprintf("rules?ref=%s%s", url.QueryEscape("refs/heads/"), url.QueryEscape(opts.Branch)) - rulesURL := ghrepo.GenerateRepoURL(repoI, rawPath) - if opts.WebMode { + // the query string parameter may have % signs in it, so it must be carefully used with Printf functions + queryString := fmt.Sprintf("?ref=%s", url.QueryEscape("refs/heads/"+opts.Branch)) + rawUrl := ghrepo.GenerateRepoURL(repoI, "rules") + if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "Opening %s in your browser.\n", text.DisplayURL(rulesURL)) + fmt.Fprintf(opts.IO.Out, "Opening %s in your browser.\n", text.DisplayURL(rawUrl)) } - return opts.Browser.Browse(rulesURL) + return opts.Browser.Browse(rawUrl + queryString) } var rules []shared.RulesetRule diff --git a/pkg/cmd/ruleset/check/check_test.go b/pkg/cmd/ruleset/check/check_test.go new file mode 100644 index 000000000..2d9dffae4 --- /dev/null +++ b/pkg/cmd/ruleset/check/check_test.go @@ -0,0 +1,233 @@ +package check + +import ( + "bytes" + "io" + "net/http" + "testing" + + "github.com/MakeNowJust/heredoc" + "github.com/cli/cli/v2/internal/browser" + "github.com/cli/cli/v2/internal/ghrepo" + "github.com/cli/cli/v2/pkg/cmdutil" + "github.com/cli/cli/v2/pkg/httpmock" + "github.com/cli/cli/v2/pkg/iostreams" + "github.com/google/shlex" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_NewCmdCheck(t *testing.T) { + tests := []struct { + name string + args string + isTTY bool + want CheckOptions + wantErr string + }{ + { + name: "no arguments", + args: "", + isTTY: true, + want: CheckOptions{ + Branch: "", + Default: false, + WebMode: false, + }, + }, + { + name: "branch name", + args: "my-branch", + isTTY: true, + want: CheckOptions{ + Branch: "my-branch", + Default: false, + WebMode: false, + }, + }, + { + name: "default", + args: "--default=true", + isTTY: true, + want: CheckOptions{ + Branch: "", + Default: true, + WebMode: false, + }, + }, + { + name: "web mode", + args: "--web", + isTTY: true, + want: CheckOptions{ + Branch: "", + Default: false, + WebMode: true, + }, + }, + { + name: "both --default and branch name specified", + args: "--default asdf", + isTTY: true, + wantErr: "specify only one of `--default` or a branch name", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ios, _, _, _ := iostreams.Test() + ios.SetStdoutTTY(tt.isTTY) + ios.SetStdinTTY(tt.isTTY) + ios.SetStderrTTY(tt.isTTY) + + f := &cmdutil.Factory{ + IOStreams: ios, + } + + var opts *CheckOptions + cmd := NewCmdCheck(f, func(o *CheckOptions) error { + opts = o + return nil + }) + cmd.PersistentFlags().StringP("repo", "R", "", "") + + argv, err := shlex.Split(tt.args) + require.NoError(t, err) + cmd.SetArgs(argv) + + cmd.SetIn(&bytes.Buffer{}) + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + + _, err = cmd.ExecuteC() + if tt.wantErr != "" { + require.EqualError(t, err, tt.wantErr) + return + } else { + require.NoError(t, err) + } + + assert.Equal(t, tt.want.Branch, opts.Branch) + assert.Equal(t, tt.want.Default, opts.Default) + assert.Equal(t, tt.want.WebMode, opts.WebMode) + }) + } +} + +func Test_checkRun(t *testing.T) { + tests := []struct { + name string + isTTY bool + opts CheckOptions + wantErr string + wantStdout string + wantStderr string + wantBrowse string + }{ + { + name: "view rules for branch", + isTTY: true, + opts: CheckOptions{ + Branch: "my-branch", + }, + wantStdout: heredoc.Doc(` + 6 rules apply to branch my-branch in repo my-org/repo-name + + - commit_author_email_pattern: [name: ] [negate: false] [operator: ends_with] [pattern: @example.com] + (configured in ruleset 1234 from organization my-org) + + - commit_author_email_pattern: [name: ] [negate: false] [operator: ends_with] [pattern: @example.com] + (configured in ruleset 5678 from repository my-org/repo-name) + + - commit_message_pattern: [name: ] [negate: false] [operator: starts_with] [pattern: fff] + (configured in ruleset 1234 from organization my-org) + + - commit_message_pattern: [name: ] [negate: false] [operator: contains] [pattern: asdf] + (configured in ruleset 5678 from repository my-org/repo-name) + + - creation + (configured in ruleset 5678 from repository my-org/repo-name) + + - required_signatures + (configured in ruleset 1234 from organization my-org) + + `), + wantStderr: "", + wantBrowse: "", + }, + { + name: "web mode, TTY", + isTTY: true, + opts: CheckOptions{ + Branch: "my-branch", + WebMode: true, + }, + wantStdout: "Opening github.com/my-org/repo-name/rules in your browser.\n", + wantStderr: "", + wantBrowse: "https://github.com/my-org/repo-name/rules?ref=refs%2Fheads%2Fmy-branch", + }, + { + name: "web mode, TTY, special character in branch name", + isTTY: true, + opts: CheckOptions{ + Branch: "my-feature/my-branch", + WebMode: true, + }, + wantStdout: "Opening github.com/my-org/repo-name/rules in your browser.\n", + wantStderr: "", + wantBrowse: "https://github.com/my-org/repo-name/rules?ref=refs%2Fheads%2Fmy-feature%2Fmy-branch", + }, + { + name: "web mode, non-TTY", + isTTY: false, + opts: CheckOptions{ + Branch: "my-branch", + WebMode: true, + }, + wantStdout: "", + wantStderr: "", + wantBrowse: "https://github.com/my-org/repo-name/rules?ref=refs%2Fheads%2Fmy-branch", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ios, _, stdout, stderr := iostreams.Test() + ios.SetStdoutTTY(tt.isTTY) + ios.SetStdinTTY(tt.isTTY) + ios.SetStderrTTY(tt.isTTY) + + fakeHTTP := &httpmock.Registry{} + fakeHTTP.Register( + httpmock.REST("GET", "repos/my-org/repo-name/rules/branches/my-branch"), + httpmock.FileResponse("./fixtures/rulesetCheck.json"), + ) + + tt.opts.IO = ios + tt.opts.HttpClient = func() (*http.Client, error) { + return &http.Client{Transport: fakeHTTP}, nil + } + tt.opts.BaseRepo = func() (ghrepo.Interface, error) { + return ghrepo.FromFullName("my-org/repo-name") + } + browser := &browser.Stub{} + tt.opts.Browser = browser + + err := checkRun(&tt.opts) + + if tt.wantErr != "" { + require.EqualError(t, err, tt.wantErr) + return + } else { + require.NoError(t, err) + } + + if tt.wantBrowse != "" { + browser.Verify(t, tt.wantBrowse) + } + + assert.Equal(t, tt.wantStdout, stdout.String()) + assert.Equal(t, tt.wantStderr, stderr.String()) + }) + } +} diff --git a/pkg/cmd/ruleset/check/fixtures/rulesetCheck.json b/pkg/cmd/ruleset/check/fixtures/rulesetCheck.json new file mode 100644 index 000000000..c02fddef4 --- /dev/null +++ b/pkg/cmd/ruleset/check/fixtures/rulesetCheck.json @@ -0,0 +1,62 @@ +[ + { + "type": "commit_author_email_pattern", + "parameters": { + "name": "", + "negate": false, + "pattern": "@example.com", + "operator": "ends_with" + }, + "ruleset_source_type": "Organization", + "ruleset_source": "my-org", + "ruleset_id": 1234 + }, + { + "type": "commit_message_pattern", + "parameters": { + "name": "", + "negate": false, + "pattern": "fff", + "operator": "starts_with" + }, + "ruleset_source_type": "Organization", + "ruleset_source": "my-org", + "ruleset_id": 1234 + }, + { + "type": "required_signatures", + "ruleset_source_type": "Organization", + "ruleset_source": "my-org", + "ruleset_id": 1234 + }, + { + "type": "commit_message_pattern", + "parameters": { + "name": "", + "negate": false, + "pattern": "asdf", + "operator": "contains" + }, + "ruleset_source_type": "Repository", + "ruleset_source": "my-org/repo-name", + "ruleset_id": 5678 + }, + { + "type": "commit_author_email_pattern", + "parameters": { + "name": "", + "negate": false, + "pattern": "@example.com", + "operator": "ends_with" + }, + "ruleset_source_type": "Repository", + "ruleset_source": "my-org/repo-name", + "ruleset_id": 5678 + }, + { + "type": "creation", + "ruleset_source_type": "Repository", + "ruleset_source": "my-org/repo-name", + "ruleset_id": 5678 + } +]