github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/auth/refresh/refresh_test.go (about) 1 package refresh 2 3 import ( 4 "bytes" 5 "io" 6 "net/http" 7 "strings" 8 "testing" 9 10 "github.com/ungtb10d/cli/v2/internal/config" 11 "github.com/ungtb10d/cli/v2/internal/prompter" 12 "github.com/ungtb10d/cli/v2/pkg/cmdutil" 13 "github.com/ungtb10d/cli/v2/pkg/httpmock" 14 "github.com/ungtb10d/cli/v2/pkg/iostreams" 15 "github.com/google/shlex" 16 "github.com/stretchr/testify/assert" 17 ) 18 19 // TODO prompt cfg test 20 21 func Test_NewCmdRefresh(t *testing.T) { 22 tests := []struct { 23 name string 24 cli string 25 wants RefreshOptions 26 wantsErr bool 27 tty bool 28 neverPrompt bool 29 }{ 30 { 31 name: "tty no arguments", 32 tty: true, 33 wants: RefreshOptions{ 34 Hostname: "", 35 }, 36 }, 37 { 38 name: "nontty no arguments", 39 wantsErr: true, 40 }, 41 { 42 name: "nontty hostname", 43 cli: "-h aline.cedrac", 44 wants: RefreshOptions{ 45 Hostname: "aline.cedrac", 46 }, 47 }, 48 { 49 name: "tty hostname", 50 tty: true, 51 cli: "-h aline.cedrac", 52 wants: RefreshOptions{ 53 Hostname: "aline.cedrac", 54 }, 55 }, 56 { 57 name: "prompts disabled, no args", 58 tty: true, 59 cli: "", 60 neverPrompt: true, 61 wantsErr: true, 62 }, 63 { 64 name: "prompts disabled, hostname", 65 tty: true, 66 cli: "-h aline.cedrac", 67 neverPrompt: true, 68 wants: RefreshOptions{ 69 Hostname: "aline.cedrac", 70 }, 71 }, 72 { 73 name: "tty one scope", 74 tty: true, 75 cli: "--scopes repo:invite", 76 wants: RefreshOptions{ 77 Scopes: []string{"repo:invite"}, 78 }, 79 }, 80 { 81 name: "tty scopes", 82 tty: true, 83 cli: "--scopes repo:invite,read:public_key", 84 wants: RefreshOptions{ 85 Scopes: []string{"repo:invite", "read:public_key"}, 86 }, 87 }, 88 } 89 90 for _, tt := range tests { 91 t.Run(tt.name, func(t *testing.T) { 92 ios, _, _, _ := iostreams.Test() 93 f := &cmdutil.Factory{ 94 IOStreams: ios, 95 } 96 ios.SetStdinTTY(tt.tty) 97 ios.SetStdoutTTY(tt.tty) 98 ios.SetNeverPrompt(tt.neverPrompt) 99 100 argv, err := shlex.Split(tt.cli) 101 assert.NoError(t, err) 102 103 var gotOpts *RefreshOptions 104 cmd := NewCmdRefresh(f, func(opts *RefreshOptions) error { 105 gotOpts = opts 106 return nil 107 }) 108 // TODO cobra hack-around 109 cmd.Flags().BoolP("help", "x", false, "") 110 111 cmd.SetArgs(argv) 112 cmd.SetIn(&bytes.Buffer{}) 113 cmd.SetOut(&bytes.Buffer{}) 114 cmd.SetErr(&bytes.Buffer{}) 115 116 _, err = cmd.ExecuteC() 117 if tt.wantsErr { 118 assert.Error(t, err) 119 return 120 } 121 assert.NoError(t, err) 122 assert.Equal(t, tt.wants.Hostname, gotOpts.Hostname) 123 assert.Equal(t, tt.wants.Scopes, gotOpts.Scopes) 124 }) 125 } 126 } 127 128 type authArgs struct { 129 hostname string 130 scopes []string 131 } 132 133 func Test_refreshRun(t *testing.T) { 134 tests := []struct { 135 name string 136 opts *RefreshOptions 137 prompterStubs func(*prompter.PrompterMock) 138 cfgHosts []string 139 oldScopes string 140 wantErr string 141 nontty bool 142 wantAuthArgs authArgs 143 }{ 144 { 145 name: "no hosts configured", 146 opts: &RefreshOptions{}, 147 wantErr: `not logged in to any hosts`, 148 }, 149 { 150 name: "hostname given but dne", 151 cfgHosts: []string{ 152 "github.com", 153 "aline.cedrac", 154 }, 155 opts: &RefreshOptions{ 156 Hostname: "obed.morton", 157 }, 158 wantErr: `not logged in to obed.morton`, 159 }, 160 { 161 name: "hostname provided and is configured", 162 cfgHosts: []string{ 163 "obed.morton", 164 "github.com", 165 }, 166 opts: &RefreshOptions{ 167 Hostname: "obed.morton", 168 }, 169 wantAuthArgs: authArgs{ 170 hostname: "obed.morton", 171 scopes: nil, 172 }, 173 }, 174 { 175 name: "no hostname, one host configured", 176 cfgHosts: []string{ 177 "github.com", 178 }, 179 opts: &RefreshOptions{ 180 Hostname: "", 181 }, 182 wantAuthArgs: authArgs{ 183 hostname: "github.com", 184 scopes: nil, 185 }, 186 }, 187 { 188 name: "no hostname, multiple hosts configured", 189 cfgHosts: []string{ 190 "github.com", 191 "aline.cedrac", 192 }, 193 opts: &RefreshOptions{ 194 Hostname: "", 195 }, 196 prompterStubs: func(pm *prompter.PrompterMock) { 197 pm.SelectFunc = func(_, _ string, opts []string) (int, error) { 198 return prompter.IndexFor(opts, "github.com") 199 } 200 }, 201 wantAuthArgs: authArgs{ 202 hostname: "github.com", 203 scopes: nil, 204 }, 205 }, 206 { 207 name: "scopes provided", 208 cfgHosts: []string{ 209 "github.com", 210 }, 211 opts: &RefreshOptions{ 212 Scopes: []string{"repo:invite", "public_key:read"}, 213 }, 214 wantAuthArgs: authArgs{ 215 hostname: "github.com", 216 scopes: []string{"repo:invite", "public_key:read"}, 217 }, 218 }, 219 { 220 name: "scopes provided", 221 cfgHosts: []string{ 222 "github.com", 223 }, 224 oldScopes: "delete_repo, codespace", 225 opts: &RefreshOptions{ 226 Scopes: []string{"repo:invite", "public_key:read"}, 227 }, 228 wantAuthArgs: authArgs{ 229 hostname: "github.com", 230 scopes: []string{"repo:invite", "public_key:read", "delete_repo", "codespace"}, 231 }, 232 }, 233 } 234 for _, tt := range tests { 235 t.Run(tt.name, func(t *testing.T) { 236 aa := authArgs{} 237 tt.opts.AuthFlow = func(_ config.Config, _ *iostreams.IOStreams, hostname string, scopes []string, interactive bool) error { 238 aa.hostname = hostname 239 aa.scopes = scopes 240 return nil 241 } 242 243 _ = config.StubWriteConfig(t) 244 cfg := config.NewFromString("") 245 for _, hostname := range tt.cfgHosts { 246 cfg.Set(hostname, "oauth_token", "abc123") 247 } 248 tt.opts.Config = func() (config.Config, error) { 249 return cfg, nil 250 } 251 252 ios, _, _, _ := iostreams.Test() 253 ios.SetStdinTTY(!tt.nontty) 254 ios.SetStdoutTTY(!tt.nontty) 255 tt.opts.IO = ios 256 257 httpReg := &httpmock.Registry{} 258 httpReg.Register( 259 httpmock.REST("GET", ""), 260 func(req *http.Request) (*http.Response, error) { 261 statusCode := 200 262 if req.Header.Get("Authorization") != "token abc123" { 263 statusCode = 400 264 } 265 return &http.Response{ 266 Request: req, 267 StatusCode: statusCode, 268 Body: io.NopCloser(strings.NewReader(``)), 269 Header: http.Header{ 270 "X-Oauth-Scopes": {tt.oldScopes}, 271 }, 272 }, nil 273 }, 274 ) 275 tt.opts.HttpClient = &http.Client{Transport: httpReg} 276 277 pm := &prompter.PrompterMock{} 278 if tt.prompterStubs != nil { 279 tt.prompterStubs(pm) 280 } 281 tt.opts.Prompter = pm 282 283 err := refreshRun(tt.opts) 284 if tt.wantErr != "" { 285 if assert.Error(t, err) { 286 assert.Contains(t, err.Error(), tt.wantErr) 287 } 288 } else { 289 assert.NoError(t, err) 290 } 291 292 assert.Equal(t, tt.wantAuthArgs.hostname, aa.hostname) 293 assert.Equal(t, tt.wantAuthArgs.scopes, aa.scopes) 294 }) 295 } 296 }