package cli_test import ( "os" "runtime" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/coder/coder/cli/clitest" "github.com/coder/coder/cli/config" "github.com/coder/coder/coderd/coderdtest" "github.com/coder/coder/pty/ptytest" ) func TestLogout(t *testing.T) { t.Parallel() t.Run("Logout", func(t *testing.T) { t.Parallel() pty := ptytest.New(t) config := login(t, pty) // Ensure session files exist. require.FileExists(t, string(config.URL())) require.FileExists(t, string(config.Session())) logoutChan := make(chan struct{}) logout, _ := clitest.New(t, "logout", "--global-config", string(config)) logout.Stdin = pty.Input() logout.Stdout = pty.Output() go func() { defer close(logoutChan) err := logout.Run() assert.NoError(t, err) assert.NoFileExists(t, string(config.URL())) assert.NoFileExists(t, string(config.Session())) }() pty.ExpectMatch("Are you sure you want to log out?") pty.WriteLine("yes") pty.ExpectMatch("You are no longer logged in. You can log in using 'coder login '.") <-logoutChan }) t.Run("SkipPrompt", func(t *testing.T) { t.Parallel() pty := ptytest.New(t) config := login(t, pty) // Ensure session files exist. require.FileExists(t, string(config.URL())) require.FileExists(t, string(config.Session())) logoutChan := make(chan struct{}) logout, _ := clitest.New(t, "logout", "--global-config", string(config), "-y") logout.Stdin = pty.Input() logout.Stdout = pty.Output() go func() { defer close(logoutChan) err := logout.Run() assert.NoError(t, err) assert.NoFileExists(t, string(config.URL())) assert.NoFileExists(t, string(config.Session())) }() pty.ExpectMatch("You are no longer logged in. You can log in using 'coder login '.") <-logoutChan }) t.Run("NoURLFile", func(t *testing.T) { t.Parallel() pty := ptytest.New(t) config := login(t, pty) // Ensure session files exist. require.FileExists(t, string(config.URL())) require.FileExists(t, string(config.Session())) err := os.Remove(string(config.URL())) require.NoError(t, err) logoutChan := make(chan struct{}) logout, _ := clitest.New(t, "logout", "--global-config", string(config)) logout.Stdin = pty.Input() logout.Stdout = pty.Output() go func() { defer close(logoutChan) err := logout.Run() assert.ErrorContains(t, err, "You are not logged in. Try logging in using 'coder login '.") }() <-logoutChan }) t.Run("NoSessionFile", func(t *testing.T) { t.Parallel() pty := ptytest.New(t) config := login(t, pty) // Ensure session files exist. require.FileExists(t, string(config.URL())) require.FileExists(t, string(config.Session())) err := os.Remove(string(config.Session())) require.NoError(t, err) logoutChan := make(chan struct{}) logout, _ := clitest.New(t, "logout", "--global-config", string(config)) logout.Stdin = pty.Input() logout.Stdout = pty.Output() go func() { defer close(logoutChan) err = logout.Run() assert.ErrorContains(t, err, "You are not logged in. Try logging in using 'coder login '.") }() <-logoutChan }) t.Run("CannotDeleteFiles", func(t *testing.T) { t.Parallel() pty := ptytest.New(t) config := login(t, pty) // Ensure session files exist. require.FileExists(t, string(config.URL())) require.FileExists(t, string(config.Session())) var ( err error urlFile *os.File sessionFile *os.File ) if runtime.GOOS == "windows" { // Opening the files so Windows does not allow deleting them. urlFile, err = os.Open(string(config.URL())) require.NoError(t, err) sessionFile, err = os.Open(string(config.Session())) require.NoError(t, err) } else { // Changing the permissions to throw error during deletion. err = os.Chmod(string(config), 0o500) require.NoError(t, err) } defer func() { if runtime.GOOS == "windows" { // Closing the opened files for cleanup. err = urlFile.Close() assert.NoError(t, err) err = sessionFile.Close() assert.NoError(t, err) } else { // Setting the permissions back for cleanup. err = os.Chmod(string(config), 0o700) assert.NoError(t, err) } }() logout, _ := clitest.New(t, "logout", "--global-config", string(config)) logout.Stdin = pty.Input() logout.Stdout = pty.Output() go func() { pty.ExpectMatch("Are you sure you want to log out?") pty.WriteLine("yes") }() err = logout.Run() require.Error(t, err) t.Logf("err: %v", err) var wantError string if runtime.GOOS == "windows" { wantError = "The process cannot access the file because it is being used by another process." } else { wantError = "permission denied" } require.ErrorContains(t, err, wantError) }) } func login(t *testing.T, pty *ptytest.PTY) config.Root { t.Helper() client := coderdtest.New(t, nil) coderdtest.CreateFirstUser(t, client) doneChan := make(chan struct{}) root, cfg := clitest.New(t, "login", "--force-tty", client.URL.String(), "--no-open") root.Stdin = pty.Input() root.Stdout = pty.Output() go func() { defer close(doneChan) err := root.Run() assert.NoError(t, err) }() pty.ExpectMatch("Paste your token here:") pty.WriteLine(client.SessionToken()) pty.ExpectMatch("Welcome to Coder") <-doneChan return cfg }