44 "context"
55 "encoding/base64"
66 "encoding/json"
7+ "errors"
78 "net/http"
89 "net/http/httptest"
910 "os"
@@ -13,6 +14,8 @@ import (
1314
1415 "google.golang.org/api/gmail/v1"
1516 "google.golang.org/api/option"
17+
18+ "github.com/steipete/gogcli/internal/outfmt"
1619)
1720
1821func TestDownloadAttachmentToPath_MissingOutPath (t * testing.T ) {
@@ -94,6 +97,120 @@ func TestDownloadAttachmentToPath_EmptyData(t *testing.T) {
9497 }
9598}
9699
100+ func TestDownloadAttachmentToPath_DirectoryNotCached (t * testing.T ) {
101+ dir := t .TempDir ()
102+ // A directory should not be treated as a cached attachment even though
103+ // os.Stat succeeds and Size() > 0 on directories.
104+ srv := httptestServerForAttachment (t , base64 .RawURLEncoding .EncodeToString ([]byte ("data" )))
105+ gsvc , err := gmail .NewService (context .Background (),
106+ option .WithoutAuthentication (),
107+ option .WithHTTPClient (srv .Client ()),
108+ option .WithEndpoint (srv .URL + "/" ),
109+ )
110+ if err != nil {
111+ t .Fatalf ("NewService: %v" , err )
112+ }
113+
114+ outPath := filepath .Join (dir , "subdir" )
115+ if err := os .Mkdir (outPath , 0o700 ); err != nil {
116+ t .Fatalf ("Mkdir: %v" , err )
117+ }
118+
119+ // With expectedSize == -1, a directory should NOT be returned as cached.
120+ // Instead it should attempt download and fail trying to write to the dir path.
121+ _ , _ , _ , dlErr := downloadAttachmentToPath (context .Background (), gsvc , "m1" , "a1" , outPath , - 1 )
122+ // We expect an error because outPath is a directory and WriteFile to a dir fails.
123+ if dlErr == nil {
124+ t .Fatalf ("expected error when outPath is a directory" )
125+ }
126+ }
127+
128+ func TestDownloadAttachmentToPath_DirectoryNotCachedBySize (t * testing.T ) {
129+ dir := t .TempDir ()
130+ // Even with a positive expectedSize matching the directory's metadata
131+ // size, a directory should not be treated as a cached file.
132+ srv := httptestServerForAttachment (t , base64 .RawURLEncoding .EncodeToString ([]byte ("data" )))
133+ gsvc , err := gmail .NewService (context .Background (),
134+ option .WithoutAuthentication (),
135+ option .WithHTTPClient (srv .Client ()),
136+ option .WithEndpoint (srv .URL + "/" ),
137+ )
138+ if err != nil {
139+ t .Fatalf ("NewService: %v" , err )
140+ }
141+
142+ // Pass the directory size as expectedSize - should NOT match because
143+ // the path is a directory, not a regular file.
144+ info , _ := os .Stat (dir )
145+ _ , cached , _ , dlErr := downloadAttachmentToPath (context .Background (), gsvc , "m1" , "a1" , dir , info .Size ())
146+ // It should not return cached=true; it will try to download and fail
147+ // because WriteFile to a directory fails.
148+ if dlErr == nil && cached {
149+ t .Fatalf ("directory should not be treated as cached file" )
150+ }
151+ }
152+
153+ func TestGmailAttachmentCmd_DryRun_OutDir_UsesName (t * testing.T ) {
154+ outDir := t .TempDir ()
155+ ctx := outfmt .WithMode (context .Background (), outfmt.Mode {JSON : true })
156+
157+ out := captureStdout (t , func () {
158+ err := runKong (t , & GmailAttachmentCmd {}, []string {"m1" , "a1" , "--out" , outDir , "--name" , "invoice.pdf" }, ctx , & RootFlags {DryRun : true })
159+ var exitErr * ExitError
160+ if ! errors .As (err , & exitErr ) || exitErr .Code != 0 {
161+ t .Fatalf ("expected exit code 0, got: %v" , err )
162+ }
163+ })
164+
165+ var got map [string ]any
166+ if err := json .Unmarshal ([]byte (out ), & got ); err != nil {
167+ t .Fatalf ("unmarshal: %v\n output=%q" , err , out )
168+ }
169+ req , ok := got ["request" ].(map [string ]any )
170+ if ! ok {
171+ t .Fatalf ("expected request object, got=%T" , got ["request" ])
172+ }
173+ path , ok := req ["path" ].(string )
174+ if ! ok {
175+ t .Fatalf ("expected request.path string, got=%T" , req ["path" ])
176+ }
177+ want := filepath .Join (outDir , "invoice.pdf" )
178+ if path != want {
179+ t .Fatalf ("unexpected path: got=%q want=%q" , path , want )
180+ }
181+ }
182+
183+ func TestGmailAttachmentCmd_DryRun_OutDirTrailingSlash_UsesNameEvenIfMissing (t * testing.T ) {
184+ base := t .TempDir ()
185+ outDir := filepath .Join (base , "newdir" ) + string (os .PathSeparator )
186+ ctx := outfmt .WithMode (context .Background (), outfmt.Mode {JSON : true })
187+
188+ out := captureStdout (t , func () {
189+ err := runKong (t , & GmailAttachmentCmd {}, []string {"m1" , "a1" , "--out" , outDir , "--name" , "invoice.pdf" }, ctx , & RootFlags {DryRun : true })
190+ var exitErr * ExitError
191+ if ! errors .As (err , & exitErr ) || exitErr .Code != 0 {
192+ t .Fatalf ("expected exit code 0, got: %v" , err )
193+ }
194+ })
195+
196+ var got map [string ]any
197+ if err := json .Unmarshal ([]byte (out ), & got ); err != nil {
198+ t .Fatalf ("unmarshal: %v\n output=%q" , err , out )
199+ }
200+ req , ok := got ["request" ].(map [string ]any )
201+ if ! ok {
202+ t .Fatalf ("expected request object, got=%T" , got ["request" ])
203+ }
204+ path , ok := req ["path" ].(string )
205+ if ! ok {
206+ t .Fatalf ("expected request.path string, got=%T" , req ["path" ])
207+ }
208+ want := filepath .Join (filepath .Join (base , "newdir" ), "invoice.pdf" )
209+ if path != want {
210+ t .Fatalf ("unexpected path: got=%q want=%q" , path , want )
211+ }
212+ }
213+
97214func httptestServerForAttachment (t * testing.T , data string ) * httptest.Server {
98215 t .Helper ()
99216 return httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
0 commit comments