github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/auth/shared/login_flow_test.go (about) 1 package shared 2 3 import ( 4 "fmt" 5 "net/http" 6 "os" 7 "path/filepath" 8 "testing" 9 10 "github.com/MakeNowJust/heredoc" 11 "github.com/ungtb10d/cli/v2/internal/prompter" 12 "github.com/ungtb10d/cli/v2/internal/run" 13 "github.com/ungtb10d/cli/v2/pkg/httpmock" 14 "github.com/ungtb10d/cli/v2/pkg/iostreams" 15 "github.com/ungtb10d/cli/v2/pkg/ssh" 16 "github.com/stretchr/testify/assert" 17 ) 18 19 type tinyConfig map[string]string 20 21 func (c tinyConfig) Get(host, key string) (string, error) { 22 return c[fmt.Sprintf("%s:%s", host, key)], nil 23 } 24 25 func (c tinyConfig) Set(host string, key string, value string) { 26 c[fmt.Sprintf("%s:%s", host, key)] = value 27 } 28 29 func (c tinyConfig) Write() error { 30 return nil 31 } 32 33 func TestLogin_ssh(t *testing.T) { 34 dir := t.TempDir() 35 ios, _, stdout, stderr := iostreams.Test() 36 37 tr := httpmock.Registry{} 38 defer tr.Verify(t) 39 40 tr.Register( 41 httpmock.REST("GET", "api/v3/"), 42 httpmock.ScopesResponder("repo,read:org")) 43 tr.Register( 44 httpmock.GraphQL(`query UserCurrent\b`), 45 httpmock.StringResponse(`{"data":{"viewer":{ "login": "monalisa" }}}`)) 46 tr.Register( 47 httpmock.REST("POST", "api/v3/user/keys"), 48 httpmock.StringResponse(`{}`)) 49 50 pm := &prompter.PrompterMock{} 51 pm.SelectFunc = func(prompt, _ string, opts []string) (int, error) { 52 switch prompt { 53 case "What is your preferred protocol for Git operations?": 54 return prompter.IndexFor(opts, "SSH") 55 case "How would you like to authenticate GitHub CLI?": 56 return prompter.IndexFor(opts, "Paste an authentication token") 57 } 58 return -1, prompter.NoSuchPromptErr(prompt) 59 } 60 pm.PasswordFunc = func(_ string) (string, error) { 61 return "monkey", nil 62 } 63 pm.ConfirmFunc = func(prompt string, _ bool) (bool, error) { 64 return true, nil 65 } 66 pm.AuthTokenFunc = func() (string, error) { 67 return "ATOKEN", nil 68 } 69 pm.InputFunc = func(_, _ string) (string, error) { 70 return "Test Key", nil 71 } 72 73 rs, runRestore := run.Stub() 74 defer runRestore(t) 75 76 keyFile := filepath.Join(dir, "id_ed25519") 77 rs.Register(`ssh-keygen`, 0, "", func(args []string) { 78 expected := []string{ 79 "ssh-keygen", "-t", "ed25519", 80 "-C", "", 81 "-N", "monkey", 82 "-f", keyFile, 83 } 84 assert.Equal(t, expected, args) 85 // simulate that the public key file has been generated 86 _ = os.WriteFile(keyFile+".pub", []byte("PUBKEY"), 0600) 87 }) 88 89 cfg := tinyConfig{} 90 91 err := Login(&LoginOptions{ 92 IO: ios, 93 Config: &cfg, 94 Prompter: pm, 95 HTTPClient: &http.Client{Transport: &tr}, 96 Hostname: "example.com", 97 Interactive: true, 98 sshContext: ssh.Context{ 99 ConfigDir: dir, 100 KeygenExe: "ssh-keygen", 101 }, 102 }) 103 assert.NoError(t, err) 104 105 assert.Equal(t, "", stdout.String()) 106 assert.Equal(t, heredoc.Docf(` 107 Tip: you can generate a Personal Access Token here https://example.com/settings/tokens 108 The minimum required scopes are 'repo', 'read:org', 'admin:public_key'. 109 - gh config set -h example.com git_protocol ssh 110 ✓ Configured git protocol 111 ✓ Uploaded the SSH key to your GitHub account: %s.pub 112 ✓ Logged in as monalisa 113 `, keyFile), stderr.String()) 114 115 assert.Equal(t, "monalisa", cfg["example.com:user"]) 116 assert.Equal(t, "ATOKEN", cfg["example.com:oauth_token"]) 117 assert.Equal(t, "ssh", cfg["example.com:git_protocol"]) 118 } 119 120 func Test_scopesSentence(t *testing.T) { 121 type args struct { 122 scopes []string 123 isEnterprise bool 124 } 125 tests := []struct { 126 name string 127 args args 128 want string 129 }{ 130 { 131 name: "basic scopes", 132 args: args{ 133 scopes: []string{"repo", "read:org"}, 134 isEnterprise: false, 135 }, 136 want: "'repo', 'read:org'", 137 }, 138 { 139 name: "empty", 140 args: args{ 141 scopes: []string(nil), 142 isEnterprise: false, 143 }, 144 want: "", 145 }, 146 { 147 name: "workflow scope for dotcom", 148 args: args{ 149 scopes: []string{"repo", "workflow"}, 150 isEnterprise: false, 151 }, 152 want: "'repo', 'workflow'", 153 }, 154 { 155 name: "workflow scope for GHE", 156 args: args{ 157 scopes: []string{"repo", "workflow"}, 158 isEnterprise: true, 159 }, 160 want: "'repo', 'workflow' (GHE 3.0+)", 161 }, 162 } 163 for _, tt := range tests { 164 t.Run(tt.name, func(t *testing.T) { 165 if got := scopesSentence(tt.args.scopes, tt.args.isEnterprise); got != tt.want { 166 t.Errorf("scopesSentence() = %q, want %q", got, tt.want) 167 } 168 }) 169 } 170 }