249 lines
7.5 KiB
Go
249 lines
7.5 KiB
Go
package migration_test
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/cli/cli/v2/internal/config/migration"
|
|
"github.com/cli/cli/v2/internal/keyring"
|
|
"github.com/cli/cli/v2/pkg/httpmock"
|
|
"github.com/cli/go-gh/v2/pkg/config"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestMigration(t *testing.T) {
|
|
cfg := config.ReadFromString(`
|
|
hosts:
|
|
github.com:
|
|
user: user1
|
|
oauth_token: xxxxxxxxxxxxxxxxxxxx
|
|
git_protocol: ssh
|
|
enterprise.com:
|
|
user: user2
|
|
oauth_token: yyyyyyyyyyyyyyyyyyyy
|
|
git_protocol: https
|
|
`)
|
|
|
|
var m migration.MultiAccount
|
|
require.NoError(t, m.Do(cfg))
|
|
|
|
// First we'll check that the oauth tokens have been moved to their new locations
|
|
requireKeyWithValue(t, cfg, []string{"hosts", "github.com", "users", "user1", "oauth_token"}, "xxxxxxxxxxxxxxxxxxxx")
|
|
requireKeyWithValue(t, cfg, []string{"hosts", "enterprise.com", "users", "user2", "oauth_token"}, "yyyyyyyyyyyyyyyyyyyy")
|
|
|
|
// Then we'll check that the old data has been left alone
|
|
requireKeyWithValue(t, cfg, []string{"hosts", "github.com", "user"}, "user1")
|
|
requireKeyWithValue(t, cfg, []string{"hosts", "github.com", "oauth_token"}, "xxxxxxxxxxxxxxxxxxxx")
|
|
requireKeyWithValue(t, cfg, []string{"hosts", "github.com", "git_protocol"}, "ssh")
|
|
|
|
requireKeyWithValue(t, cfg, []string{"hosts", "enterprise.com", "user"}, "user2")
|
|
requireKeyWithValue(t, cfg, []string{"hosts", "enterprise.com", "oauth_token"}, "yyyyyyyyyyyyyyyyyyyy")
|
|
requireKeyWithValue(t, cfg, []string{"hosts", "enterprise.com", "git_protocol"}, "https")
|
|
}
|
|
|
|
func TestMigrationSecureStorage(t *testing.T) {
|
|
cfg := config.ReadFromString(`
|
|
hosts:
|
|
github.com:
|
|
user: userOne
|
|
git_protocol: ssh
|
|
enterprise.com:
|
|
user: userTwo
|
|
git_protocol: https
|
|
`)
|
|
|
|
userOneToken := "userOne-token"
|
|
userTwoToken := "userTwo-token"
|
|
|
|
keyring.MockInit()
|
|
require.NoError(t, keyring.Set("gh:github.com", "", userOneToken))
|
|
require.NoError(t, keyring.Set("gh:enterprise.com", "", userTwoToken))
|
|
|
|
var m migration.MultiAccount
|
|
require.NoError(t, m.Do(cfg))
|
|
|
|
// Verify token gets stored with host and username
|
|
gotUserOneToken, err := keyring.Get("gh:github.com", "userOne")
|
|
require.NoError(t, err)
|
|
require.Equal(t, userOneToken, gotUserOneToken)
|
|
|
|
// Verify token still exists with only host
|
|
gotUserOneToken, err = keyring.Get("gh:github.com", "")
|
|
require.NoError(t, err)
|
|
require.Equal(t, userOneToken, gotUserOneToken)
|
|
|
|
// Verify token gets stored with host and username
|
|
gotUserTwoToken, err := keyring.Get("gh:enterprise.com", "userTwo")
|
|
require.NoError(t, err)
|
|
require.Equal(t, userTwoToken, gotUserTwoToken)
|
|
|
|
// Verify token still exists with only host
|
|
gotUserTwoToken, err = keyring.Get("gh:enterprise.com", "")
|
|
require.NoError(t, err)
|
|
require.Equal(t, userTwoToken, gotUserTwoToken)
|
|
|
|
// First we'll check that the users have been created with no config underneath them
|
|
requireKeyExists(t, cfg, []string{"hosts", "github.com", "users", "userOne"})
|
|
requireKeyExists(t, cfg, []string{"hosts", "enterprise.com", "users", "userTwo"})
|
|
|
|
// Then we'll check that the old data has been left alone
|
|
requireKeyWithValue(t, cfg, []string{"hosts", "github.com", "user"}, "userOne")
|
|
requireKeyWithValue(t, cfg, []string{"hosts", "github.com", "git_protocol"}, "ssh")
|
|
|
|
requireKeyWithValue(t, cfg, []string{"hosts", "enterprise.com", "user"}, "userTwo")
|
|
requireKeyWithValue(t, cfg, []string{"hosts", "enterprise.com", "git_protocol"}, "https")
|
|
}
|
|
|
|
func TestPreVersionIsEmptyString(t *testing.T) {
|
|
var m migration.MultiAccount
|
|
require.Equal(t, "", m.PreVersion())
|
|
}
|
|
|
|
func TestPostVersion(t *testing.T) {
|
|
var m migration.MultiAccount
|
|
require.Equal(t, "1", m.PostVersion())
|
|
}
|
|
|
|
func TestMigrationReturnsSuccessfullyWhenNoHostsEntry(t *testing.T) {
|
|
cfg := config.ReadFromString(``)
|
|
|
|
var m migration.MultiAccount
|
|
require.NoError(t, m.Do(cfg))
|
|
}
|
|
|
|
func TestMigrationReturnsSuccessfullyWhenEmptyHosts(t *testing.T) {
|
|
cfg := config.ReadFromString(`
|
|
hosts:
|
|
`)
|
|
|
|
var m migration.MultiAccount
|
|
require.NoError(t, m.Do(cfg))
|
|
}
|
|
|
|
func TestMigrationReturnsSuccessfullyWhenAnonymousUserExists(t *testing.T) {
|
|
// Simulates config that gets generated when a user logs
|
|
// in with a token and git protocol is not specified and
|
|
// secure storage is used.
|
|
token := "test-token"
|
|
keyring.MockInit()
|
|
require.NoError(t, keyring.Set("gh:github.com", "", token))
|
|
|
|
cfg := config.ReadFromString(`
|
|
hosts:
|
|
github.com:
|
|
user: x-access-token
|
|
`)
|
|
|
|
reg := &httpmock.Registry{}
|
|
defer reg.Verify(t)
|
|
reg.Register(
|
|
httpmock.GraphQL(`query CurrentUser\b`),
|
|
httpmock.StringResponse(`{"data":{"viewer":{"login":"monalisa"}}}`),
|
|
)
|
|
|
|
m := migration.MultiAccount{Transport: reg}
|
|
require.NoError(t, m.Do(cfg))
|
|
|
|
require.Equal(t, fmt.Sprintf("token %s", token), reg.Requests[0].Header.Get("Authorization"))
|
|
requireKeyWithValue(t, cfg, []string{"hosts", "github.com", "user"}, "monalisa")
|
|
// monalisa key gets created with no value
|
|
users, err := cfg.Keys([]string{"hosts", "github.com", "users"})
|
|
require.NoError(t, err)
|
|
require.Equal(t, []string{"monalisa"}, users)
|
|
|
|
// Verify token gets stored with host and username
|
|
gotToken, err := keyring.Get("gh:github.com", "monalisa")
|
|
require.NoError(t, err)
|
|
require.Equal(t, token, gotToken)
|
|
|
|
// Verify token still exists with only host
|
|
gotToken, err = keyring.Get("gh:github.com", "")
|
|
require.NoError(t, err)
|
|
require.Equal(t, token, gotToken)
|
|
}
|
|
|
|
func TestMigrationReturnsSuccessfullyWhenAnonymousUserExistsAndInsecureStorage(t *testing.T) {
|
|
// Simulates config that gets generated when a user logs
|
|
// in with a token and git protocol is specified and
|
|
// secure storage is not used.
|
|
cfg := config.ReadFromString(`
|
|
hosts:
|
|
github.com:
|
|
user: x-access-token
|
|
oauth_token: test-token
|
|
git_protocol: ssh
|
|
`)
|
|
|
|
reg := &httpmock.Registry{}
|
|
defer reg.Verify(t)
|
|
reg.Register(
|
|
httpmock.GraphQL(`query CurrentUser\b`),
|
|
httpmock.StringResponse(`{"data":{"viewer":{"login":"monalisa"}}}`),
|
|
)
|
|
|
|
m := migration.MultiAccount{Transport: reg}
|
|
require.NoError(t, m.Do(cfg))
|
|
|
|
require.Equal(t, "token test-token", reg.Requests[0].Header.Get("Authorization"))
|
|
requireKeyWithValue(t, cfg, []string{"hosts", "github.com", "user"}, "monalisa")
|
|
requireKeyWithValue(t, cfg, []string{"hosts", "github.com", "users", "monalisa", "oauth_token"}, "test-token")
|
|
}
|
|
|
|
func TestMigrationRemovesHostsWithInvalidTokens(t *testing.T) {
|
|
// Simulates config when user is logged in securely
|
|
// but no token entry is in the keyring.
|
|
keyring.MockInit()
|
|
cfg := config.ReadFromString(`
|
|
hosts:
|
|
github.com:
|
|
user: user1
|
|
git_protocol: ssh
|
|
`)
|
|
|
|
m := migration.MultiAccount{}
|
|
require.NoError(t, m.Do(cfg))
|
|
|
|
requireNoKey(t, cfg, []string{"hosts", "github.com"})
|
|
}
|
|
|
|
func TestMigrationErrorsWhenUnableToGetExpectedSecureToken(t *testing.T) {
|
|
// Simulates config when user is logged in securely
|
|
// but no token entry is in the keyring.
|
|
keyring.MockInitWithError(errors.New("keyring test error"))
|
|
cfg := config.ReadFromString(`
|
|
hosts:
|
|
github.com:
|
|
user: user1
|
|
git_protocol: ssh
|
|
`)
|
|
|
|
m := migration.MultiAccount{}
|
|
err := m.Do(cfg)
|
|
|
|
require.ErrorContains(t, err, `couldn't find oauth token for "github.com": keyring test error`)
|
|
}
|
|
|
|
func requireKeyExists(t *testing.T, cfg *config.Config, keys []string) {
|
|
t.Helper()
|
|
|
|
_, err := cfg.Get(keys)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func requireKeyWithValue(t *testing.T, cfg *config.Config, keys []string, value string) {
|
|
t.Helper()
|
|
|
|
actual, err := cfg.Get(keys)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, value, actual)
|
|
}
|
|
|
|
func requireNoKey(t *testing.T, cfg *config.Config, keys []string) {
|
|
t.Helper()
|
|
|
|
_, err := cfg.Get(keys)
|
|
var keyNotFoundError *config.KeyNotFoundError
|
|
require.ErrorAs(t, err, &keyNotFoundError)
|
|
}
|