fizzy-cli 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/bin/fizzy +0 -0
  3. package/cmd/board.go +1 -1
  4. package/cmd/board_create.go +1 -1
  5. package/cmd/board_delete.go +40 -0
  6. package/cmd/board_delete_test.go +121 -0
  7. package/cmd/board_show.go +40 -0
  8. package/cmd/board_show_test.go +113 -0
  9. package/cmd/board_update.go +72 -0
  10. package/cmd/board_update_test.go +233 -0
  11. package/cmd/card_assign.go +1 -1
  12. package/cmd/card_close.go +1 -1
  13. package/cmd/card_create.go +1 -1
  14. package/cmd/card_delete.go +1 -1
  15. package/cmd/card_golden.go +1 -1
  16. package/cmd/card_list.go +62 -1
  17. package/cmd/card_list_test.go +225 -0
  18. package/cmd/card_not_now.go +1 -1
  19. package/cmd/card_reaction.go +13 -0
  20. package/cmd/card_reaction_create.go +46 -0
  21. package/cmd/card_reaction_create_test.go +148 -0
  22. package/cmd/card_reaction_delete.go +46 -0
  23. package/cmd/card_reaction_delete_test.go +112 -0
  24. package/cmd/card_reaction_list.go +51 -0
  25. package/cmd/card_reaction_list_test.go +127 -0
  26. package/cmd/card_reopen.go +1 -1
  27. package/cmd/card_tag.go +1 -1
  28. package/cmd/card_triage.go +1 -1
  29. package/cmd/card_ungolden.go +1 -1
  30. package/cmd/card_untriage.go +1 -1
  31. package/cmd/card_unwatch.go +1 -1
  32. package/cmd/card_update.go +1 -1
  33. package/cmd/card_watch.go +1 -1
  34. package/cmd/column_create.go +1 -1
  35. package/cmd/column_delete.go +40 -0
  36. package/cmd/column_delete_test.go +121 -0
  37. package/cmd/column_show.go +40 -0
  38. package/cmd/column_show_test.go +111 -0
  39. package/cmd/column_update.go +67 -0
  40. package/cmd/column_update_test.go +198 -0
  41. package/cmd/comment_create.go +1 -1
  42. package/cmd/comment_delete.go +1 -1
  43. package/cmd/comment_update.go +1 -1
  44. package/cmd/login.go +12 -12
  45. package/cmd/notification_unread.go +1 -1
  46. package/cmd/reaction.go +2 -2
  47. package/cmd/reaction_create.go +1 -1
  48. package/cmd/reaction_delete.go +1 -1
  49. package/cmd/step_create.go +1 -1
  50. package/cmd/step_delete.go +1 -1
  51. package/cmd/step_update.go +1 -1
  52. package/cmd/user.go +22 -0
  53. package/cmd/user_deactivate.go +40 -0
  54. package/cmd/user_deactivate_test.go +121 -0
  55. package/cmd/user_list.go +44 -0
  56. package/cmd/user_list_test.go +126 -0
  57. package/cmd/user_show.go +40 -0
  58. package/cmd/user_show_test.go +110 -0
  59. package/cmd/user_update.go +71 -0
  60. package/cmd/user_update_test.go +177 -0
  61. package/docs/API.md +63 -2
  62. package/internal/api/boards.go +34 -0
  63. package/internal/api/cards.go +40 -6
  64. package/internal/api/columns.go +63 -0
  65. package/internal/api/reactions.go +61 -0
  66. package/internal/api/types.go +17 -0
  67. package/internal/api/users.go +75 -0
  68. package/internal/ui/board_show.go +17 -0
  69. package/internal/ui/column_show.go +16 -0
  70. package/internal/ui/format.go +14 -1
  71. package/internal/ui/user_list.go +19 -0
  72. package/internal/ui/user_show.go +23 -0
  73. package/package.json +1 -1
@@ -0,0 +1,121 @@
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "net/http"
6
+ "net/http/httptest"
7
+ "testing"
8
+
9
+ "github.com/rogeriopvl/fizzy/internal/app"
10
+ "github.com/rogeriopvl/fizzy/internal/testutil"
11
+ )
12
+
13
+ func TestUserDeactivateCommandSuccess(t *testing.T) {
14
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
15
+ if r.URL.Path != "/users/user-123" {
16
+ t.Errorf("expected /users/user-123, got %s", r.URL.Path)
17
+ }
18
+ if r.Method != http.MethodDelete {
19
+ t.Errorf("expected DELETE, got %s", r.Method)
20
+ }
21
+
22
+ auth := r.Header.Get("Authorization")
23
+ if auth != "Bearer test-token" {
24
+ t.Errorf("expected Bearer test-token, got %s", auth)
25
+ }
26
+
27
+ w.WriteHeader(http.StatusNoContent)
28
+ }))
29
+ defer server.Close()
30
+
31
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
32
+ testApp := &app.App{Client: client}
33
+
34
+ cmd := userDeactivateCmd
35
+ cmd.SetContext(testApp.ToContext(context.Background()))
36
+
37
+ if err := handleDeactivateUser(cmd, "user-123"); err != nil {
38
+ t.Fatalf("handleDeactivateUser failed: %v", err)
39
+ }
40
+ }
41
+
42
+ func TestUserDeactivateCommandNotFound(t *testing.T) {
43
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
44
+ w.WriteHeader(http.StatusNotFound)
45
+ w.Write([]byte("User not found"))
46
+ }))
47
+ defer server.Close()
48
+
49
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
50
+ testApp := &app.App{Client: client}
51
+
52
+ cmd := userDeactivateCmd
53
+ cmd.SetContext(testApp.ToContext(context.Background()))
54
+
55
+ err := handleDeactivateUser(cmd, "nonexistent-user")
56
+ if err == nil {
57
+ t.Errorf("expected error for user not found")
58
+ }
59
+ if err.Error() != "deactivating user: unexpected status code 404: User not found" {
60
+ t.Errorf("expected user not found error, got %v", err)
61
+ }
62
+ }
63
+
64
+ func TestUserDeactivateCommandForbidden(t *testing.T) {
65
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
66
+ w.WriteHeader(http.StatusForbidden)
67
+ w.Write([]byte("You don't have permission to deactivate this user"))
68
+ }))
69
+ defer server.Close()
70
+
71
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
72
+ testApp := &app.App{Client: client}
73
+
74
+ cmd := userDeactivateCmd
75
+ cmd.SetContext(testApp.ToContext(context.Background()))
76
+
77
+ err := handleDeactivateUser(cmd, "user-123")
78
+ if err == nil {
79
+ t.Errorf("expected error for forbidden access")
80
+ }
81
+ if err.Error() != "deactivating user: unexpected status code 403: You don't have permission to deactivate this user" {
82
+ t.Errorf("expected permission error, got %v", err)
83
+ }
84
+ }
85
+
86
+ func TestUserDeactivateCommandAPIError(t *testing.T) {
87
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
88
+ w.WriteHeader(http.StatusInternalServerError)
89
+ w.Write([]byte("Internal Server Error"))
90
+ }))
91
+ defer server.Close()
92
+
93
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
94
+ testApp := &app.App{Client: client}
95
+
96
+ cmd := userDeactivateCmd
97
+ cmd.SetContext(testApp.ToContext(context.Background()))
98
+
99
+ err := handleDeactivateUser(cmd, "user-123")
100
+ if err == nil {
101
+ t.Errorf("expected error for API failure")
102
+ }
103
+ if err.Error() != "deactivating user: unexpected status code 500: Internal Server Error" {
104
+ t.Errorf("expected API error, got %v", err)
105
+ }
106
+ }
107
+
108
+ func TestUserDeactivateCommandNoClient(t *testing.T) {
109
+ testApp := &app.App{}
110
+
111
+ cmd := userDeactivateCmd
112
+ cmd.SetContext(testApp.ToContext(context.Background()))
113
+
114
+ err := handleDeactivateUser(cmd, "user-123")
115
+ if err == nil {
116
+ t.Errorf("expected error when client not available")
117
+ }
118
+ if err.Error() != "API client not available" {
119
+ t.Errorf("expected 'client not available' error, got %v", err)
120
+ }
121
+ }
@@ -0,0 +1,44 @@
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+
7
+ "github.com/rogeriopvl/fizzy/internal/app"
8
+ "github.com/rogeriopvl/fizzy/internal/ui"
9
+ "github.com/spf13/cobra"
10
+ )
11
+
12
+ var userListCmd = &cobra.Command{
13
+ Use: "list",
14
+ Short: "List all users",
15
+ Long: `Retrieve and display all users from the current account`,
16
+ Run: func(cmd *cobra.Command, args []string) {
17
+ if err := handleListUsers(cmd); err != nil {
18
+ fmt.Fprintf(cmd.OutOrStderr(), "Error: %v\n", err)
19
+ }
20
+ },
21
+ }
22
+
23
+ func handleListUsers(cmd *cobra.Command) error {
24
+ a := app.FromContext(cmd.Context())
25
+ if a == nil || a.Client == nil {
26
+ return fmt.Errorf("API client not available")
27
+ }
28
+
29
+ users, err := a.Client.GetUsers(context.Background())
30
+ if err != nil {
31
+ return fmt.Errorf("fetching users: %w", err)
32
+ }
33
+
34
+ if len(users) == 0 {
35
+ fmt.Println("No users found")
36
+ return nil
37
+ }
38
+
39
+ return ui.DisplayUsers(cmd.OutOrStdout(), users)
40
+ }
41
+
42
+ func init() {
43
+ userCmd.AddCommand(userListCmd)
44
+ }
@@ -0,0 +1,126 @@
1
+ package cmd
2
+
3
+ import (
4
+ "bytes"
5
+ "context"
6
+ "encoding/json"
7
+ "net/http"
8
+ "net/http/httptest"
9
+ "testing"
10
+
11
+ "github.com/rogeriopvl/fizzy/internal/api"
12
+ "github.com/rogeriopvl/fizzy/internal/app"
13
+ "github.com/rogeriopvl/fizzy/internal/testutil"
14
+ )
15
+
16
+ func TestUserListCommand(t *testing.T) {
17
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
18
+ if r.URL.Path != "/users" {
19
+ t.Errorf("expected /users, got %s", r.URL.Path)
20
+ }
21
+ if r.Method != http.MethodGet {
22
+ t.Errorf("expected GET, got %s", r.Method)
23
+ }
24
+
25
+ auth := r.Header.Get("Authorization")
26
+ if auth == "" {
27
+ t.Error("missing Authorization header")
28
+ }
29
+ if auth != "Bearer test-token" {
30
+ t.Errorf("expected Bearer test-token, got %s", auth)
31
+ }
32
+
33
+ w.Header().Set("Content-Type", "application/json")
34
+ response := []api.User{
35
+ {
36
+ ID: "user-123",
37
+ Name: "John Doe",
38
+ Email: "john@example.com",
39
+ Role: "admin",
40
+ Active: true,
41
+ CreatedAt: "2025-01-01T00:00:00Z",
42
+ },
43
+ {
44
+ ID: "user-456",
45
+ Name: "Jane Smith",
46
+ Email: "jane@example.com",
47
+ Role: "member",
48
+ Active: false,
49
+ CreatedAt: "2025-01-02T00:00:00Z",
50
+ },
51
+ }
52
+ json.NewEncoder(w).Encode(response)
53
+ }))
54
+ defer server.Close()
55
+
56
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
57
+ testApp := &app.App{Client: client}
58
+
59
+ cmd := userListCmd
60
+ cmd.SetContext(testApp.ToContext(context.Background()))
61
+ cmd.SetOut(&bytes.Buffer{})
62
+
63
+ if err := handleListUsers(cmd); err != nil {
64
+ t.Fatalf("handleListUsers failed: %v", err)
65
+ }
66
+ }
67
+
68
+ func TestUserListCommandNoUsers(t *testing.T) {
69
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
70
+ w.Header().Set("Content-Type", "application/json")
71
+ json.NewEncoder(w).Encode([]api.User{})
72
+ }))
73
+ defer server.Close()
74
+
75
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
76
+ testApp := &app.App{Client: client}
77
+
78
+ cmd := userListCmd
79
+ cmd.SetContext(testApp.ToContext(context.Background()))
80
+ cmd.SetOut(&bytes.Buffer{})
81
+
82
+ if err := handleListUsers(cmd); err != nil {
83
+ t.Fatalf("handleListUsers failed: %v", err)
84
+ }
85
+ }
86
+
87
+ func TestUserListCommandAPIError(t *testing.T) {
88
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
89
+ w.WriteHeader(http.StatusInternalServerError)
90
+ w.Write([]byte("Internal Server Error"))
91
+ }))
92
+ defer server.Close()
93
+
94
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
95
+ testApp := &app.App{Client: client}
96
+
97
+ cmd := userListCmd
98
+ cmd.SetContext(testApp.ToContext(context.Background()))
99
+
100
+ cmd.SetOut(&bytes.Buffer{})
101
+
102
+ err := handleListUsers(cmd)
103
+ if err == nil {
104
+ t.Errorf("expected error for API failure")
105
+ }
106
+ if err.Error() != "fetching users: unexpected status code 500: Internal Server Error" {
107
+ t.Errorf("expected API error, got %v", err)
108
+ }
109
+ }
110
+
111
+ func TestUserListCommandNoClient(t *testing.T) {
112
+ testApp := &app.App{}
113
+
114
+ cmd := userListCmd
115
+ cmd.SetContext(testApp.ToContext(context.Background()))
116
+
117
+ cmd.SetOut(&bytes.Buffer{})
118
+
119
+ err := handleListUsers(cmd)
120
+ if err == nil {
121
+ t.Errorf("expected error when client not available")
122
+ }
123
+ if err.Error() != "API client not available" {
124
+ t.Errorf("expected 'client not available' error, got %v", err)
125
+ }
126
+ }
@@ -0,0 +1,40 @@
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+
7
+ "github.com/rogeriopvl/fizzy/internal/app"
8
+ "github.com/rogeriopvl/fizzy/internal/ui"
9
+ "github.com/spf13/cobra"
10
+ )
11
+
12
+ var userShowCmd = &cobra.Command{
13
+ Use: "show <user_id>",
14
+ Short: "Show user details",
15
+ Long: `Retrieve and display detailed information about a specific user`,
16
+ Args: cobra.ExactArgs(1),
17
+ Run: func(cmd *cobra.Command, args []string) {
18
+ if err := handleShowUser(cmd, args[0]); err != nil {
19
+ fmt.Fprintf(cmd.OutOrStderr(), "Error: %v\n", err)
20
+ }
21
+ },
22
+ }
23
+
24
+ func handleShowUser(cmd *cobra.Command, userID string) error {
25
+ a := app.FromContext(cmd.Context())
26
+ if a == nil || a.Client == nil {
27
+ return fmt.Errorf("API client not available")
28
+ }
29
+
30
+ user, err := a.Client.GetUser(context.Background(), userID)
31
+ if err != nil {
32
+ return fmt.Errorf("fetching user: %w", err)
33
+ }
34
+
35
+ return ui.DisplayUser(cmd.OutOrStdout(), user)
36
+ }
37
+
38
+ func init() {
39
+ userCmd.AddCommand(userShowCmd)
40
+ }
@@ -0,0 +1,110 @@
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "encoding/json"
6
+ "net/http"
7
+ "net/http/httptest"
8
+ "testing"
9
+
10
+ "github.com/rogeriopvl/fizzy/internal/api"
11
+ "github.com/rogeriopvl/fizzy/internal/app"
12
+ "github.com/rogeriopvl/fizzy/internal/testutil"
13
+ )
14
+
15
+ func TestUserShowCommand(t *testing.T) {
16
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
17
+ if r.URL.Path != "/users/user-123" {
18
+ t.Errorf("expected /users/user-123, got %s", r.URL.Path)
19
+ }
20
+ if r.Method != http.MethodGet {
21
+ t.Errorf("expected GET, got %s", r.Method)
22
+ }
23
+
24
+ auth := r.Header.Get("Authorization")
25
+ if auth != "Bearer test-token" {
26
+ t.Errorf("expected Bearer test-token, got %s", auth)
27
+ }
28
+
29
+ w.Header().Set("Content-Type", "application/json")
30
+ response := api.User{
31
+ ID: "user-123",
32
+ Name: "John Doe",
33
+ Email: "john@example.com",
34
+ Role: "admin",
35
+ Active: true,
36
+ CreatedAt: "2025-01-01T00:00:00Z",
37
+ }
38
+ json.NewEncoder(w).Encode(response)
39
+ }))
40
+ defer server.Close()
41
+
42
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
43
+ testApp := &app.App{Client: client}
44
+
45
+ cmd := userShowCmd
46
+ cmd.SetContext(testApp.ToContext(context.Background()))
47
+
48
+ if err := handleShowUser(cmd, "user-123"); err != nil {
49
+ t.Fatalf("handleShowUser failed: %v", err)
50
+ }
51
+ }
52
+
53
+ func TestUserShowCommandNotFound(t *testing.T) {
54
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
55
+ w.WriteHeader(http.StatusNotFound)
56
+ w.Write([]byte("User not found"))
57
+ }))
58
+ defer server.Close()
59
+
60
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
61
+ testApp := &app.App{Client: client}
62
+
63
+ cmd := userShowCmd
64
+ cmd.SetContext(testApp.ToContext(context.Background()))
65
+
66
+ err := handleShowUser(cmd, "nonexistent-user")
67
+ if err == nil {
68
+ t.Errorf("expected error for user not found")
69
+ }
70
+ if err.Error() != "fetching user: unexpected status code 404: User not found" {
71
+ t.Errorf("expected user not found error, got %v", err)
72
+ }
73
+ }
74
+
75
+ func TestUserShowCommandAPIError(t *testing.T) {
76
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
77
+ w.WriteHeader(http.StatusInternalServerError)
78
+ w.Write([]byte("Internal Server Error"))
79
+ }))
80
+ defer server.Close()
81
+
82
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
83
+ testApp := &app.App{Client: client}
84
+
85
+ cmd := userShowCmd
86
+ cmd.SetContext(testApp.ToContext(context.Background()))
87
+
88
+ err := handleShowUser(cmd, "user-123")
89
+ if err == nil {
90
+ t.Errorf("expected error for API failure")
91
+ }
92
+ if err.Error() != "fetching user: unexpected status code 500: Internal Server Error" {
93
+ t.Errorf("expected API error, got %v", err)
94
+ }
95
+ }
96
+
97
+ func TestUserShowCommandNoClient(t *testing.T) {
98
+ testApp := &app.App{}
99
+
100
+ cmd := userShowCmd
101
+ cmd.SetContext(testApp.ToContext(context.Background()))
102
+
103
+ err := handleShowUser(cmd, "user-123")
104
+ if err == nil {
105
+ t.Errorf("expected error when client not available")
106
+ }
107
+ if err.Error() != "API client not available" {
108
+ t.Errorf("expected 'client not available' error, got %v", err)
109
+ }
110
+ }
@@ -0,0 +1,71 @@
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+
7
+ "github.com/rogeriopvl/fizzy/internal/api"
8
+ "github.com/rogeriopvl/fizzy/internal/app"
9
+ "github.com/spf13/cobra"
10
+ )
11
+
12
+ var userUpdateCmd = &cobra.Command{
13
+ Use: "update <user_id>",
14
+ Short: "Update a user",
15
+ Long: `Update user settings such as name and avatar.
16
+
17
+ Avatar must be provided as a URL (e.g., https://example.com/avatar.jpg).
18
+
19
+ Example:
20
+ fizzy user update user-123 --name "John Doe"
21
+ fizzy user update user-123 --avatar https://example.com/avatar.jpg`,
22
+ Args: cobra.ExactArgs(1),
23
+ Run: func(cmd *cobra.Command, args []string) {
24
+ if err := handleUpdateUser(cmd, args[0]); err != nil {
25
+ fmt.Fprintf(cmd.OutOrStderr(), "Error: %v\n", err)
26
+ }
27
+ },
28
+ }
29
+
30
+ func handleUpdateUser(cmd *cobra.Command, userID string) error {
31
+ if !cmd.Flags().Changed("name") && !cmd.Flags().Changed("avatar") {
32
+ return fmt.Errorf("at least one flag must be provided (--name or --avatar)")
33
+ }
34
+
35
+ a := app.FromContext(cmd.Context())
36
+ if a == nil || a.Client == nil {
37
+ return fmt.Errorf("API client not available")
38
+ }
39
+
40
+ payload := api.UpdateUserPayload{}
41
+
42
+ if cmd.Flags().Changed("name") {
43
+ name, err := cmd.Flags().GetString("name")
44
+ if err != nil {
45
+ return fmt.Errorf("invalid name flag: %w", err)
46
+ }
47
+ payload.Name = name
48
+ }
49
+ if cmd.Flags().Changed("avatar") {
50
+ avatar, err := cmd.Flags().GetString("avatar")
51
+ if err != nil {
52
+ return fmt.Errorf("invalid avatar flag: %w", err)
53
+ }
54
+ payload.Avatar = avatar
55
+ }
56
+
57
+ err := a.Client.PutUser(context.Background(), userID, payload)
58
+ if err != nil {
59
+ return fmt.Errorf("updating user: %w", err)
60
+ }
61
+
62
+ fmt.Fprintf(cmd.OutOrStdout(), "✓ User '%s' updated successfully\n", userID)
63
+ return nil
64
+ }
65
+
66
+ func init() {
67
+ userUpdateCmd.Flags().StringP("name", "n", "", "User name")
68
+ userUpdateCmd.Flags().String("avatar", "", "Avatar URL (e.g., https://example.com/avatar.jpg)")
69
+
70
+ userCmd.AddCommand(userUpdateCmd)
71
+ }
@@ -0,0 +1,177 @@
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "encoding/json"
6
+ "io"
7
+ "net/http"
8
+ "net/http/httptest"
9
+ "testing"
10
+
11
+ "github.com/rogeriopvl/fizzy/internal/api"
12
+ "github.com/rogeriopvl/fizzy/internal/app"
13
+ "github.com/rogeriopvl/fizzy/internal/testutil"
14
+ "github.com/spf13/cobra"
15
+ )
16
+
17
+ func TestUserUpdateCommandSuccess(t *testing.T) {
18
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
19
+ if r.URL.Path != "/users/user-123" {
20
+ t.Errorf("expected /users/user-123, got %s", r.URL.Path)
21
+ }
22
+ if r.Method != http.MethodPut {
23
+ t.Errorf("expected PUT, got %s", r.Method)
24
+ }
25
+
26
+ auth := r.Header.Get("Authorization")
27
+ if auth != "Bearer test-token" {
28
+ t.Errorf("expected Bearer test-token, got %s", auth)
29
+ }
30
+
31
+ body, _ := io.ReadAll(r.Body)
32
+ var payload map[string]api.UpdateUserPayload
33
+ if err := json.Unmarshal(body, &payload); err != nil {
34
+ t.Fatalf("failed to unmarshal request body: %v", err)
35
+ }
36
+
37
+ userPayload := payload["user"]
38
+ if userPayload.Name != "Updated Name" {
39
+ t.Errorf("expected name 'Updated Name', got %s", userPayload.Name)
40
+ }
41
+
42
+ w.WriteHeader(http.StatusNoContent)
43
+ }))
44
+ defer server.Close()
45
+
46
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
47
+ testApp := &app.App{Client: client}
48
+
49
+ cmd := userUpdateCmd
50
+ cmd.SetContext(testApp.ToContext(context.Background()))
51
+ cmd.ParseFlags([]string{"--name", "Updated Name"})
52
+
53
+ if err := handleUpdateUser(cmd, "user-123"); err != nil {
54
+ t.Fatalf("handleUpdateUser failed: %v", err)
55
+ }
56
+ }
57
+
58
+ func TestUserUpdateCommandWithAllFlags(t *testing.T) {
59
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
60
+ if r.Method != http.MethodPut {
61
+ t.Errorf("expected PUT, got %s", r.Method)
62
+ }
63
+
64
+ body, _ := io.ReadAll(r.Body)
65
+ var payload map[string]api.UpdateUserPayload
66
+ json.Unmarshal(body, &payload)
67
+
68
+ userPayload := payload["user"]
69
+ if userPayload.Name != "Updated Name" {
70
+ t.Errorf("expected name 'Updated Name', got %s", userPayload.Name)
71
+ }
72
+ if userPayload.Avatar != "https://example.com/avatar.png" {
73
+ t.Errorf("expected avatar 'https://example.com/avatar.png', got %s", userPayload.Avatar)
74
+ }
75
+
76
+ w.WriteHeader(http.StatusNoContent)
77
+ }))
78
+ defer server.Close()
79
+
80
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
81
+ testApp := &app.App{Client: client}
82
+
83
+ cmd := userUpdateCmd
84
+ cmd.SetContext(testApp.ToContext(context.Background()))
85
+ cmd.ParseFlags([]string{
86
+ "--name", "Updated Name",
87
+ "--avatar", "https://example.com/avatar.png",
88
+ })
89
+
90
+ if err := handleUpdateUser(cmd, "user-123"); err != nil {
91
+ t.Fatalf("handleUpdateUser failed: %v", err)
92
+ }
93
+ }
94
+
95
+ func TestUserUpdateCommandNoFlags(t *testing.T) {
96
+ client := testutil.NewTestClient("http://localhost", "", "", "test-token")
97
+ testApp := &app.App{Client: client}
98
+
99
+ cmd := &cobra.Command{
100
+ Use: "update <user_id>",
101
+ Args: cobra.ExactArgs(1),
102
+ }
103
+ cmd.Flags().String("name", "", "User name")
104
+ cmd.Flags().String("avatar", "", "Avatar URL or file path")
105
+
106
+ cmd.SetContext(testApp.ToContext(context.Background()))
107
+
108
+ err := handleUpdateUser(cmd, "user-123")
109
+ if err == nil {
110
+ t.Errorf("expected error when no flags provided")
111
+ }
112
+ if err.Error() != "at least one flag must be provided (--name or --avatar)" {
113
+ t.Errorf("expected flag requirement error, got %v", err)
114
+ }
115
+ }
116
+
117
+ func TestUserUpdateCommandNotFound(t *testing.T) {
118
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
119
+ w.WriteHeader(http.StatusNotFound)
120
+ w.Write([]byte("User not found"))
121
+ }))
122
+ defer server.Close()
123
+
124
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
125
+ testApp := &app.App{Client: client}
126
+
127
+ cmd := userUpdateCmd
128
+ cmd.SetContext(testApp.ToContext(context.Background()))
129
+ cmd.ParseFlags([]string{"--name", "Updated Name"})
130
+
131
+ err := handleUpdateUser(cmd, "nonexistent-user")
132
+ if err == nil {
133
+ t.Errorf("expected error for user not found")
134
+ }
135
+ if err.Error() != "updating user: unexpected status code 404: User not found" {
136
+ t.Errorf("expected user not found error, got %v", err)
137
+ }
138
+ }
139
+
140
+ func TestUserUpdateCommandAPIError(t *testing.T) {
141
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
142
+ w.WriteHeader(http.StatusInternalServerError)
143
+ w.Write([]byte("Internal Server Error"))
144
+ }))
145
+ defer server.Close()
146
+
147
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
148
+ testApp := &app.App{Client: client}
149
+
150
+ cmd := userUpdateCmd
151
+ cmd.SetContext(testApp.ToContext(context.Background()))
152
+ cmd.ParseFlags([]string{"--name", "Updated Name"})
153
+
154
+ err := handleUpdateUser(cmd, "user-123")
155
+ if err == nil {
156
+ t.Errorf("expected error for API failure")
157
+ }
158
+ if err.Error() != "updating user: unexpected status code 500: Internal Server Error" {
159
+ t.Errorf("expected API error, got %v", err)
160
+ }
161
+ }
162
+
163
+ func TestUserUpdateCommandNoClient(t *testing.T) {
164
+ testApp := &app.App{}
165
+
166
+ cmd := userUpdateCmd
167
+ cmd.SetContext(testApp.ToContext(context.Background()))
168
+ cmd.ParseFlags([]string{"--name", "Updated Name"})
169
+
170
+ err := handleUpdateUser(cmd, "user-123")
171
+ if err == nil {
172
+ t.Errorf("expected error when client not available")
173
+ }
174
+ if err.Error() != "API client not available" {
175
+ t.Errorf("expected 'client not available' error, got %v", err)
176
+ }
177
+ }