ananke/cmd/ananke/main_dispatch_additional_test.go

158 lines
5.2 KiB
Go

package main
import (
"errors"
"io"
"log"
"os"
"os/exec"
"strings"
"testing"
)
// TestMainNoArgsSubprocess runs one orchestration or CLI step.
// Signature: TestMainNoArgsSubprocess(t *testing.T).
// Why: covers usage exit path when no command is provided.
func TestMainNoArgsSubprocess(t *testing.T) {
if os.Getenv("ANANKE_TEST_MAIN_NO_ARGS") == "1" {
os.Args = []string{"ananke"}
main()
return
}
cmd := exec.Command(os.Args[0], "-test.run=TestMainNoArgsSubprocess")
cmd.Env = append(os.Environ(), "ANANKE_TEST_MAIN_NO_ARGS=1")
out, err := cmd.CombinedOutput()
if err == nil {
t.Fatalf("expected no-args subprocess to fail")
}
exitErr, ok := err.(*exec.ExitError)
if !ok {
t.Fatalf("expected exit error type, got %T (%v)", err, err)
}
if exitErr.ExitCode() != 2 {
t.Fatalf("expected exit code 2, got %d output=%s", exitErr.ExitCode(), string(out))
}
if !strings.Contains(string(out), "Usage:") {
t.Fatalf("expected usage output, got: %s", string(out))
}
}
// TestMainStatusSubprocess runs one orchestration or CLI step.
// Signature: TestMainStatusSubprocess(t *testing.T).
// Why: covers successful dispatch branch for status command.
func TestMainStatusSubprocess(t *testing.T) {
if os.Getenv("ANANKE_TEST_MAIN_STATUS") == "1" {
cfgPath := os.Getenv("ANANKE_TEST_CFG")
os.Args = []string{"ananke", "status", "--config", cfgPath}
main()
return
}
cfgPath := writeTestConfig(t)
cmd := exec.Command(os.Args[0], "-test.run=TestMainStatusSubprocess")
cmd.Env = append(os.Environ(),
"ANANKE_TEST_MAIN_STATUS=1",
"ANANKE_TEST_CFG="+cfgPath,
)
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("expected status subprocess success: %v\n%s", err, string(out))
}
}
// TestRunMainDispatchSuccessPaths runs one orchestration or CLI step.
// Signature: TestRunMainDispatchSuccessPaths(t *testing.T).
// Why: unit-level dispatch coverage ensures every CLI command route remains wired.
func TestRunMainDispatchSuccessPaths(t *testing.T) {
logger := log.New(io.Discard, "", 0)
restore := stubDispatchCommands(
func(_ *log.Logger, _ []string) error { return nil },
func(_ *log.Logger, _ []string) error { return nil },
func(_ *log.Logger, _ []string) error { return nil },
func(_ *log.Logger, _ []string) error { return nil },
func(_ *log.Logger, _ []string) error { return nil },
func(_ *log.Logger, _ []string) error { return nil },
)
defer restore()
commands := []string{"startup", "shutdown", "etcd-restore", "daemon", "status", "intent", "help", "-h", "--help"}
for _, command := range commands {
code := runMain(logger, []string{"ananke", command})
if code != 0 {
t.Fatalf("expected exit code 0 for command %q, got %d", command, code)
}
}
}
// TestRunMainDispatchErrorPaths runs one orchestration or CLI step.
// Signature: TestRunMainDispatchErrorPaths(t *testing.T).
// Why: command failures must consistently map to exit code 1 for automation safety.
func TestRunMainDispatchErrorPaths(t *testing.T) {
logger := log.New(io.Discard, "", 0)
fail := errors.New("boom")
restore := stubDispatchCommands(
func(_ *log.Logger, _ []string) error { return fail },
func(_ *log.Logger, _ []string) error { return fail },
func(_ *log.Logger, _ []string) error { return fail },
func(_ *log.Logger, _ []string) error { return fail },
func(_ *log.Logger, _ []string) error { return fail },
func(_ *log.Logger, _ []string) error { return fail },
)
defer restore()
commands := []string{"startup", "shutdown", "etcd-restore", "daemon", "status", "intent"}
for _, command := range commands {
code := runMain(logger, []string{"ananke", command})
if code != 1 {
t.Fatalf("expected exit code 1 for failing command %q, got %d", command, code)
}
}
}
// TestRunMainUsageAndUnknownPaths runs one orchestration or CLI step.
// Signature: TestRunMainUsageAndUnknownPaths(t *testing.T).
// Why: no-arg and unknown-command branches should stay deterministic for shell callers.
func TestRunMainUsageAndUnknownPaths(t *testing.T) {
logger := log.New(io.Discard, "", 0)
if code := runMain(logger, []string{"ananke"}); code != 2 {
t.Fatalf("expected exit code 2 for no-arg usage path, got %d", code)
}
if code := runMain(logger, []string{"ananke", "definitely-unknown"}); code != 2 {
t.Fatalf("expected exit code 2 for unknown command path, got %d", code)
}
}
// stubDispatchCommands runs one orchestration or CLI step.
// Signature: stubDispatchCommands(startup, shutdown, etcdRestore, daemon, status, intent func(*log.Logger, []string) error) func().
// Why: keeps command-var overrides scoped and safely restorable across tests.
func stubDispatchCommands(
startup,
shutdown,
etcdRestore,
daemon,
status,
intent func(*log.Logger, []string) error,
) func() {
prevStartup := startupCommand
prevShutdown := shutdownCommand
prevEtcdRestore := etcdRestoreCommand
prevDaemon := daemonCommand
prevStatus := statusCommand
prevIntent := intentCommand
startupCommand = startup
shutdownCommand = shutdown
etcdRestoreCommand = etcdRestore
daemonCommand = daemon
statusCommand = status
intentCommand = intent
return func() {
startupCommand = prevStartup
shutdownCommand = prevShutdown
etcdRestoreCommand = prevEtcdRestore
daemonCommand = prevDaemon
statusCommand = prevStatus
intentCommand = prevIntent
}
}