github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/issue/close/close_test.go (about)

     1  package close
     2  
     3  import (
     4  	"bytes"
     5  	"net/http"
     6  	"testing"
     7  
     8  	fd "github.com/ungtb10d/cli/v2/internal/featuredetection"
     9  	"github.com/ungtb10d/cli/v2/internal/ghrepo"
    10  	"github.com/ungtb10d/cli/v2/pkg/cmdutil"
    11  	"github.com/ungtb10d/cli/v2/pkg/httpmock"
    12  	"github.com/ungtb10d/cli/v2/pkg/iostreams"
    13  	"github.com/google/shlex"
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  func TestNewCmdClose(t *testing.T) {
    18  	tests := []struct {
    19  		name    string
    20  		input   string
    21  		output  CloseOptions
    22  		wantErr bool
    23  		errMsg  string
    24  	}{
    25  		{
    26  			name:    "no argument",
    27  			input:   "",
    28  			wantErr: true,
    29  			errMsg:  "accepts 1 arg(s), received 0",
    30  		},
    31  		{
    32  			name:  "issue number",
    33  			input: "123",
    34  			output: CloseOptions{
    35  				SelectorArg: "123",
    36  			},
    37  		},
    38  		{
    39  			name:  "issue url",
    40  			input: "https://github.com/ungtb10d/cli/3",
    41  			output: CloseOptions{
    42  				SelectorArg: "https://github.com/ungtb10d/cli/3",
    43  			},
    44  		},
    45  		{
    46  			name:  "comment",
    47  			input: "123 --comment 'closing comment'",
    48  			output: CloseOptions{
    49  				SelectorArg: "123",
    50  				Comment:     "closing comment",
    51  			},
    52  		},
    53  		{
    54  			name:  "reason",
    55  			input: "123 --reason 'not planned'",
    56  			output: CloseOptions{
    57  				SelectorArg: "123",
    58  				Reason:      "not planned",
    59  			},
    60  		},
    61  	}
    62  	for _, tt := range tests {
    63  		t.Run(tt.name, func(t *testing.T) {
    64  			ios, _, _, _ := iostreams.Test()
    65  			f := &cmdutil.Factory{
    66  				IOStreams: ios,
    67  			}
    68  			argv, err := shlex.Split(tt.input)
    69  			assert.NoError(t, err)
    70  			var gotOpts *CloseOptions
    71  			cmd := NewCmdClose(f, func(opts *CloseOptions) error {
    72  				gotOpts = opts
    73  				return nil
    74  			})
    75  			cmd.SetArgs(argv)
    76  			cmd.SetIn(&bytes.Buffer{})
    77  			cmd.SetOut(&bytes.Buffer{})
    78  			cmd.SetErr(&bytes.Buffer{})
    79  
    80  			_, err = cmd.ExecuteC()
    81  			if tt.wantErr {
    82  				assert.Error(t, err)
    83  				assert.Equal(t, tt.errMsg, err.Error())
    84  				return
    85  			}
    86  
    87  			assert.NoError(t, err)
    88  			assert.Equal(t, tt.output.SelectorArg, gotOpts.SelectorArg)
    89  			assert.Equal(t, tt.output.Comment, gotOpts.Comment)
    90  			assert.Equal(t, tt.output.Reason, gotOpts.Reason)
    91  		})
    92  	}
    93  }
    94  
    95  func TestCloseRun(t *testing.T) {
    96  	tests := []struct {
    97  		name       string
    98  		opts       *CloseOptions
    99  		httpStubs  func(*httpmock.Registry)
   100  		wantStderr string
   101  		wantErr    bool
   102  		errMsg     string
   103  	}{
   104  		{
   105  			name: "close issue by number",
   106  			opts: &CloseOptions{
   107  				SelectorArg: "13",
   108  			},
   109  			httpStubs: func(reg *httpmock.Registry) {
   110  				reg.Register(
   111  					httpmock.GraphQL(`query IssueByNumber\b`),
   112  					httpmock.StringResponse(`
   113              { "data": { "repository": {
   114                "hasIssuesEnabled": true,
   115                "issue": { "id": "THE-ID", "number": 13, "title": "The title of the issue"}
   116              } } }`),
   117  				)
   118  				reg.Register(
   119  					httpmock.GraphQL(`mutation IssueClose\b`),
   120  					httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
   121  						func(inputs map[string]interface{}) {
   122  							assert.Equal(t, "THE-ID", inputs["issueId"])
   123  						}),
   124  				)
   125  			},
   126  			wantStderr: "✓ Closed issue #13 (The title of the issue)\n",
   127  		},
   128  		{
   129  			name: "close issue with comment",
   130  			opts: &CloseOptions{
   131  				SelectorArg: "13",
   132  				Comment:     "closing comment",
   133  			},
   134  			httpStubs: func(reg *httpmock.Registry) {
   135  				reg.Register(
   136  					httpmock.GraphQL(`query IssueByNumber\b`),
   137  					httpmock.StringResponse(`
   138              { "data": { "repository": {
   139                "hasIssuesEnabled": true,
   140                "issue": { "id": "THE-ID", "number": 13, "title": "The title of the issue"}
   141              } } }`),
   142  				)
   143  				reg.Register(
   144  					httpmock.GraphQL(`mutation CommentCreate\b`),
   145  					httpmock.GraphQLMutation(`
   146              { "data": { "addComment": { "commentEdge": { "node": {
   147                "url": "https://github.com/OWNER/REPO/issues/123#issuecomment-456"
   148              } } } } }`,
   149  						func(inputs map[string]interface{}) {
   150  							assert.Equal(t, "THE-ID", inputs["subjectId"])
   151  							assert.Equal(t, "closing comment", inputs["body"])
   152  						}),
   153  				)
   154  				reg.Register(
   155  					httpmock.GraphQL(`mutation IssueClose\b`),
   156  					httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
   157  						func(inputs map[string]interface{}) {
   158  							assert.Equal(t, "THE-ID", inputs["issueId"])
   159  						}),
   160  				)
   161  			},
   162  			wantStderr: "✓ Closed issue #13 (The title of the issue)\n",
   163  		},
   164  		{
   165  			name: "close issue with reason",
   166  			opts: &CloseOptions{
   167  				SelectorArg: "13",
   168  				Reason:      "not planned",
   169  				Detector:    &fd.EnabledDetectorMock{},
   170  			},
   171  			httpStubs: func(reg *httpmock.Registry) {
   172  				reg.Register(
   173  					httpmock.GraphQL(`query IssueByNumber\b`),
   174  					httpmock.StringResponse(`
   175              { "data": { "repository": {
   176                "hasIssuesEnabled": true,
   177                "issue": { "id": "THE-ID", "number": 13, "title": "The title of the issue"}
   178              } } }`),
   179  				)
   180  				reg.Register(
   181  					httpmock.GraphQL(`mutation IssueClose\b`),
   182  					httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
   183  						func(inputs map[string]interface{}) {
   184  							assert.Equal(t, 2, len(inputs))
   185  							assert.Equal(t, "THE-ID", inputs["issueId"])
   186  							assert.Equal(t, "NOT_PLANNED", inputs["stateReason"])
   187  						}),
   188  				)
   189  			},
   190  			wantStderr: "✓ Closed issue #13 (The title of the issue)\n",
   191  		},
   192  		{
   193  			name: "close issue with reason when reason is not supported",
   194  			opts: &CloseOptions{
   195  				SelectorArg: "13",
   196  				Reason:      "not planned",
   197  				Detector:    &fd.DisabledDetectorMock{},
   198  			},
   199  			httpStubs: func(reg *httpmock.Registry) {
   200  				reg.Register(
   201  					httpmock.GraphQL(`query IssueByNumber\b`),
   202  					httpmock.StringResponse(`
   203              { "data": { "repository": {
   204                "hasIssuesEnabled": true,
   205                "issue": { "id": "THE-ID", "number": 13, "title": "The title of the issue"}
   206              } } }`),
   207  				)
   208  				reg.Register(
   209  					httpmock.GraphQL(`mutation IssueClose\b`),
   210  					httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
   211  						func(inputs map[string]interface{}) {
   212  							assert.Equal(t, 1, len(inputs))
   213  							assert.Equal(t, "THE-ID", inputs["issueId"])
   214  						}),
   215  				)
   216  			},
   217  			wantStderr: "✓ Closed issue #13 (The title of the issue)\n",
   218  		},
   219  		{
   220  			name: "issue already closed",
   221  			opts: &CloseOptions{
   222  				SelectorArg: "13",
   223  			},
   224  			httpStubs: func(reg *httpmock.Registry) {
   225  				reg.Register(
   226  					httpmock.GraphQL(`query IssueByNumber\b`),
   227  					httpmock.StringResponse(`
   228              { "data": { "repository": {
   229                "hasIssuesEnabled": true,
   230                "issue": { "number": 13, "title": "The title of the issue", "state": "CLOSED"}
   231              } } }`),
   232  				)
   233  			},
   234  			wantStderr: "! Issue #13 (The title of the issue) is already closed\n",
   235  		},
   236  		{
   237  			name: "issues disabled",
   238  			opts: &CloseOptions{
   239  				SelectorArg: "13",
   240  			},
   241  			httpStubs: func(reg *httpmock.Registry) {
   242  				reg.Register(
   243  					httpmock.GraphQL(`query IssueByNumber\b`),
   244  					httpmock.StringResponse(`{
   245              "data": { "repository": { "hasIssuesEnabled": false, "issue": null } },
   246              "errors": [ { "type": "NOT_FOUND", "path": [ "repository", "issue" ],
   247              "message": "Could not resolve to an issue or pull request with the number of 13."
   248  					} ] }`),
   249  				)
   250  			},
   251  			wantErr: true,
   252  			errMsg:  "the 'OWNER/REPO' repository has disabled issues",
   253  		},
   254  	}
   255  	for _, tt := range tests {
   256  		reg := &httpmock.Registry{}
   257  		if tt.httpStubs != nil {
   258  			tt.httpStubs(reg)
   259  		}
   260  		tt.opts.HttpClient = func() (*http.Client, error) {
   261  			return &http.Client{Transport: reg}, nil
   262  		}
   263  		ios, _, _, stderr := iostreams.Test()
   264  		tt.opts.IO = ios
   265  		tt.opts.BaseRepo = func() (ghrepo.Interface, error) {
   266  			return ghrepo.FromFullName("OWNER/REPO")
   267  		}
   268  		t.Run(tt.name, func(t *testing.T) {
   269  			defer reg.Verify(t)
   270  
   271  			err := closeRun(tt.opts)
   272  			if tt.wantErr {
   273  				assert.EqualError(t, err, tt.errMsg)
   274  				return
   275  			}
   276  
   277  			assert.NoError(t, err)
   278  			assert.Equal(t, tt.wantStderr, stderr.String())
   279  		})
   280  	}
   281  }