diff --git a/cmd/vpnportal/main.go b/cmd/vpnportal/main.go index 842bed6..26862c9 100644 --- a/cmd/vpnportal/main.go +++ b/cmd/vpnportal/main.go @@ -21,12 +21,17 @@ func main() { os.Exit(run(os.Args[1:], os.Stdout, os.Stderr)) } -func run(args []string, stdout, stderr io.Writer) int { - cmd := "serve" - rest := args +// commandFor waehlt das Unterkommando. Fehlt es oder beginnt das erste +// Argument mit '-', gilt serve als Vorgabe. +func commandFor(args []string) (cmd string, rest []string) { if len(args) > 0 && len(args[0]) > 0 && args[0][0] != '-' { - cmd, rest = args[0], args[1:] + return args[0], args[1:] } + return "serve", args +} + +func run(args []string, stdout, stderr io.Writer) int { + cmd, rest := commandFor(args) switch cmd { case "version": diff --git a/cmd/vpnportal/main_test.go b/cmd/vpnportal/main_test.go index fe02445..e1e8ad0 100644 --- a/cmd/vpnportal/main_test.go +++ b/cmd/vpnportal/main_test.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "path/filepath" "strings" "testing" ) @@ -30,14 +31,57 @@ func TestRunUnknownCommand(t *testing.T) { } } -func TestRunNoArgsDefaultsToServe(t *testing.T) { - var out, errOut bytes.Buffer - // serve ohne --config muss mit Fehler abbrechen, nicht mit "unknown command" - code := run(nil, &out, &errOut) - if code == 2 { - t.Fatalf("no args must default to serve, not unknown-command") +// commandFor wird bewusst ohne Seiteneffekte getestet. Ein Test, der run(nil) +// aufruft, wuerde den Default-Konfigpfad /etc/vpnportal/config.yaml lesen — +// auf einem Zielhost also die Produktivkonfiguration, samt Verbindungsversuch +// zu Firewall und Domain Controllern. +func TestCommandForDispatch(t *testing.T) { + cases := []struct { + name string + args []string + wantCmd string + wantRest []string + }{ + {"ohne Argumente", nil, "serve", nil}, + {"leere Liste", []string{}, "serve", []string{}}, + {"nur Flag", []string{"--config", "/x.yaml"}, "serve", []string{"--config", "/x.yaml"}}, + {"kurzes Flag", []string{"-h"}, "serve", []string{"-h"}}, + {"version", []string{"version"}, "version", []string{}}, + {"serve mit Flag", []string{"serve", "--config", "/x"}, "serve", []string{"--config", "/x"}}, + {"check mit Flag", []string{"check", "--test-auth", "u"}, "check", []string{"--test-auth", "u"}}, + {"unbekannt", []string{"frobnicate"}, "frobnicate", []string{}}, + {"leeres Argument", []string{""}, "serve", []string{""}}, } - if !strings.Contains(errOut.String(), "config") { - t.Errorf("stderr should complain about missing config, got %q", errOut.String()) + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + cmd, rest := commandFor(tc.args) + if cmd != tc.wantCmd { + t.Errorf("cmd = %q, want %q", cmd, tc.wantCmd) + } + if len(rest) != len(tc.wantRest) { + t.Fatalf("rest = %q, want %q", rest, tc.wantRest) + } + for i := range rest { + if rest[i] != tc.wantRest[i] { + t.Errorf("rest[%d] = %q, want %q", i, rest[i], tc.wantRest[i]) + } + } + }) + } +} + +func TestRunServeReportsMissingConfigPath(t *testing.T) { + // Expliziter Pfad: der Test darf niemals die Konfiguration des Hosts lesen. + missing := filepath.Join(t.TempDir(), "gibtsnicht.yaml") + var out, errOut bytes.Buffer + code := run([]string{"--config", missing}, &out, &errOut) + if code == 2 { + t.Fatal("ein fuehrendes Flag muss auf serve abgebildet werden, nicht auf unknown-command") + } + if code == 0 { + t.Fatal("fehlende Konfiguration muss zum Abbruch fuehren") + } + if !strings.Contains(errOut.String(), missing) { + t.Errorf("stderr muss den Pfad nennen, got %q", errOut.String()) } }