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>
153 lines
3.6 KiB
Go
153 lines
3.6 KiB
Go
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)))
|
|
}
|