github.com/andrewhsu/cli/v2@v2.0.1-0.20210910131313-d4b4061f5b89/pkg/cmd/auth/shared/login_flow_test.go (about)

     1  package shared
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"path/filepath"
     8  	"testing"
     9  
    10  	"github.com/MakeNowJust/heredoc"
    11  	"github.com/andrewhsu/cli/v2/internal/run"
    12  	"github.com/andrewhsu/cli/v2/pkg/httpmock"
    13  	"github.com/andrewhsu/cli/v2/pkg/iostreams"
    14  	"github.com/andrewhsu/cli/v2/pkg/prompt"
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  type tinyConfig map[string]string
    19  
    20  func (c tinyConfig) Get(host, key string) (string, error) {
    21  	return c[fmt.Sprintf("%s:%s", host, key)], nil
    22  }
    23  
    24  func (c tinyConfig) Set(host string, key string, value string) error {
    25  	c[fmt.Sprintf("%s:%s", host, key)] = value
    26  	return nil
    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  	io, _, 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  	ask, askRestore := prompt.InitAskStubber()
    51  	defer askRestore()
    52  
    53  	ask.StubOne("SSH")    // preferred protocol
    54  	ask.StubOne(true)     // generate a new key
    55  	ask.StubOne("monkey") // enter a passphrase
    56  	ask.StubOne(1)        // paste a token
    57  	ask.StubOne("ATOKEN") // token
    58  
    59  	rs, runRestore := run.Stub()
    60  	defer runRestore(t)
    61  
    62  	keyFile := filepath.Join(dir, "id_ed25519")
    63  	rs.Register(`ssh-keygen`, 0, "", func(args []string) {
    64  		expected := []string{
    65  			"ssh-keygen", "-t", "ed25519",
    66  			"-C", "",
    67  			"-N", "monkey",
    68  			"-f", keyFile,
    69  		}
    70  		assert.Equal(t, expected, args)
    71  		// simulate that the public key file has been generated
    72  		_ = ioutil.WriteFile(keyFile+".pub", []byte("PUBKEY"), 0600)
    73  	})
    74  
    75  	cfg := tinyConfig{}
    76  
    77  	err := Login(&LoginOptions{
    78  		IO:          io,
    79  		Config:      &cfg,
    80  		HTTPClient:  &http.Client{Transport: &tr},
    81  		Hostname:    "example.com",
    82  		Interactive: true,
    83  		sshContext: sshContext{
    84  			configDir: dir,
    85  			keygenExe: "ssh-keygen",
    86  		},
    87  	})
    88  	assert.NoError(t, err)
    89  
    90  	assert.Equal(t, "", stdout.String())
    91  	assert.Equal(t, heredoc.Docf(`
    92  		Tip: you can generate a Personal Access Token here https://example.com/settings/tokens
    93  		The minimum required scopes are 'repo', 'read:org', 'admin:public_key'.
    94  		- gh config set -h example.com git_protocol ssh
    95  		✓ Configured git protocol
    96  		✓ Uploaded the SSH key to your GitHub account: %s.pub
    97  		✓ Logged in as monalisa
    98  	`, keyFile), stderr.String())
    99  
   100  	assert.Equal(t, "monalisa", cfg["example.com:user"])
   101  	assert.Equal(t, "ATOKEN", cfg["example.com:oauth_token"])
   102  	assert.Equal(t, "ssh", cfg["example.com:git_protocol"])
   103  }
   104  
   105  func Test_scopesSentence(t *testing.T) {
   106  	type args struct {
   107  		scopes       []string
   108  		isEnterprise bool
   109  	}
   110  	tests := []struct {
   111  		name string
   112  		args args
   113  		want string
   114  	}{
   115  		{
   116  			name: "basic scopes",
   117  			args: args{
   118  				scopes:       []string{"repo", "read:org"},
   119  				isEnterprise: false,
   120  			},
   121  			want: "'repo', 'read:org'",
   122  		},
   123  		{
   124  			name: "empty",
   125  			args: args{
   126  				scopes:       []string(nil),
   127  				isEnterprise: false,
   128  			},
   129  			want: "",
   130  		},
   131  		{
   132  			name: "workflow scope for dotcom",
   133  			args: args{
   134  				scopes:       []string{"repo", "workflow"},
   135  				isEnterprise: false,
   136  			},
   137  			want: "'repo', 'workflow'",
   138  		},
   139  		{
   140  			name: "workflow scope for GHE",
   141  			args: args{
   142  				scopes:       []string{"repo", "workflow"},
   143  				isEnterprise: true,
   144  			},
   145  			want: "'repo', 'workflow' (GHE 3.0+)",
   146  		},
   147  	}
   148  	for _, tt := range tests {
   149  		t.Run(tt.name, func(t *testing.T) {
   150  			if got := scopesSentence(tt.args.scopes, tt.args.isEnterprise); got != tt.want {
   151  				t.Errorf("scopesSentence() = %q, want %q", got, tt.want)
   152  			}
   153  		})
   154  	}
   155  }