fizzy-cli 0.8.0 → 0.9.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 (156) hide show
  1. package/package.json +2 -1
  2. package/.env +0 -1
  3. package/.github/workflows/release.yml +0 -29
  4. package/.github/workflows/tests.yml +0 -24
  5. package/AGENTS.md +0 -33
  6. package/CHANGELOG.md +0 -194
  7. package/Makefile +0 -43
  8. package/bin/fizzy +0 -0
  9. package/cmd/account.go +0 -14
  10. package/cmd/account_list.go +0 -44
  11. package/cmd/account_list_test.go +0 -118
  12. package/cmd/board.go +0 -49
  13. package/cmd/board_create.go +0 -59
  14. package/cmd/board_create_test.go +0 -141
  15. package/cmd/board_delete.go +0 -40
  16. package/cmd/board_delete_test.go +0 -121
  17. package/cmd/board_list.go +0 -44
  18. package/cmd/board_list_test.go +0 -115
  19. package/cmd/board_show.go +0 -40
  20. package/cmd/board_show_test.go +0 -113
  21. package/cmd/board_test.go +0 -92
  22. package/cmd/board_update.go +0 -72
  23. package/cmd/board_update_test.go +0 -233
  24. package/cmd/card.go +0 -24
  25. package/cmd/card_assign.go +0 -55
  26. package/cmd/card_assign_test.go +0 -130
  27. package/cmd/card_close.go +0 -46
  28. package/cmd/card_close_test.go +0 -92
  29. package/cmd/card_create.go +0 -72
  30. package/cmd/card_create_test.go +0 -186
  31. package/cmd/card_delete.go +0 -46
  32. package/cmd/card_delete_test.go +0 -92
  33. package/cmd/card_golden.go +0 -46
  34. package/cmd/card_golden_test.go +0 -92
  35. package/cmd/card_list.go +0 -114
  36. package/cmd/card_list_test.go +0 -373
  37. package/cmd/card_not_now.go +0 -46
  38. package/cmd/card_not_now_test.go +0 -92
  39. package/cmd/card_reaction.go +0 -13
  40. package/cmd/card_reaction_create.go +0 -46
  41. package/cmd/card_reaction_create_test.go +0 -148
  42. package/cmd/card_reaction_delete.go +0 -46
  43. package/cmd/card_reaction_delete_test.go +0 -112
  44. package/cmd/card_reaction_list.go +0 -51
  45. package/cmd/card_reaction_list_test.go +0 -127
  46. package/cmd/card_reopen.go +0 -46
  47. package/cmd/card_reopen_test.go +0 -92
  48. package/cmd/card_show.go +0 -46
  49. package/cmd/card_show_test.go +0 -92
  50. package/cmd/card_tag.go +0 -51
  51. package/cmd/card_tag_test.go +0 -112
  52. package/cmd/card_triage.go +0 -46
  53. package/cmd/card_ungolden.go +0 -46
  54. package/cmd/card_ungolden_test.go +0 -92
  55. package/cmd/card_untriage.go +0 -46
  56. package/cmd/card_untriage_test.go +0 -92
  57. package/cmd/card_unwatch.go +0 -46
  58. package/cmd/card_unwatch_test.go +0 -92
  59. package/cmd/card_update.go +0 -82
  60. package/cmd/card_update_test.go +0 -149
  61. package/cmd/card_watch.go +0 -46
  62. package/cmd/card_watch_test.go +0 -92
  63. package/cmd/column.go +0 -14
  64. package/cmd/column_create.go +0 -79
  65. package/cmd/column_create_test.go +0 -178
  66. package/cmd/column_delete.go +0 -40
  67. package/cmd/column_delete_test.go +0 -121
  68. package/cmd/column_list.go +0 -44
  69. package/cmd/column_list_test.go +0 -138
  70. package/cmd/column_show.go +0 -40
  71. package/cmd/column_show_test.go +0 -111
  72. package/cmd/column_update.go +0 -67
  73. package/cmd/column_update_test.go +0 -198
  74. package/cmd/comment.go +0 -14
  75. package/cmd/comment_create.go +0 -51
  76. package/cmd/comment_create_test.go +0 -129
  77. package/cmd/comment_delete.go +0 -46
  78. package/cmd/comment_delete_test.go +0 -92
  79. package/cmd/comment_list.go +0 -51
  80. package/cmd/comment_list_test.go +0 -132
  81. package/cmd/comment_show.go +0 -46
  82. package/cmd/comment_show_test.go +0 -104
  83. package/cmd/comment_update.go +0 -51
  84. package/cmd/comment_update_test.go +0 -130
  85. package/cmd/login.go +0 -81
  86. package/cmd/login_test.go +0 -98
  87. package/cmd/notification.go +0 -14
  88. package/cmd/notification_list.go +0 -69
  89. package/cmd/notification_list_test.go +0 -288
  90. package/cmd/notification_read.go +0 -51
  91. package/cmd/notification_read_all.go +0 -38
  92. package/cmd/notification_read_all_test.go +0 -75
  93. package/cmd/notification_read_test.go +0 -138
  94. package/cmd/notification_unread.go +0 -44
  95. package/cmd/notification_unread_test.go +0 -99
  96. package/cmd/reaction.go +0 -13
  97. package/cmd/reaction_create.go +0 -46
  98. package/cmd/reaction_create_test.go +0 -113
  99. package/cmd/reaction_delete.go +0 -46
  100. package/cmd/reaction_delete_test.go +0 -92
  101. package/cmd/reaction_list.go +0 -51
  102. package/cmd/reaction_list_test.go +0 -125
  103. package/cmd/root.go +0 -38
  104. package/cmd/step.go +0 -14
  105. package/cmd/step_create.go +0 -53
  106. package/cmd/step_create_test.go +0 -171
  107. package/cmd/step_delete.go +0 -46
  108. package/cmd/step_delete_test.go +0 -92
  109. package/cmd/step_update.go +0 -66
  110. package/cmd/step_update_test.go +0 -190
  111. package/cmd/tag.go +0 -15
  112. package/cmd/tag_list.go +0 -47
  113. package/cmd/tag_list_test.go +0 -109
  114. package/cmd/use.go +0 -85
  115. package/cmd/use_test.go +0 -186
  116. package/cmd/user.go +0 -22
  117. package/cmd/user_deactivate.go +0 -40
  118. package/cmd/user_deactivate_test.go +0 -121
  119. package/cmd/user_list.go +0 -44
  120. package/cmd/user_list_test.go +0 -126
  121. package/cmd/user_show.go +0 -40
  122. package/cmd/user_show_test.go +0 -110
  123. package/cmd/user_update.go +0 -71
  124. package/cmd/user_update_test.go +0 -177
  125. package/go.mod +0 -31
  126. package/go.sum +0 -53
  127. package/internal/api/boards.go +0 -93
  128. package/internal/api/cards.go +0 -322
  129. package/internal/api/client.go +0 -99
  130. package/internal/api/columns.go +0 -113
  131. package/internal/api/comments.go +0 -108
  132. package/internal/api/identity.go +0 -24
  133. package/internal/api/notifications.go +0 -89
  134. package/internal/api/reactions.go +0 -130
  135. package/internal/api/steps.go +0 -101
  136. package/internal/api/tags.go +0 -24
  137. package/internal/api/types.go +0 -195
  138. package/internal/api/users.go +0 -75
  139. package/internal/app/app.go +0 -49
  140. package/internal/colors/colors.go +0 -32
  141. package/internal/config/config.go +0 -70
  142. package/internal/testutil/client.go +0 -26
  143. package/internal/ui/account_list.go +0 -14
  144. package/internal/ui/account_selector.go +0 -63
  145. package/internal/ui/board_list.go +0 -14
  146. package/internal/ui/board_show.go +0 -17
  147. package/internal/ui/card_list.go +0 -14
  148. package/internal/ui/card_show.go +0 -23
  149. package/internal/ui/column_list.go +0 -28
  150. package/internal/ui/column_show.go +0 -16
  151. package/internal/ui/comment_list.go +0 -25
  152. package/internal/ui/format.go +0 -27
  153. package/internal/ui/notification_list.go +0 -27
  154. package/internal/ui/reaction_list.go +0 -14
  155. package/internal/ui/user_list.go +0 -19
  156. package/internal/ui/user_show.go +0 -23
package/cmd/card_list.go DELETED
@@ -1,114 +0,0 @@
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/rogeriopvl/fizzy/internal/ui"
10
- "github.com/spf13/cobra"
11
- )
12
-
13
- var cardListCmd = &cobra.Command{
14
- Use: "list",
15
- Short: "List all cards",
16
- Long: `Retrieve and display cards from Fizzy with optional filters.
17
-
18
- Filter options:
19
- --tag <id> Filter by tag ID (can be used multiple times)
20
- --assignee <id> Filter by assignee user ID (can be used multiple times)
21
- --creator <id> Filter by creator user ID (can be used multiple times)
22
- --closer <id> Filter by user who closed the card (can be used multiple times)
23
- --card <id> Filter to specific card ID (can be used multiple times)
24
- --indexed-by Filter by status: all, closed, not_now, stalled, postponing_soon, golden
25
- --sorted-by Sort order: latest, newest, oldest
26
- --unassigned Show only unassigned cards
27
- --created-in Filter by creation date: today, yesterday, thisweek, lastweek, thismonth, lastmonth, thisyear, lastyear
28
- --closed-in Filter by closure date: today, yesterday, thisweek, lastweek, thismonth, lastmonth, thisyear, lastyear
29
- --search Search terms (can be used multiple times)`,
30
- Run: func(cmd *cobra.Command, args []string) {
31
- if err := handleListCards(cmd); err != nil {
32
- fmt.Fprintf(cmd.OutOrStderr(), "Error: %v\n", err)
33
- }
34
- },
35
- }
36
-
37
- func handleListCards(cmd *cobra.Command) error {
38
- a := app.FromContext(cmd.Context())
39
- if a == nil || a.Client == nil {
40
- return fmt.Errorf("API client not available")
41
- }
42
-
43
- if a.Config.SelectedBoard == "" {
44
- return fmt.Errorf("no board selected")
45
- }
46
-
47
- filters := api.CardFilters{
48
- BoardIDs: []string{a.Config.SelectedBoard},
49
- }
50
-
51
- if tags, _ := cmd.Flags().GetStringSlice("tag"); len(tags) > 0 {
52
- filters.TagIDs = tags
53
- }
54
- if assignees, _ := cmd.Flags().GetStringSlice("assignee"); len(assignees) > 0 {
55
- filters.AssigneeIDs = assignees
56
- }
57
- if creators, _ := cmd.Flags().GetStringSlice("creator"); len(creators) > 0 {
58
- filters.CreatorIDs = creators
59
- }
60
- if closers, _ := cmd.Flags().GetStringSlice("closer"); len(closers) > 0 {
61
- filters.CloserIDs = closers
62
- }
63
- if cardIDs, _ := cmd.Flags().GetStringSlice("card"); len(cardIDs) > 0 {
64
- filters.CardIDs = cardIDs
65
- }
66
- if searches, _ := cmd.Flags().GetStringSlice("search"); len(searches) > 0 {
67
- filters.Terms = searches
68
- }
69
-
70
- if indexedBy, _ := cmd.Flags().GetString("indexed-by"); indexedBy != "" {
71
- filters.IndexedBy = indexedBy
72
- }
73
- if sortedBy, _ := cmd.Flags().GetString("sorted-by"); sortedBy != "" {
74
- filters.SortedBy = sortedBy
75
- }
76
- if createdIn, _ := cmd.Flags().GetString("created-in"); createdIn != "" {
77
- filters.CreationStatus = createdIn
78
- }
79
- if closedIn, _ := cmd.Flags().GetString("closed-in"); closedIn != "" {
80
- filters.ClosureStatus = closedIn
81
- }
82
-
83
- if unassigned, _ := cmd.Flags().GetBool("unassigned"); unassigned {
84
- filters.AssignmentStatus = "unassigned"
85
- }
86
-
87
- cards, err := a.Client.GetCards(context.Background(), filters)
88
- if err != nil {
89
- return fmt.Errorf("fetching cards: %w", err)
90
- }
91
-
92
- if len(cards) == 0 {
93
- fmt.Println("No cards found")
94
- return nil
95
- }
96
-
97
- return ui.DisplayCards(cards)
98
- }
99
-
100
- func init() {
101
- cardListCmd.Flags().StringSliceP("tag", "t", []string{}, "Filter by tag ID (can be used multiple times)")
102
- cardListCmd.Flags().StringSliceP("assignee", "a", []string{}, "Filter by assignee user ID (can be used multiple times)")
103
- cardListCmd.Flags().StringSlice("creator", []string{}, "Filter by creator user ID (can be used multiple times)")
104
- cardListCmd.Flags().StringSlice("closer", []string{}, "Filter by closer user ID (can be used multiple times)")
105
- cardListCmd.Flags().StringSlice("card", []string{}, "Filter to specific card ID (can be used multiple times)")
106
- cardListCmd.Flags().String("indexed-by", "", "Filter by status: all, closed, not_now, stalled, postponing_soon, golden")
107
- cardListCmd.Flags().String("sorted-by", "", "Sort order: latest, newest, oldest")
108
- cardListCmd.Flags().BoolP("unassigned", "u", false, "Show only unassigned cards")
109
- cardListCmd.Flags().String("created-in", "", "Filter by creation date")
110
- cardListCmd.Flags().String("closed-in", "", "Filter by closure date")
111
- cardListCmd.Flags().StringSliceP("search", "s", []string{}, "Search terms (can be used multiple times)")
112
-
113
- cardCmd.AddCommand(cardListCmd)
114
- }
@@ -1,373 +0,0 @@
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/config"
13
- "github.com/rogeriopvl/fizzy/internal/testutil"
14
- "github.com/spf13/cobra"
15
- )
16
-
17
- func newCardListCmd() *cobra.Command {
18
- cmd := &cobra.Command{}
19
- cmd.Flags().StringSliceP("tag", "t", []string{}, "Filter by tag ID (can be used multiple times)")
20
- cmd.Flags().StringSliceP("assignee", "a", []string{}, "Filter by assignee user ID (can be used multiple times)")
21
- cmd.Flags().StringSlice("creator", []string{}, "Filter by creator user ID (can be used multiple times)")
22
- cmd.Flags().StringSlice("closer", []string{}, "Filter by closer user ID (can be used multiple times)")
23
- cmd.Flags().StringSlice("card", []string{}, "Filter to specific card ID (can be used multiple times)")
24
- cmd.Flags().String("indexed-by", "", "Filter by status: all, closed, not_now, stalled, postponing_soon, golden")
25
- cmd.Flags().String("sorted-by", "", "Sort order: latest, newest, oldest")
26
- cmd.Flags().BoolP("unassigned", "u", false, "Show only unassigned cards")
27
- cmd.Flags().String("created-in", "", "Filter by creation date")
28
- cmd.Flags().String("closed-in", "", "Filter by closure date")
29
- cmd.Flags().StringSliceP("search", "s", []string{}, "Search terms (can be used multiple times)")
30
- return cmd
31
- }
32
-
33
- func TestCardListCommand(t *testing.T) {
34
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
35
- if r.URL.Path != "/cards" {
36
- t.Errorf("expected /cards, got %s", r.URL.Path)
37
- }
38
- if r.Method != http.MethodGet {
39
- t.Errorf("expected GET, got %s", r.Method)
40
- }
41
-
42
- boardIDs := r.URL.Query()["board_ids[]"]
43
- if len(boardIDs) == 0 || boardIDs[0] != "board-123" {
44
- t.Errorf("expected board_ids[]=board-123 in query, got %v", boardIDs)
45
- }
46
-
47
- auth := r.Header.Get("Authorization")
48
- if auth != "Bearer test-token" {
49
- t.Errorf("expected Bearer test-token, got %s", auth)
50
- }
51
-
52
- w.Header().Set("Content-Type", "application/json")
53
- response := []api.Card{
54
- {
55
- ID: "card-123",
56
- Number: 1,
57
- Title: "Implement feature",
58
- Status: "in_progress",
59
- CreatedAt: "2025-01-01T00:00:00Z",
60
- },
61
- {
62
- ID: "card-456",
63
- Number: 2,
64
- Title: "Fix bug",
65
- Status: "todo",
66
- CreatedAt: "2025-01-02T00:00:00Z",
67
- },
68
- }
69
- json.NewEncoder(w).Encode(response)
70
- }))
71
- defer server.Close()
72
-
73
- client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
74
- testApp := &app.App{
75
- Client: client,
76
- Config: &config.Config{SelectedBoard: "board-123"},
77
- }
78
-
79
- cmd := cardListCmd
80
- cmd.SetContext(testApp.ToContext(context.Background()))
81
-
82
- if err := handleListCards(cmd); err != nil {
83
- t.Fatalf("handleListCards failed: %v", err)
84
- }
85
- }
86
-
87
- func TestCardListCommandNoCards(t *testing.T) {
88
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
89
- w.Header().Set("Content-Type", "application/json")
90
- json.NewEncoder(w).Encode([]api.Card{})
91
- }))
92
- defer server.Close()
93
-
94
- client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
95
- testApp := &app.App{
96
- Client: client,
97
- Config: &config.Config{SelectedBoard: "board-123"},
98
- }
99
-
100
- cmd := cardListCmd
101
- cmd.SetContext(testApp.ToContext(context.Background()))
102
-
103
- if err := handleListCards(cmd); err != nil {
104
- t.Fatalf("handleListCards failed: %v", err)
105
- }
106
- }
107
-
108
- func TestCardListCommandAPIError(t *testing.T) {
109
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
110
- w.WriteHeader(http.StatusInternalServerError)
111
- w.Write([]byte("Internal Server Error"))
112
- }))
113
- defer server.Close()
114
-
115
- client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
116
- testApp := &app.App{
117
- Client: client,
118
- Config: &config.Config{SelectedBoard: "board-123"},
119
- }
120
-
121
- cmd := cardListCmd
122
- cmd.SetContext(testApp.ToContext(context.Background()))
123
-
124
- err := handleListCards(cmd)
125
- if err == nil {
126
- t.Errorf("expected error for API failure")
127
- }
128
- if err.Error() != "fetching cards: unexpected status code 500: Internal Server Error" {
129
- t.Errorf("expected API error, got %v", err)
130
- }
131
- }
132
-
133
- func TestCardListCommandNoBoard(t *testing.T) {
134
- client := testutil.NewTestClient("http://localhost", "", "", "test-token")
135
- testApp := &app.App{
136
- Client: client,
137
- Config: &config.Config{SelectedBoard: ""},
138
- }
139
-
140
- cmd := cardListCmd
141
- cmd.SetContext(testApp.ToContext(context.Background()))
142
-
143
- err := handleListCards(cmd)
144
- if err == nil {
145
- t.Errorf("expected error when board not selected")
146
- }
147
- if err.Error() != "no board selected" {
148
- t.Errorf("expected 'no board selected' error, got %v", err)
149
- }
150
- }
151
-
152
- func TestCardListCommandNoClient(t *testing.T) {
153
- testApp := &app.App{}
154
-
155
- cmd := cardListCmd
156
- cmd.SetContext(testApp.ToContext(context.Background()))
157
-
158
- err := handleListCards(cmd)
159
- if err == nil {
160
- t.Errorf("expected error when client not available")
161
- }
162
- if err.Error() != "API client not available" {
163
- t.Errorf("expected 'client not available' error, got %v", err)
164
- }
165
- }
166
-
167
- func TestCardListCommandWithTagFilter(t *testing.T) {
168
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
169
- tagIDs := r.URL.Query()["tag_ids[]"]
170
- if len(tagIDs) != 2 || tagIDs[0] != "tag-123" || tagIDs[1] != "tag-456" {
171
- t.Errorf("expected tag_ids[]=tag-123&tag_ids[]=tag-456, got %v", tagIDs)
172
- }
173
-
174
- w.Header().Set("Content-Type", "application/json")
175
- json.NewEncoder(w).Encode([]api.Card{})
176
- }))
177
- defer server.Close()
178
-
179
- client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
180
- testApp := &app.App{
181
- Client: client,
182
- Config: &config.Config{SelectedBoard: "board-123"},
183
- }
184
-
185
- cmd := newCardListCmd()
186
- cmd.SetContext(testApp.ToContext(context.Background()))
187
- cmd.ParseFlags([]string{"--tag", "tag-123", "--tag", "tag-456"})
188
-
189
- if err := handleListCards(cmd); err != nil {
190
- t.Fatalf("handleListCards failed: %v", err)
191
- }
192
- }
193
-
194
- func TestCardListCommandWithAssigneeFilter(t *testing.T) {
195
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
196
- assigneeIDs := r.URL.Query()["assignee_ids[]"]
197
- if len(assigneeIDs) != 1 || assigneeIDs[0] != "user-123" {
198
- t.Errorf("expected assignee_ids[]=user-123, got %v", assigneeIDs)
199
- }
200
-
201
- w.Header().Set("Content-Type", "application/json")
202
- json.NewEncoder(w).Encode([]api.Card{})
203
- }))
204
- defer server.Close()
205
-
206
- client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
207
- testApp := &app.App{
208
- Client: client,
209
- Config: &config.Config{SelectedBoard: "board-123"},
210
- }
211
-
212
- cmd := newCardListCmd()
213
- cmd.SetContext(testApp.ToContext(context.Background()))
214
- cmd.ParseFlags([]string{"--assignee", "user-123"})
215
-
216
- if err := handleListCards(cmd); err != nil {
217
- t.Fatalf("handleListCards failed: %v", err)
218
- }
219
- }
220
-
221
- func TestCardListCommandWithIndexedByFilter(t *testing.T) {
222
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
223
- indexedBy := r.URL.Query().Get("indexed_by")
224
- if indexedBy != "closed" {
225
- t.Errorf("expected indexed_by=closed, got %s", indexedBy)
226
- }
227
-
228
- w.Header().Set("Content-Type", "application/json")
229
- json.NewEncoder(w).Encode([]api.Card{})
230
- }))
231
- defer server.Close()
232
-
233
- client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
234
- testApp := &app.App{
235
- Client: client,
236
- Config: &config.Config{SelectedBoard: "board-123"},
237
- }
238
-
239
- cmd := newCardListCmd()
240
- cmd.SetContext(testApp.ToContext(context.Background()))
241
- cmd.ParseFlags([]string{"--indexed-by", "closed"})
242
-
243
- if err := handleListCards(cmd); err != nil {
244
- t.Fatalf("handleListCards failed: %v", err)
245
- }
246
- }
247
-
248
- func TestCardListCommandWithSortedByFilter(t *testing.T) {
249
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
250
- sortedBy := r.URL.Query().Get("sorted_by")
251
- if sortedBy != "newest" {
252
- t.Errorf("expected sorted_by=newest, got %s", sortedBy)
253
- }
254
-
255
- w.Header().Set("Content-Type", "application/json")
256
- json.NewEncoder(w).Encode([]api.Card{})
257
- }))
258
- defer server.Close()
259
-
260
- client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
261
- testApp := &app.App{
262
- Client: client,
263
- Config: &config.Config{SelectedBoard: "board-123"},
264
- }
265
-
266
- cmd := newCardListCmd()
267
- cmd.SetContext(testApp.ToContext(context.Background()))
268
- cmd.ParseFlags([]string{"--sorted-by", "newest"})
269
-
270
- if err := handleListCards(cmd); err != nil {
271
- t.Fatalf("handleListCards failed: %v", err)
272
- }
273
- }
274
-
275
- func TestCardListCommandWithUnassignedFilter(t *testing.T) {
276
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
277
- assignmentStatus := r.URL.Query().Get("assignment_status")
278
- if assignmentStatus != "unassigned" {
279
- t.Errorf("expected assignment_status=unassigned, got %s", assignmentStatus)
280
- }
281
-
282
- w.Header().Set("Content-Type", "application/json")
283
- json.NewEncoder(w).Encode([]api.Card{})
284
- }))
285
- defer server.Close()
286
-
287
- client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
288
- testApp := &app.App{
289
- Client: client,
290
- Config: &config.Config{SelectedBoard: "board-123"},
291
- }
292
-
293
- cmd := newCardListCmd()
294
- cmd.SetContext(testApp.ToContext(context.Background()))
295
- cmd.ParseFlags([]string{"--unassigned"})
296
-
297
- if err := handleListCards(cmd); err != nil {
298
- t.Fatalf("handleListCards failed: %v", err)
299
- }
300
- }
301
-
302
- func TestCardListCommandWithSearchFilter(t *testing.T) {
303
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
304
- terms := r.URL.Query()["terms[]"]
305
- if len(terms) != 2 || terms[0] != "bug" || terms[1] != "critical" {
306
- t.Errorf("expected terms[]=bug&terms[]=critical, got %v", terms)
307
- }
308
-
309
- w.Header().Set("Content-Type", "application/json")
310
- json.NewEncoder(w).Encode([]api.Card{})
311
- }))
312
- defer server.Close()
313
-
314
- client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
315
- testApp := &app.App{
316
- Client: client,
317
- Config: &config.Config{SelectedBoard: "board-123"},
318
- }
319
-
320
- cmd := newCardListCmd()
321
- cmd.SetContext(testApp.ToContext(context.Background()))
322
- cmd.ParseFlags([]string{"--search", "bug", "--search", "critical"})
323
-
324
- if err := handleListCards(cmd); err != nil {
325
- t.Fatalf("handleListCards failed: %v", err)
326
- }
327
- }
328
-
329
- func TestCardListCommandWithMultipleFilters(t *testing.T) {
330
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
331
- boardIDs := r.URL.Query()["board_ids[]"]
332
- if len(boardIDs) == 0 || boardIDs[0] != "board-123" {
333
- t.Errorf("expected board_ids[]=board-123, got %v", boardIDs)
334
- }
335
-
336
- tagIDs := r.URL.Query()["tag_ids[]"]
337
- if len(tagIDs) != 1 || tagIDs[0] != "tag-123" {
338
- t.Errorf("expected tag_ids[]=tag-123, got %v", tagIDs)
339
- }
340
-
341
- assigneeIDs := r.URL.Query()["assignee_ids[]"]
342
- if len(assigneeIDs) != 1 || assigneeIDs[0] != "user-456" {
343
- t.Errorf("expected assignee_ids[]=user-456, got %v", assigneeIDs)
344
- }
345
-
346
- sortedBy := r.URL.Query().Get("sorted_by")
347
- if sortedBy != "latest" {
348
- t.Errorf("expected sorted_by=latest, got %s", sortedBy)
349
- }
350
-
351
- w.Header().Set("Content-Type", "application/json")
352
- json.NewEncoder(w).Encode([]api.Card{})
353
- }))
354
- defer server.Close()
355
-
356
- client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
357
- testApp := &app.App{
358
- Client: client,
359
- Config: &config.Config{SelectedBoard: "board-123"},
360
- }
361
-
362
- cmd := newCardListCmd()
363
- cmd.SetContext(testApp.ToContext(context.Background()))
364
- cmd.ParseFlags([]string{
365
- "--tag", "tag-123",
366
- "--assignee", "user-456",
367
- "--sorted-by", "latest",
368
- })
369
-
370
- if err := handleListCards(cmd); err != nil {
371
- t.Fatalf("handleListCards failed: %v", err)
372
- }
373
- }
@@ -1,46 +0,0 @@
1
- package cmd
2
-
3
- import (
4
- "context"
5
- "fmt"
6
- "strconv"
7
-
8
- "github.com/rogeriopvl/fizzy/internal/app"
9
- "github.com/spf13/cobra"
10
- )
11
-
12
- var cardNotNowCmd = &cobra.Command{
13
- Use: "not-now <card_number>",
14
- Short: "Move a card to Not Now status",
15
- Long: `Move an existing card to the "Not Now" status`,
16
- Args: cobra.ExactArgs(1),
17
- Run: func(cmd *cobra.Command, args []string) {
18
- if err := handleNotNowCard(cmd, args[0]); err != nil {
19
- fmt.Fprintf(cmd.OutOrStderr(), "Error: %v\n", err)
20
- }
21
- },
22
- }
23
-
24
- func handleNotNowCard(cmd *cobra.Command, cardNumber string) error {
25
- cardNum, err := strconv.Atoi(cardNumber)
26
- if err != nil {
27
- return fmt.Errorf("invalid card number: %w", err)
28
- }
29
-
30
- a := app.FromContext(cmd.Context())
31
- if a == nil || a.Client == nil {
32
- return fmt.Errorf("API client not available")
33
- }
34
-
35
- _, err = a.Client.PostCardNotNow(context.Background(), cardNum)
36
- if err != nil {
37
- return fmt.Errorf("moving card to not now: %w", err)
38
- }
39
-
40
- fmt.Fprintf(cmd.OutOrStdout(), "✓ Card #%d moved to Not Now successfully\n", cardNum)
41
- return nil
42
- }
43
-
44
- func init() {
45
- cardCmd.AddCommand(cardNotNowCmd)
46
- }
@@ -1,92 +0,0 @@
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 TestCardNotNowCommandSuccess(t *testing.T) {
14
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
15
- if r.URL.Path != "/cards/123/not_now" {
16
- t.Errorf("expected /cards/123/not_now, got %s", r.URL.Path)
17
- }
18
- if r.Method != http.MethodPost {
19
- t.Errorf("expected POST, 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 := cardNotNowCmd
35
- cmd.SetContext(testApp.ToContext(context.Background()))
36
-
37
- if err := handleNotNowCard(cmd, "123"); err != nil {
38
- t.Fatalf("handleNotNowCard failed: %v", err)
39
- }
40
- }
41
-
42
- func TestCardNotNowCommandInvalidCardNumber(t *testing.T) {
43
- testApp := &app.App{}
44
-
45
- cmd := cardNotNowCmd
46
- cmd.SetContext(testApp.ToContext(context.Background()))
47
-
48
- err := handleNotNowCard(cmd, "not-a-number")
49
- if err == nil {
50
- t.Errorf("expected error for invalid card number")
51
- }
52
- if err.Error() != "invalid card number: strconv.Atoi: parsing \"not-a-number\": invalid syntax" {
53
- t.Errorf("expected invalid card number error, got %v", err)
54
- }
55
- }
56
-
57
- func TestCardNotNowCommandNoClient(t *testing.T) {
58
- testApp := &app.App{}
59
-
60
- cmd := cardNotNowCmd
61
- cmd.SetContext(testApp.ToContext(context.Background()))
62
-
63
- err := handleNotNowCard(cmd, "123")
64
- if err == nil {
65
- t.Errorf("expected error when client not available")
66
- }
67
- if err.Error() != "API client not available" {
68
- t.Errorf("expected 'client not available' error, got %v", err)
69
- }
70
- }
71
-
72
- func TestCardNotNowCommandAPIError(t *testing.T) {
73
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
74
- w.WriteHeader(http.StatusInternalServerError)
75
- w.Write([]byte("Internal Server Error"))
76
- }))
77
- defer server.Close()
78
-
79
- client := testutil.NewTestClient(server.URL, "", "", "test-token")
80
- testApp := &app.App{Client: client}
81
-
82
- cmd := cardNotNowCmd
83
- cmd.SetContext(testApp.ToContext(context.Background()))
84
-
85
- err := handleNotNowCard(cmd, "123")
86
- if err == nil {
87
- t.Errorf("expected error for API failure")
88
- }
89
- if err.Error() != "moving card to not now: unexpected status code 500: Internal Server Error" {
90
- t.Errorf("expected API error, got %v", err)
91
- }
92
- }
@@ -1,13 +0,0 @@
1
- package cmd
2
-
3
- import "github.com/spf13/cobra"
4
-
5
- var cardReactionCmd = &cobra.Command{
6
- Use: "reaction",
7
- Short: "Manage card reactions",
8
- Long: `Manage reactions (boosts) on cards in Fizzy`,
9
- }
10
-
11
- func init() {
12
- cardCmd.AddCommand(cardReactionCmd)
13
- }
@@ -1,46 +0,0 @@
1
- package cmd
2
-
3
- import (
4
- "context"
5
- "fmt"
6
- "strconv"
7
-
8
- "github.com/rogeriopvl/fizzy/internal/app"
9
- "github.com/spf13/cobra"
10
- )
11
-
12
- var cardReactionCreateCmd = &cobra.Command{
13
- Use: "create <card_number> <emoji>",
14
- Short: "Create a reaction on a card",
15
- Long: `Create an emoji reaction (boost) on a card`,
16
- Args: cobra.ExactArgs(2),
17
- Run: func(cmd *cobra.Command, args []string) {
18
- if err := handleCreateCardReaction(cmd, args[0], args[1]); err != nil {
19
- fmt.Fprintf(cmd.OutOrStderr(), "Error: %v\n", err)
20
- }
21
- },
22
- }
23
-
24
- func handleCreateCardReaction(cmd *cobra.Command, cardNumber, emoji string) error {
25
- cardNum, err := strconv.Atoi(cardNumber)
26
- if err != nil {
27
- return fmt.Errorf("invalid card number: %w", err)
28
- }
29
-
30
- a := app.FromContext(cmd.Context())
31
- if a == nil || a.Client == nil {
32
- return fmt.Errorf("API client not available")
33
- }
34
-
35
- _, err = a.Client.PostCardReaction(context.Background(), cardNum, emoji)
36
- if err != nil {
37
- return fmt.Errorf("creating reaction: %w", err)
38
- }
39
-
40
- fmt.Fprintf(cmd.OutOrStdout(), "✓ Reaction %s created successfully\n", emoji)
41
- return nil
42
- }
43
-
44
- func init() {
45
- cardReactionCmd.AddCommand(cardReactionCreateCmd)
46
- }