From c165d5ccc0960db1e46499c01bb8870f595009af Mon Sep 17 00:00:00 2001 From: William Martin Date: Fri, 1 Dec 2023 17:17:53 +0100 Subject: [PATCH] Use isolated config in logout cmd tests --- pkg/cmd/auth/logout/logout_test.go | 320 +++++++++++++++-------------- 1 file changed, 167 insertions(+), 153 deletions(-) diff --git a/pkg/cmd/auth/logout/logout_test.go b/pkg/cmd/auth/logout/logout_test.go index 97f8bba93..5e0048bce 100644 --- a/pkg/cmd/auth/logout/logout_test.go +++ b/pkg/cmd/auth/logout/logout_test.go @@ -6,9 +6,7 @@ import ( "regexp" "testing" - "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/internal/config" - "github.com/cli/cli/v2/internal/keyring" "github.com/cli/cli/v2/internal/prompter" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" @@ -82,6 +80,7 @@ func Test_NewCmdLogout(t *testing.T) { }, }, } + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ios, _, _, _ := iostreams.Test() @@ -115,14 +114,18 @@ func Test_NewCmdLogout(t *testing.T) { } } -type host string -type user string +type user struct { + name string + token string +} type hostUsers struct { - host host + host string users []user } +type tokenAssertion func(t *testing.T, cfg config.Config) + func Test_logoutRun_tty(t *testing.T) { tests := []struct { name string @@ -131,6 +134,7 @@ func Test_logoutRun_tty(t *testing.T) { cfgHosts []hostUsers secureStorage bool wantHosts string + assertToken tokenAssertion wantErrOut *regexp.Regexp wantErr string }{ @@ -138,54 +142,73 @@ func Test_logoutRun_tty(t *testing.T) { name: "logs out prompted user when multiple known hosts with one user each", opts: &LogoutOptions{}, cfgHosts: []hostUsers{ - {"ghe.io", []user{"monalisa-ghe"}}, - {"github.com", []user{"monalisa"}}, + {"ghe.io", []user{ + {"monalisa-ghe", "abc123"}, + }}, + {"github.com", []user{ + {"monalisa", "abc123"}, + }}, }, - wantHosts: "ghe.io:\n users:\n monalisa-ghe:\n oauth_token: abc123\n git_protocol: ssh\n oauth_token: abc123\n git_protocol: ssh\n user: monalisa-ghe\n", prompterStubs: func(pm *prompter.PrompterMock) { pm.SelectFunc = func(_, _ string, opts []string) (int, error) { return prompter.IndexFor(opts, "monalisa (github.com)") } }, - wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), + assertToken: hasNoToken("github.com"), + wantHosts: "ghe.io:\n users:\n monalisa-ghe:\n oauth_token: abc123\n git_protocol: ssh\n oauth_token: abc123\n git_protocol: ssh\n user: monalisa-ghe\n", + wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), }, { name: "logs out prompted user when multiple known hosts with multiple users each", opts: &LogoutOptions{}, cfgHosts: []hostUsers{ - {"ghe.io", []user{"monalisa-ghe", "monalisa-ghe2"}}, - {"github.com", []user{"monalisa", "monalisa2"}}, + {"ghe.io", []user{ + {"monalisa-ghe", "abc123"}, + {"monalisa-ghe2", "abc123"}, + }}, + {"github.com", []user{ + {"monalisa", "monalisa-token"}, + {"monalisa2", "monalisa2-token"}, + }}, }, - wantHosts: "ghe.io:\n users:\n monalisa-ghe:\n oauth_token: abc123\n git_protocol: ssh\n monalisa-ghe2:\n oauth_token: abc123\n git_protocol: ssh\n git_protocol: ssh\n user: monalisa-ghe2\n oauth_token: abc123\ngithub.com:\n users:\n monalisa2:\n oauth_token: abc123\n git_protocol: ssh\n git_protocol: ssh\n user: monalisa2\n oauth_token: abc123\n", prompterStubs: func(pm *prompter.PrompterMock) { pm.SelectFunc = func(_, _ string, opts []string) (int, error) { return prompter.IndexFor(opts, "monalisa (github.com)") } }, - wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), + assertToken: hasActiveToken("github.com", "monalisa2-token"), + wantHosts: "ghe.io:\n users:\n monalisa-ghe:\n oauth_token: abc123\n git_protocol: ssh\n monalisa-ghe2:\n oauth_token: abc123\n git_protocol: ssh\n git_protocol: ssh\n user: monalisa-ghe2\n oauth_token: abc123\ngithub.com:\n users:\n monalisa2:\n oauth_token: monalisa2-token\n git_protocol: ssh\n git_protocol: ssh\n user: monalisa2\n oauth_token: monalisa2-token\n", + wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), }, { name: "logs out only logged in user", opts: &LogoutOptions{}, cfgHosts: []hostUsers{ - {"github.com", []user{"monalisa"}}, + {"github.com", []user{ + {"monalisa", "abc123"}, + }}, }, - wantHosts: "{}\n", - wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), + wantHosts: "{}\n", + assertToken: hasNoToken("github.com"), + wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), }, { name: "logs out prompted user when one known host with multiple users", opts: &LogoutOptions{}, cfgHosts: []hostUsers{ - {"github.com", []user{"monalisa", "monalisa2"}}, + {"github.com", []user{ + {"monalisa", "monalisa-token"}, + {"monalisa2", "monalisa2-token"}, + }}, }, prompterStubs: func(pm *prompter.PrompterMock) { pm.SelectFunc = func(_, _ string, opts []string) (int, error) { return prompter.IndexFor(opts, "monalisa (github.com)") } }, - wantHosts: "github.com:\n users:\n monalisa2:\n oauth_token: abc123\n git_protocol: ssh\n git_protocol: ssh\n user: monalisa2\n oauth_token: abc123\n", - wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), + wantHosts: "github.com:\n users:\n monalisa2:\n oauth_token: monalisa2-token\n git_protocol: ssh\n git_protocol: ssh\n user: monalisa2\n oauth_token: monalisa2-token\n", + assertToken: hasActiveToken("github.com", "monalisa2-token"), + wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), }, { name: "logs out specified user when multiple known hosts with one user each", @@ -194,11 +217,16 @@ func Test_logoutRun_tty(t *testing.T) { Username: "monalisa-ghe", }, cfgHosts: []hostUsers{ - {"ghe.io", []user{"monalisa-ghe"}}, - {"github.com", []user{"monalisa"}}, + {"ghe.io", []user{ + {"monalisa-ghe", "abc123"}, + }}, + {"github.com", []user{ + {"monalisa", "abc123"}, + }}, }, - wantHosts: "github.com:\n users:\n monalisa:\n oauth_token: abc123\n git_protocol: ssh\n oauth_token: abc123\n git_protocol: ssh\n user: monalisa\n", - wantErrOut: regexp.MustCompile(`Logged out of ghe.io account monalisa-ghe`), + wantHosts: "github.com:\n users:\n monalisa:\n oauth_token: abc123\n git_protocol: ssh\n oauth_token: abc123\n git_protocol: ssh\n user: monalisa\n", + assertToken: hasNoToken("ghe.io"), + wantErrOut: regexp.MustCompile(`Logged out of ghe.io account monalisa-ghe`), }, { name: "logs out specified user that is using secure storage", @@ -208,10 +236,13 @@ func Test_logoutRun_tty(t *testing.T) { Username: "monalisa", }, cfgHosts: []hostUsers{ - {"github.com", []user{"monalisa"}}, + {"github.com", []user{ + {"monalisa", "abc123"}, + }}, }, - wantHosts: "{}\n", - wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), + wantHosts: "{}\n", + assertToken: hasNoToken("github.com"), + wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), }, { name: "errors when no known hosts", @@ -225,7 +256,9 @@ func Test_logoutRun_tty(t *testing.T) { Username: "monalisa-ghe", }, cfgHosts: []hostUsers{ - {"github.com", []user{"monalisa"}}, + {"github.com", []user{ + {"monalisa", "abc123"}, + }}, }, wantErr: "not logged in to ghe.io", }, @@ -236,7 +269,9 @@ func Test_logoutRun_tty(t *testing.T) { Username: "unknown-user", }, cfgHosts: []hostUsers{ - {"ghe.io", []user{"monalisa-ghe"}}, + {"ghe.io", []user{ + {"monalisa-ghe", "abc123"}, + }}, }, wantErr: "not logged in to ghe.io account unknown-user", }, @@ -246,24 +281,43 @@ func Test_logoutRun_tty(t *testing.T) { Username: "unknown-user", }, cfgHosts: []hostUsers{ - {"github.com", []user{"monalisa"}}, - {"ghe.io", []user{"monalisa"}}, + {"ghe.io", []user{ + {"monalisa-ghe", "abc123"}, + }}, + {"github.com", []user{ + {"monalisa", "abc123"}, + }}, }, wantErr: "no accounts matched that criteria", }, + { + name: "switches user if there is another one available", + opts: &LogoutOptions{ + Hostname: "github.com", + Username: "monalisa2", + }, + cfgHosts: []hostUsers{ + {"github.com", []user{ + {"monalisa", "monalisa-token"}, + {"monalisa2", "monalisa2-token"}, + }}, + }, + wantHosts: "github.com:\n users:\n monalisa:\n oauth_token: monalisa-token\n git_protocol: ssh\n git_protocol: ssh\n user: monalisa\n oauth_token: monalisa-token\n", + assertToken: hasActiveToken("github.com", "monalisa-token"), + wantErrOut: regexp.MustCompile("✓ Switched active account for github.com to monalisa"), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - keyring.MockInit() - readConfigs := config.StubWriteConfig(t) - cfg := config.NewFromString("") + cfg, readConfigs := config.NewIsolatedTestConfig(t) + for _, hostUsers := range tt.cfgHosts { for _, user := range hostUsers.users { _, _ = cfg.Authentication().Login( string(hostUsers.host), - string(user), - "abc123", "ssh", tt.secureStorage, + user.name, + user.token, "ssh", tt.secureStorage, ) } } @@ -297,13 +351,14 @@ func Test_logoutRun_tty(t *testing.T) { require.True(t, tt.wantErrOut.MatchString(stderr.String()), stderr.String()) } - mainBuf := bytes.Buffer{} hostsBuf := bytes.Buffer{} - readConfigs(&mainBuf, &hostsBuf) - secureToken, _ := cfg.Authentication().TokenFromKeyring(tt.opts.Hostname) + readConfigs(io.Discard, &hostsBuf) require.Equal(t, tt.wantHosts, hostsBuf.String()) - require.Equal(t, "", secureToken) + + if tt.assertToken != nil { + tt.assertToken(t, cfg) + } }) } } @@ -314,8 +369,8 @@ func Test_logoutRun_nontty(t *testing.T) { opts *LogoutOptions cfgHosts []hostUsers secureStorage bool - ghtoken string wantHosts string + assertToken tokenAssertion wantErrOut *regexp.Regexp wantErr string }{ @@ -326,10 +381,13 @@ func Test_logoutRun_nontty(t *testing.T) { Username: "monalisa", }, cfgHosts: []hostUsers{ - {"github.com", []user{"monalisa"}}, + {"github.com", []user{ + {"monalisa", "abc123"}, + }}, }, - wantHosts: "{}\n", - wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), + wantHosts: "{}\n", + assertToken: hasNoToken("github.com"), + wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), }, { name: "logs out specified user when multiple known hosts", @@ -338,11 +396,16 @@ func Test_logoutRun_nontty(t *testing.T) { Username: "monalisa", }, cfgHosts: []hostUsers{ - {"github.com", []user{"monalisa"}}, - {"ghe.io", []user{"monalisa-ghe"}}, + {"github.com", []user{ + {"monalisa", "abc123"}, + }}, + {"ghe.io", []user{ + {"monalisa-ghe", "abc123"}, + }}, }, - wantHosts: "ghe.io:\n users:\n monalisa-ghe:\n oauth_token: abc123\n git_protocol: ssh\n oauth_token: abc123\n git_protocol: ssh\n user: monalisa-ghe\n", - wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), + wantHosts: "ghe.io:\n users:\n monalisa-ghe:\n oauth_token: abc123\n git_protocol: ssh\n oauth_token: abc123\n git_protocol: ssh\n user: monalisa-ghe\n", + assertToken: hasNoToken("github.com"), + wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), }, { name: "logs out specified user that is using secure storage", @@ -352,10 +415,13 @@ func Test_logoutRun_nontty(t *testing.T) { Username: "monalisa", }, cfgHosts: []hostUsers{ - {"github.com", []user{"monalisa"}}, + {"github.com", []user{ + {"monalisa", "abc123"}, + }}, }, - wantHosts: "{}\n", - wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), + wantHosts: "{}\n", + assertToken: hasNoToken("github.com"), + wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`), }, { name: "errors when no known hosts", @@ -372,7 +438,9 @@ func Test_logoutRun_nontty(t *testing.T) { Username: "monalisa-ghe", }, cfgHosts: []hostUsers{ - {"github.com", []user{"monalisa"}}, + {"github.com", []user{ + {"monalisa", "abc123"}, + }}, }, wantErr: "not logged in to ghe.io", }, @@ -383,7 +451,9 @@ func Test_logoutRun_nontty(t *testing.T) { Username: "unknown-user", }, cfgHosts: []hostUsers{ - {"ghe.io", []user{"monalisa-ghe"}}, + {"ghe.io", []user{ + {"monalisa-ghe", "abc123"}, + }}, }, wantErr: "not logged in to ghe.io account unknown-user", }, @@ -393,8 +463,10 @@ func Test_logoutRun_nontty(t *testing.T) { Hostname: "ghe.io", }, cfgHosts: []hostUsers{ - {"ghe.io", []user{"monalisa-ghe"}}, - {"ghe.io", []user{"monalisa-ghe-2"}}, + {"ghe.io", []user{ + {"monalisa-ghe", "abc123"}, + {"monalisa-ghe2", "abc123"}, + }}, }, wantErr: "unable to determine which account to log out of, please specify `--hostname` and `--user`", }, @@ -404,24 +476,43 @@ func Test_logoutRun_nontty(t *testing.T) { Username: "monalisa", }, cfgHosts: []hostUsers{ - {"github.com", []user{"monalisa"}}, - {"ghe.io", []user{"monalisa"}}, + {"github.com", []user{ + {"monalisa", "abc123"}, + }}, + {"ghe.io", []user{ + {"monalisa", "abc123"}, + }}, }, wantErr: "unable to determine which account to log out of, please specify `--hostname` and `--user`", }, + { + name: "switches user if there is another one available", + opts: &LogoutOptions{ + Hostname: "github.com", + Username: "monalisa2", + }, + cfgHosts: []hostUsers{ + {"github.com", []user{ + {"monalisa", "monalisa-token"}, + {"monalisa2", "monalisa2-token"}, + }}, + }, + wantHosts: "github.com:\n users:\n monalisa:\n oauth_token: monalisa-token\n git_protocol: ssh\n git_protocol: ssh\n user: monalisa\n oauth_token: monalisa-token\n", + assertToken: hasActiveToken("github.com", "monalisa-token"), + wantErrOut: regexp.MustCompile("✓ Switched active account for github.com to monalisa"), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - keyring.MockInit() - readConfigs := config.StubWriteConfig(t) - cfg := config.NewFromString("") + cfg, readConfigs := config.NewIsolatedTestConfig(t) + for _, hostUsers := range tt.cfgHosts { for _, user := range hostUsers.users { _, _ = cfg.Authentication().Login( string(hostUsers.host), - string(user), - "abc123", "ssh", tt.secureStorage, + user.name, + user.token, "ssh", tt.secureStorage, ) } } @@ -448,109 +539,32 @@ func Test_logoutRun_nontty(t *testing.T) { require.True(t, tt.wantErrOut.MatchString(stderr.String()), stderr.String()) } - mainBuf := bytes.Buffer{} hostsBuf := bytes.Buffer{} - readConfigs(&mainBuf, &hostsBuf) - secureToken, _ := cfg.Authentication().TokenFromKeyring(tt.opts.Hostname) + readConfigs(io.Discard, &hostsBuf) require.Equal(t, tt.wantHosts, hostsBuf.String()) - require.Equal(t, "", secureToken) + + if tt.assertToken != nil { + tt.assertToken(t, cfg) + } }) } } -func TestLogoutSwitchesUserNonTTY(t *testing.T) { - keyring.MockInit() +func hasNoToken(hostname string) tokenAssertion { + return func(t *testing.T, cfg config.Config) { + t.Helper() - ios, _, _, stderr := iostreams.Test() - ios.SetStdinTTY(false) - ios.SetStdoutTTY(false) - - readConfigs := config.StubWriteConfig(t) - cfg := config.NewFromString("") - _, err := cfg.Authentication().Login("github.com", "test-user-1", "test-token-1", "https", true) - require.NoError(t, err) - - _, err = cfg.Authentication().Login("github.com", "test-user-2", "test-token-2", "ssh", true) - require.NoError(t, err) - - opts := LogoutOptions{ - IO: ios, - Config: func() (config.Config, error) { - return cfg, nil - }, - Hostname: "github.com", - Username: "test-user-2", + token, _ := cfg.Authentication().Token(hostname) + require.Empty(t, token) } - - require.NoError(t, logoutRun(&opts)) - - hostsBuf := bytes.Buffer{} - readConfigs(io.Discard, &hostsBuf) - - secureToken, _ := cfg.Authentication().TokenFromKeyring("github.com") - require.Equal(t, "test-token-1", secureToken) - - expectedHosts := heredoc.Doc(` - github.com: - users: - test-user-1: - git_protocol: https - git_protocol: https - user: test-user-1 - `) - - require.Equal(t, expectedHosts, hostsBuf.String()) - - require.Contains(t, stderr.String(), "✓ Switched active account for github.com to test-user-1") } -func TestLogoutSwitchesUserTTY(t *testing.T) { - keyring.MockInit() +func hasActiveToken(hostname string, expectedToken string) tokenAssertion { + return func(t *testing.T, cfg config.Config) { + t.Helper() - ios, _, _, stderr := iostreams.Test() - ios.SetStdinTTY(true) - ios.SetStdoutTTY(true) - - readConfigs := config.StubWriteConfig(t) - cfg := config.NewFromString("") - _, err := cfg.Authentication().Login("github.com", "test-user-1", "test-token-1", "https", true) - require.NoError(t, err) - - _, err = cfg.Authentication().Login("github.com", "test-user-2", "test-token-2", "ssh", true) - require.NoError(t, err) - - pm := &prompter.PrompterMock{} - pm.SelectFunc = func(_, _ string, opts []string) (int, error) { - return prompter.IndexFor(opts, "test-user-2 (github.com)") + token, _ := cfg.Authentication().Token(hostname) + require.Equal(t, expectedToken, token) } - - opts := LogoutOptions{ - IO: ios, - Config: func() (config.Config, error) { - return cfg, nil - }, - Prompter: pm, - } - - require.NoError(t, logoutRun(&opts)) - - hostsBuf := bytes.Buffer{} - readConfigs(io.Discard, &hostsBuf) - - secureToken, _ := cfg.Authentication().TokenFromKeyring("github.com") - require.Equal(t, "test-token-1", secureToken) - - expectedHosts := heredoc.Doc(` - github.com: - users: - test-user-1: - git_protocol: https - git_protocol: https - user: test-user-1 - `) - - require.Equal(t, expectedHosts, hostsBuf.String()) - - require.Contains(t, stderr.String(), "✓ Switched active account for github.com to test-user-1") }