package app import ( "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "time" ) func withStdin(t *testing.T, input string, fn func()) { t.Helper() old := os.Stdin r, w, err := os.Pipe() if err != nil { t.Fatal(err) } if _, err := w.WriteString(input); err != nil { t.Fatal(err) } _ = w.Close() os.Stdin = r defer func() { os.Stdin = old }() fn() } func TestCLIAuthWorkflowCommands(t *testing.T) { configFile := filepath.Join(t.TempDir(), "client.json") t.Setenv("GITOOCEAN_CONFIG", configFile) var logoutAuth string ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.Method + " " + r.URL.Path { case "POST /api/register": writeJSON(w, http.StatusCreated, User{ID: 1, Email: "alice@example.com", Username: "alice", IsAdmin: true}) case "POST /api/login": writeJSON(w, http.StatusOK, map[string]any{"user": User{ID: 1, Email: "alice@example.com", Username: "alice", IsAdmin: true}, "token": "tok", "expires_at": time.Now().UTC().Add(tokenTTL)}) case "GET /api/me": if r.Header.Get("Authorization") != "Bearer tok" { t.Fatalf("missing whoami auth: %q", r.Header.Get("Authorization")) } writeJSON(w, http.StatusOK, User{ID: 1, Email: "alice@example.com", Username: "alice", IsAdmin: true}) case "POST /api/logout": logoutAuth = r.Header.Get("Authorization") writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) default: t.Fatalf("unexpected auth request %s %s", r.Method, r.URL.Path) } })) defer ts.Close() out := captureStdout(t, func() { withStdin(t, "alice@example.com\nalice\npassword123\n", func() { if err := cliRegister([]string{"--server", ts.URL}); err != nil { t.Fatal(err) } }) }) if !strings.Contains(out, "Registered alice") || !strings.Contains(out, "admin account") { t.Fatalf("register output: %s", out) } out = captureStdout(t, func() { withStdin(t, "alice\npassword123\n", func() { if err := cliLogin([]string{"--server", ts.URL}); err != nil { t.Fatal(err) } }) }) if !strings.Contains(out, "logged in as alice") { t.Fatalf("login output: %s", out) } cfg, err := loadConfig() if err != nil { t.Fatal(err) } if cfg.Server != ts.URL || cfg.Token != "tok" || cfg.Username != "alice" { t.Fatalf("unexpected saved config: %#v", cfg) } out = captureStdout(t, func() { if err := cliWhoami(nil); err != nil { t.Fatal(err) } }) if !strings.Contains(out, "Username: alice") || !strings.Contains(out, "Admin: true") { t.Fatalf("whoami output: %s", out) } out = captureStdout(t, func() { if err := cliLogout(nil); err != nil { t.Fatal(err) } }) if logoutAuth != "Bearer tok" || !strings.Contains(out, "logged out") { t.Fatalf("logout auth=%q output=%s", logoutAuth, out) } if _, err := os.Stat(configFile); !os.IsNotExist(err) { t.Fatalf("expected config file removed, stat err=%v", err) } } func TestCLICloneUsage(t *testing.T) { if err := cliClone(nil); err == nil || !strings.Contains(err.Error(), "usage") { t.Fatalf("expected clone usage, got %v", err) } }