@@ -106,3 +106,190 @@ func TestGmailLabelsGetCmd_JSON(t *testing.T) {
106106 t .Fatalf ("unexpected counts: %#v" , parsed .Label )
107107 }
108108}
109+
110+ func TestGmailLabelsListCmd_TextAndJSON (t * testing.T ) {
111+ origNew := newGmailService
112+ t .Cleanup (func () { newGmailService = origNew })
113+
114+ srv := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
115+ switch {
116+ case strings .HasSuffix (r .URL .Path , "/users/me/labels" ) || strings .HasSuffix (r .URL .Path , "/gmail/v1/users/me/labels" ):
117+ w .Header ().Set ("Content-Type" , "application/json" )
118+ _ = json .NewEncoder (w ).Encode (map [string ]any {
119+ "labels" : []map [string ]any {
120+ {"id" : "INBOX" , "name" : "INBOX" , "type" : "system" },
121+ {"id" : "Label_1" , "name" : "Custom" , "type" : "user" },
122+ },
123+ })
124+ return
125+ default :
126+ http .NotFound (w , r )
127+ return
128+ }
129+ }))
130+ defer srv .Close ()
131+
132+ svc , err := gmail .NewService (context .Background (),
133+ option .WithoutAuthentication (),
134+ option .WithHTTPClient (srv .Client ()),
135+ option .WithEndpoint (srv .URL + "/" ),
136+ )
137+ if err != nil {
138+ t .Fatalf ("NewService: %v" , err )
139+ }
140+ newGmailService = func (context.Context , string ) (* gmail.Service , error ) { return svc , nil }
141+
142+ flags := & rootFlags {Account : "a@b.com" }
143+
144+ // Text output uses tabwriter to os.Stdout.
145+ textOut := captureStdout (t , func () {
146+ u , uiErr := ui .New (ui.Options {Stdout : io .Discard , Stderr : io .Discard , Color : "never" })
147+ if uiErr != nil {
148+ t .Fatalf ("ui.New: %v" , uiErr )
149+ }
150+ ctx := ui .WithUI (context .Background (), u )
151+ ctx = outfmt .WithMode (ctx , outfmt .ModeText )
152+
153+ cmd := newGmailLabelsListCmd (flags )
154+ cmd .SetContext (ctx )
155+ cmd .SetArgs ([]string {})
156+ if err := cmd .Execute (); err != nil {
157+ t .Fatalf ("execute: %v" , err )
158+ }
159+ })
160+ if ! strings .Contains (textOut , "ID" ) || ! strings .Contains (textOut , "NAME" ) {
161+ t .Fatalf ("unexpected output: %q" , textOut )
162+ }
163+ if ! strings .Contains (textOut , "INBOX" ) || ! strings .Contains (textOut , "Custom" ) {
164+ t .Fatalf ("missing labels: %q" , textOut )
165+ }
166+
167+ jsonOut := captureStdout (t , func () {
168+ u , uiErr := ui .New (ui.Options {Stdout : io .Discard , Stderr : io .Discard , Color : "never" })
169+ if uiErr != nil {
170+ t .Fatalf ("ui.New: %v" , uiErr )
171+ }
172+ ctx := ui .WithUI (context .Background (), u )
173+ ctx = outfmt .WithMode (ctx , outfmt .ModeJSON )
174+
175+ cmd := newGmailLabelsListCmd (flags )
176+ cmd .SetContext (ctx )
177+ cmd .SetArgs ([]string {})
178+ if err := cmd .Execute (); err != nil {
179+ t .Fatalf ("execute: %v" , err )
180+ }
181+ })
182+
183+ var parsed struct {
184+ Labels []* gmail.Label `json:"labels"`
185+ }
186+ if err := json .Unmarshal ([]byte (jsonOut ), & parsed ); err != nil {
187+ t .Fatalf ("json parse: %v\n out=%q" , err , jsonOut )
188+ }
189+ if len (parsed .Labels ) != 2 {
190+ t .Fatalf ("unexpected labels: %#v" , parsed .Labels )
191+ }
192+ }
193+
194+ func TestGmailLabelsModifyCmd_JSON (t * testing.T ) {
195+ origNew := newGmailService
196+ t .Cleanup (func () { newGmailService = origNew })
197+
198+ srv := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
199+ switch {
200+ case r .Method == http .MethodGet && (strings .HasSuffix (r .URL .Path , "/users/me/labels" ) || strings .HasSuffix (r .URL .Path , "/gmail/v1/users/me/labels" )):
201+ w .Header ().Set ("Content-Type" , "application/json" )
202+ _ = json .NewEncoder (w ).Encode (map [string ]any {
203+ "labels" : []map [string ]any {
204+ {"id" : "INBOX" , "name" : "INBOX" , "type" : "system" },
205+ {"id" : "Label_1" , "name" : "Custom" , "type" : "user" },
206+ },
207+ })
208+ return
209+ case r .Method == http .MethodPost && (strings .Contains (r .URL .Path , "/users/me/threads/" ) || strings .Contains (r .URL .Path , "/gmail/v1/users/me/threads/" )) && strings .HasSuffix (r .URL .Path , "/modify" ):
210+ parts := strings .Split (r .URL .Path , "/" )
211+ threadID := parts [len (parts )- 2 ]
212+
213+ var body struct {
214+ AddLabelIds []string `json:"addLabelIds"`
215+ RemoveLabelIds []string `json:"removeLabelIds"`
216+ }
217+ _ = json .NewDecoder (r .Body ).Decode (& body )
218+ if len (body .AddLabelIds ) != 1 || body .AddLabelIds [0 ] != "INBOX" {
219+ http .Error (w , "bad addLabelIds" , http .StatusBadRequest )
220+ return
221+ }
222+ if len (body .RemoveLabelIds ) != 1 || body .RemoveLabelIds [0 ] != "Label_1" {
223+ http .Error (w , "bad removeLabelIds" , http .StatusBadRequest )
224+ return
225+ }
226+
227+ if threadID == "t2" {
228+ w .Header ().Set ("Content-Type" , "application/json" )
229+ w .WriteHeader (http .StatusInternalServerError )
230+ _ = json .NewEncoder (w ).Encode (map [string ]any {
231+ "error" : map [string ]any {"code" : 500 , "message" : "boom" },
232+ })
233+ return
234+ }
235+
236+ w .Header ().Set ("Content-Type" , "application/json" )
237+ _ = json .NewEncoder (w ).Encode (map [string ]any {})
238+ return
239+ default :
240+ http .NotFound (w , r )
241+ return
242+ }
243+ }))
244+ defer srv .Close ()
245+
246+ svc , err := gmail .NewService (context .Background (),
247+ option .WithoutAuthentication (),
248+ option .WithHTTPClient (srv .Client ()),
249+ option .WithEndpoint (srv .URL + "/" ),
250+ )
251+ if err != nil {
252+ t .Fatalf ("NewService: %v" , err )
253+ }
254+ newGmailService = func (context.Context , string ) (* gmail.Service , error ) { return svc , nil }
255+
256+ flags := & rootFlags {Account : "a@b.com" }
257+
258+ out := captureStdout (t , func () {
259+ u , uiErr := ui .New (ui.Options {Stdout : io .Discard , Stderr : io .Discard , Color : "never" })
260+ if uiErr != nil {
261+ t .Fatalf ("ui.New: %v" , uiErr )
262+ }
263+ ctx := ui .WithUI (context .Background (), u )
264+ ctx = outfmt .WithMode (ctx , outfmt .ModeJSON )
265+
266+ cmd := newGmailLabelsModifyCmd (flags )
267+ cmd .SetContext (ctx )
268+ cmd .SetArgs ([]string {"t1" , "t2" })
269+ cmd .Flags ().Set ("add" , "INBOX" )
270+ cmd .Flags ().Set ("remove" , "Custom" )
271+ if err := cmd .Execute (); err != nil {
272+ t .Fatalf ("execute: %v" , err )
273+ }
274+ })
275+
276+ var parsed struct {
277+ Results []struct {
278+ ThreadID string `json:"threadId"`
279+ Success bool `json:"success"`
280+ Error string `json:"error"`
281+ } `json:"results"`
282+ }
283+ if err := json .Unmarshal ([]byte (out ), & parsed ); err != nil {
284+ t .Fatalf ("json parse: %v\n out=%q" , err , out )
285+ }
286+ if len (parsed .Results ) != 2 {
287+ t .Fatalf ("unexpected results: %#v" , parsed .Results )
288+ }
289+ if parsed .Results [0 ].ThreadID != "t1" || ! parsed .Results [0 ].Success {
290+ t .Fatalf ("unexpected result 0: %#v" , parsed .Results [0 ])
291+ }
292+ if parsed .Results [1 ].ThreadID != "t2" || parsed .Results [1 ].Success || parsed .Results [1 ].Error == "" {
293+ t .Fatalf ("unexpected result 1: %#v" , parsed .Results [1 ])
294+ }
295+ }
0 commit comments