FRE-4694: Add CLI command end-to-end tests
Add comprehensive e2e tests for all CLI commands with mocked API responses. Fix test infrastructure to handle global state (os.Stdout capture, HOME env var) and broken test parallelism in stdout-capturing tests. - Add testutil_test.go with runFreshCommand, setupE2E, mockAPIServer - Add e2e_full_test.go with ~40 tests covering auth, mail, contacts, attachments, folders, labels, drafts, help output - Add newRootCmdBase() for fresh command trees per test - Remove t.Parallel() from stdout-capturing and HOME-dependent tests - Fix SessionWithMockSession to use runFreshCommand (stdout capture) Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
152
cmd/auth_test.go
Normal file
152
cmd/auth_test.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestLoginCommand tests the login CLI command
|
||||
func TestLoginCommand(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a temporary config file
|
||||
tmpDir := t.TempDir()
|
||||
configPath := tmpDir + "/config.yaml"
|
||||
|
||||
configContent := `
|
||||
api:
|
||||
base_url: "http://localhost:8080"
|
||||
timeout: 30s
|
||||
`
|
||||
err := os.WriteFile(configPath, []byte(configContent), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write config: %v", err)
|
||||
}
|
||||
|
||||
// Set config path environment variable
|
||||
os.Setenv("POP_CONFIG_PATH", configPath)
|
||||
defer os.Unsetenv("POP_CONFIG_PATH")
|
||||
|
||||
// Create root command with login subcommand
|
||||
rootCmd := NewRootCmd()
|
||||
rootCmd.SetArgs([]string{"login"})
|
||||
|
||||
// Capture output
|
||||
var buf bytes.Buffer
|
||||
rootCmd.SetOut(&buf)
|
||||
rootCmd.SetErr(&buf)
|
||||
|
||||
// Execute command
|
||||
err = rootCmd.Execute()
|
||||
if err != nil {
|
||||
// Login requires interactive input, so error is expected in non-interactive mode
|
||||
t.Logf("Login command executed with error (expected in non-interactive mode): %v", err)
|
||||
}
|
||||
|
||||
// Verify command ran
|
||||
output := buf.String()
|
||||
t.Logf("Command output: %s", output)
|
||||
}
|
||||
|
||||
// TestLogoutCommand tests the logout CLI command
|
||||
func TestLogoutCommand(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a temporary config file
|
||||
tmpDir := t.TempDir()
|
||||
configPath := tmpDir + "/config.yaml"
|
||||
|
||||
err := os.WriteFile(configPath, []byte("{}"), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write config: %v", err)
|
||||
}
|
||||
|
||||
os.Setenv("POP_CONFIG_PATH", configPath)
|
||||
defer os.Unsetenv("POP_CONFIG_PATH")
|
||||
|
||||
// Create root command with logout subcommand
|
||||
rootCmd := NewRootCmd()
|
||||
rootCmd.SetArgs([]string{"logout"})
|
||||
|
||||
// Capture output
|
||||
var buf bytes.Buffer
|
||||
rootCmd.SetOut(&buf)
|
||||
rootCmd.SetErr(&buf)
|
||||
|
||||
// Execute command
|
||||
err = rootCmd.Execute()
|
||||
// Logout may fail if no session exists, which is expected
|
||||
if err != nil {
|
||||
t.Logf("Logout command executed with error (expected if no session): %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSessionCommand tests the session CLI command
|
||||
func TestSessionCommand(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a temporary config file
|
||||
tmpDir := t.TempDir()
|
||||
configPath := tmpDir + "/config.yaml"
|
||||
|
||||
err := os.WriteFile(configPath, []byte("{}"), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write config: %v", err)
|
||||
}
|
||||
|
||||
os.Setenv("POP_CONFIG_PATH", configPath)
|
||||
defer os.Unsetenv("POP_CONFIG_PATH")
|
||||
|
||||
// Create root command with session subcommand
|
||||
rootCmd := NewRootCmd()
|
||||
rootCmd.SetArgs([]string{"session"})
|
||||
|
||||
// Capture output
|
||||
var buf bytes.Buffer
|
||||
rootCmd.SetOut(&buf)
|
||||
rootCmd.SetErr(&buf)
|
||||
|
||||
// Execute command
|
||||
err = rootCmd.Execute()
|
||||
// Session may fail if no active session, which is expected
|
||||
if err != nil {
|
||||
t.Logf("Session command executed with error (expected if no session): %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRootCommandHelp tests the help output
|
||||
func TestRootCommandHelp(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rootCmd := NewRootCmd()
|
||||
rootCmd.SetArgs([]string{"--help"})
|
||||
|
||||
var buf bytes.Buffer
|
||||
rootCmd.SetOut(&buf)
|
||||
|
||||
err := rootCmd.Execute()
|
||||
if err != nil {
|
||||
t.Fatalf("Help command failed: %v", err)
|
||||
}
|
||||
|
||||
output, _ := io.ReadAll(&buf)
|
||||
if len(output) == 0 {
|
||||
t.Error("Help output is empty")
|
||||
}
|
||||
|
||||
// Verify help contains expected commands
|
||||
helpText := string(output)
|
||||
expectedCommands := []string{"login", "logout", "session", "mail", "contact", "attachment", "folder", "draft"}
|
||||
for _, cmd := range expectedCommands {
|
||||
if !contains(helpText, cmd) {
|
||||
t.Errorf("Help output missing command: %s", cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if string contains substring
|
||||
func contains(s, substr string) bool {
|
||||
return len(s) > 0 && len(substr) > 0 && (s == substr || len(s) > len(substr) && (s[:len(substr)] == substr || contains(s[1:], substr)))
|
||||
}
|
||||
Reference in New Issue
Block a user