github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/repo/edit/edit_test.go (about) 1 package edit 2 3 import ( 4 "bytes" 5 "context" 6 "io" 7 "net/http" 8 "testing" 9 10 "github.com/ungtb10d/cli/v2/pkg/prompt" 11 12 "github.com/ungtb10d/cli/v2/internal/ghrepo" 13 "github.com/ungtb10d/cli/v2/pkg/cmdutil" 14 "github.com/ungtb10d/cli/v2/pkg/httpmock" 15 "github.com/ungtb10d/cli/v2/pkg/iostreams" 16 "github.com/google/shlex" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 ) 20 21 func TestNewCmdEdit(t *testing.T) { 22 tests := []struct { 23 name string 24 args string 25 wantOpts EditOptions 26 wantErr string 27 }{ 28 { 29 name: "change repo description", 30 args: "--description hello", 31 wantOpts: EditOptions{ 32 Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"), 33 Edits: EditRepositoryInput{ 34 Description: sp("hello"), 35 }, 36 }, 37 }, 38 } 39 40 for _, tt := range tests { 41 t.Run(tt.name, func(t *testing.T) { 42 ios, _, _, _ := iostreams.Test() 43 ios.SetStdoutTTY(true) 44 ios.SetStdinTTY(true) 45 ios.SetStderrTTY(true) 46 47 f := &cmdutil.Factory{ 48 IOStreams: ios, 49 BaseRepo: func() (ghrepo.Interface, error) { 50 return ghrepo.New("OWNER", "REPO"), nil 51 }, 52 HttpClient: func() (*http.Client, error) { 53 return nil, nil 54 }, 55 } 56 57 argv, err := shlex.Split(tt.args) 58 assert.NoError(t, err) 59 60 var gotOpts *EditOptions 61 cmd := NewCmdEdit(f, func(opts *EditOptions) error { 62 gotOpts = opts 63 return nil 64 }) 65 cmd.Flags().BoolP("help", "x", false, "") 66 67 cmd.SetArgs(argv) 68 cmd.SetIn(&bytes.Buffer{}) 69 cmd.SetOut(io.Discard) 70 cmd.SetErr(io.Discard) 71 72 _, err = cmd.ExecuteC() 73 if tt.wantErr != "" { 74 assert.EqualError(t, err, tt.wantErr) 75 return 76 } 77 require.NoError(t, err) 78 79 assert.Equal(t, ghrepo.FullName(tt.wantOpts.Repository), ghrepo.FullName(gotOpts.Repository)) 80 assert.Equal(t, tt.wantOpts.Edits, gotOpts.Edits) 81 }) 82 } 83 } 84 85 func Test_editRun(t *testing.T) { 86 tests := []struct { 87 name string 88 opts EditOptions 89 httpStubs func(*testing.T, *httpmock.Registry) 90 wantsStderr string 91 wantsErr string 92 }{ 93 { 94 name: "change name and description", 95 opts: EditOptions{ 96 Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"), 97 Edits: EditRepositoryInput{ 98 Homepage: sp("newURL"), 99 Description: sp("hello world!"), 100 }, 101 }, 102 httpStubs: func(t *testing.T, r *httpmock.Registry) { 103 r.Register( 104 httpmock.REST("PATCH", "repos/OWNER/REPO"), 105 httpmock.RESTPayload(200, `{}`, func(payload map[string]interface{}) { 106 assert.Equal(t, 2, len(payload)) 107 assert.Equal(t, "newURL", payload["homepage"]) 108 assert.Equal(t, "hello world!", payload["description"]) 109 })) 110 }, 111 }, 112 { 113 name: "add and remove topics", 114 opts: EditOptions{ 115 Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"), 116 AddTopics: []string{"topic1", "topic2"}, 117 RemoveTopics: []string{"topic3"}, 118 }, 119 httpStubs: func(t *testing.T, r *httpmock.Registry) { 120 r.Register( 121 httpmock.REST("GET", "repos/OWNER/REPO/topics"), 122 httpmock.StringResponse(`{"names":["topic2", "topic3", "go"]}`)) 123 r.Register( 124 httpmock.REST("PUT", "repos/OWNER/REPO/topics"), 125 httpmock.RESTPayload(200, `{}`, func(payload map[string]interface{}) { 126 assert.Equal(t, 1, len(payload)) 127 assert.Equal(t, []interface{}{"topic2", "go", "topic1"}, payload["names"]) 128 })) 129 }, 130 }, 131 } 132 133 for _, tt := range tests { 134 t.Run(tt.name, func(t *testing.T) { 135 ios, _, _, _ := iostreams.Test() 136 ios.SetStdoutTTY(true) 137 ios.SetStdinTTY(true) 138 ios.SetStderrTTY(true) 139 140 httpReg := &httpmock.Registry{} 141 defer httpReg.Verify(t) 142 if tt.httpStubs != nil { 143 tt.httpStubs(t, httpReg) 144 } 145 146 opts := &tt.opts 147 opts.HTTPClient = &http.Client{Transport: httpReg} 148 opts.IO = ios 149 err := editRun(context.Background(), opts) 150 if tt.wantsErr == "" { 151 require.NoError(t, err) 152 } else { 153 assert.EqualError(t, err, tt.wantsErr) 154 return 155 } 156 }) 157 } 158 } 159 160 func Test_editRun_interactive(t *testing.T) { 161 tests := []struct { 162 name string 163 opts EditOptions 164 askStubs func(*prompt.AskStubber) 165 httpStubs func(*testing.T, *httpmock.Registry) 166 wantsStderr string 167 wantsErr string 168 }{ 169 { 170 name: "updates repo description", 171 opts: EditOptions{ 172 Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"), 173 InteractiveMode: true, 174 }, 175 askStubs: func(as *prompt.AskStubber) { 176 as.StubPrompt("What do you want to edit?").AnswerWith([]string{"Description"}) 177 as.StubPrompt("Description of the repository").AnswerWith("awesome repo description") 178 }, 179 httpStubs: func(t *testing.T, reg *httpmock.Registry) { 180 reg.Register( 181 httpmock.GraphQL(`query RepositoryInfo\b`), 182 httpmock.StringResponse(` 183 { 184 "data": { 185 "repository": { 186 "description": "old description", 187 "homePageUrl": "https://url.com", 188 "defaultBranchRef": { 189 "name": "main" 190 }, 191 "isInOrganization": false, 192 "repositoryTopics": { 193 "nodes": [{ 194 "topic": { 195 "name": "x" 196 } 197 }] 198 } 199 } 200 } 201 }`)) 202 reg.Register( 203 httpmock.REST("PATCH", "repos/OWNER/REPO"), 204 httpmock.RESTPayload(200, `{}`, func(payload map[string]interface{}) { 205 assert.Equal(t, "awesome repo description", payload["description"]) 206 })) 207 }, 208 }, 209 { 210 name: "updates repo topics", 211 opts: EditOptions{ 212 Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"), 213 InteractiveMode: true, 214 }, 215 askStubs: func(as *prompt.AskStubber) { 216 as.StubPrompt("What do you want to edit?").AnswerWith([]string{"Description", "Topics"}) 217 as.StubPrompt("Description of the repository").AnswerWith("awesome repo description") 218 as.StubPrompt("Add topics?(csv format)").AnswerWith("a, b,c,d ") 219 as.StubPrompt("Remove Topics").AnswerWith([]string{"x"}) 220 }, 221 httpStubs: func(t *testing.T, reg *httpmock.Registry) { 222 reg.Register( 223 httpmock.GraphQL(`query RepositoryInfo\b`), 224 httpmock.StringResponse(` 225 { 226 "data": { 227 "repository": { 228 "description": "old description", 229 "homePageUrl": "https://url.com", 230 "defaultBranchRef": { 231 "name": "main" 232 }, 233 "isInOrganization": false, 234 "repositoryTopics": { 235 "nodes": [{ 236 "topic": { 237 "name": "x" 238 } 239 }] 240 } 241 } 242 } 243 }`)) 244 reg.Register( 245 httpmock.REST("PATCH", "repos/OWNER/REPO"), 246 httpmock.RESTPayload(200, `{}`, func(payload map[string]interface{}) { 247 assert.Equal(t, "awesome repo description", payload["description"]) 248 })) 249 reg.Register( 250 httpmock.REST("PUT", "repos/OWNER/REPO/topics"), 251 httpmock.RESTPayload(200, `{}`, func(payload map[string]interface{}) { 252 assert.Equal(t, []interface{}{"a", "b", "c", "d"}, payload["names"]) 253 })) 254 }, 255 }, 256 { 257 name: "updates repo merge options", 258 opts: EditOptions{ 259 Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"), 260 InteractiveMode: true, 261 }, 262 askStubs: func(as *prompt.AskStubber) { 263 as.StubPrompt("What do you want to edit?").AnswerWith([]string{"Merge Options"}) 264 as.StubPrompt("Allowed merge strategies").AnswerWith([]string{allowMergeCommits, allowRebaseMerge}) 265 as.StubPrompt("Enable Auto Merge?").AnswerWith(false) 266 as.StubPrompt("Automatically delete head branches after merging?").AnswerWith(false) 267 }, 268 httpStubs: func(t *testing.T, reg *httpmock.Registry) { 269 reg.Register( 270 httpmock.GraphQL(`query RepositoryInfo\b`), 271 httpmock.StringResponse(` 272 { 273 "data": { 274 "repository": { 275 "description": "old description", 276 "homePageUrl": "https://url.com", 277 "defaultBranchRef": { 278 "name": "main" 279 }, 280 "isInOrganization": false, 281 "squashMergeAllowed": false, 282 "rebaseMergeAllowed": true, 283 "mergeCommitAllowed": true, 284 "deleteBranchOnMerge": false, 285 "repositoryTopics": { 286 "nodes": [{ 287 "topic": { 288 "name": "x" 289 } 290 }] 291 } 292 } 293 } 294 }`)) 295 reg.Register( 296 httpmock.REST("PATCH", "repos/OWNER/REPO"), 297 httpmock.RESTPayload(200, `{}`, func(payload map[string]interface{}) { 298 assert.Equal(t, true, payload["allow_merge_commit"]) 299 assert.Equal(t, false, payload["allow_squash_merge"]) 300 assert.Equal(t, true, payload["allow_rebase_merge"]) 301 })) 302 }, 303 }, 304 } 305 306 for _, tt := range tests { 307 t.Run(tt.name, func(t *testing.T) { 308 ios, _, _, _ := iostreams.Test() 309 ios.SetStdoutTTY(true) 310 ios.SetStdinTTY(true) 311 ios.SetStderrTTY(true) 312 313 httpReg := &httpmock.Registry{} 314 defer httpReg.Verify(t) 315 if tt.httpStubs != nil { 316 tt.httpStubs(t, httpReg) 317 } 318 319 opts := &tt.opts 320 opts.HTTPClient = &http.Client{Transport: httpReg} 321 opts.IO = ios 322 //nolint:staticcheck // SA1019: prompt.NewAskStubber is deprecated: use PrompterMock 323 as := prompt.NewAskStubber(t) 324 if tt.askStubs != nil { 325 tt.askStubs(as) 326 } 327 328 err := editRun(context.Background(), opts) 329 if tt.wantsErr == "" { 330 require.NoError(t, err) 331 } else { 332 assert.EqualError(t, err, tt.wantsErr) 333 return 334 } 335 }) 336 } 337 } 338 339 func sp(v string) *string { 340 return &v 341 }