@@ -223,3 +223,227 @@ func TestMediaDir_AlreadyExists(t *testing.T) {
223223 t .Fatal ("MediaDir() returned empty path" )
224224 }
225225}
226+
227+ // ── Error path tests ───────────────────────────────────────────────────
228+
229+ func TestMediaDir_MkdirAllError (t * testing.T ) {
230+ tmp := t .TempDir ()
231+ // Create a file where .odek/media/ should be, so MkdirAll fails.
232+ odekDir := filepath .Join (tmp , ".odek" )
233+ if err := os .MkdirAll (odekDir , 0755 ); err != nil {
234+ t .Fatalf ("mkdir .odek: %v" , err )
235+ }
236+ if err := os .WriteFile (filepath .Join (odekDir , "media" ), []byte ("not-a-dir" ), 0644 ); err != nil {
237+ t .Fatalf ("write file: %v" , err )
238+ }
239+ t .Setenv ("HOME" , tmp )
240+
241+ _ , err := MediaDir ()
242+ if err == nil {
243+ t .Fatal ("expected error when media path is blocked by a file" )
244+ }
245+ }
246+
247+ func TestDownloadVoice_DownloadFileError (t * testing.T ) {
248+ handler := func (w http.ResponseWriter , r * http.Request ) {
249+ if strings .Contains (r .URL .String (), "getFile" ) {
250+ fmt .Fprintf (w , `{"ok":true,"result":{"file_id":"v1","file_path":"voice/clip.oga"}}` )
251+ } else {
252+ w .WriteHeader (http .StatusNotFound )
253+ }
254+ }
255+ ts := httptest .NewServer (http .HandlerFunc (handler ))
256+ defer ts .Close ()
257+ bot := testBot (t , ts )
258+
259+ _ , err := DownloadVoice (bot , "v1" )
260+ if err == nil {
261+ t .Fatal ("expected error when download fails" )
262+ }
263+ }
264+
265+ func TestDownloadVoice_EmptyExtensionFallback (t * testing.T ) {
266+ var callCount int
267+ handler := func (w http.ResponseWriter , r * http.Request ) {
268+ callCount ++
269+ switch {
270+ case strings .Contains (r .URL .String (), "getFile" ):
271+ // File path with no extension → should default to .ogg
272+ fmt .Fprintf (w , `{"ok":true,"result":{"file_id":"v1","file_path":"voicedata"}}` )
273+ default :
274+ w .Write ([]byte ("audio-data" ))
275+ }
276+ }
277+ ts := httptest .NewServer (http .HandlerFunc (handler ))
278+ defer ts .Close ()
279+ bot := testBot (t , ts )
280+
281+ path , err := DownloadVoice (bot , "v1" )
282+ if err != nil {
283+ t .Fatalf ("DownloadVoice error: %v" , err )
284+ }
285+ if ! strings .HasSuffix (path , ".ogg" ) {
286+ t .Errorf ("expected .ogg fallback extension, got %q" , path )
287+ }
288+ os .Remove (path )
289+ }
290+
291+ func TestDownloadVoice_ShortFileIDSuffix (t * testing.T ) {
292+ handler := func (w http.ResponseWriter , r * http.Request ) {
293+ if strings .Contains (r .URL .String (), "getFile" ) {
294+ fmt .Fprintf (w , `{"ok":true,"result":{"file_id":"short","file_path":"voice/clip.ogg"}}` )
295+ } else {
296+ w .Write ([]byte ("data" ))
297+ }
298+ }
299+ ts := httptest .NewServer (http .HandlerFunc (handler ))
300+ defer ts .Close ()
301+ bot := testBot (t , ts )
302+
303+ path , err := DownloadVoice (bot , "short" )
304+ if err != nil {
305+ t .Fatalf ("DownloadVoice error: %v" , err )
306+ }
307+ if ! strings .Contains (path , "voice_short" ) {
308+ t .Errorf ("expected short fileID in path, got %q" , path )
309+ }
310+ os .Remove (path )
311+ }
312+
313+ func TestDownloadPhoto_EmptyExtensionFallback (t * testing.T ) {
314+ handler := func (w http.ResponseWriter , r * http.Request ) {
315+ if strings .Contains (r .URL .String (), "getFile" ) {
316+ fmt .Fprintf (w , `{"ok":true,"result":{"file_id":"p1","file_path":"photodata"}}` )
317+ } else {
318+ w .Write ([]byte ("jpg-data" ))
319+ }
320+ }
321+ ts := httptest .NewServer (http .HandlerFunc (handler ))
322+ defer ts .Close ()
323+ bot := testBot (t , ts )
324+
325+ path , err := DownloadPhoto (bot , []string {"p1" })
326+ if err != nil {
327+ t .Fatalf ("DownloadPhoto error: %v" , err )
328+ }
329+ if ! strings .HasSuffix (path , ".jpg" ) {
330+ t .Errorf ("expected .jpg fallback extension, got %q" , path )
331+ }
332+ os .Remove (path )
333+ }
334+
335+ func TestDownloadPhoto_DownloadFileError (t * testing.T ) {
336+ handler := func (w http.ResponseWriter , r * http.Request ) {
337+ if strings .Contains (r .URL .String (), "getFile" ) {
338+ fmt .Fprintf (w , `{"ok":true,"result":{"file_id":"p1","file_path":"photos/img.jpg"}}` )
339+ } else {
340+ w .WriteHeader (http .StatusInternalServerError )
341+ }
342+ }
343+ ts := httptest .NewServer (http .HandlerFunc (handler ))
344+ defer ts .Close ()
345+ bot := testBot (t , ts )
346+
347+ _ , err := DownloadPhoto (bot , []string {"p1" })
348+ if err == nil {
349+ t .Fatal ("expected error when download fails" )
350+ }
351+ }
352+
353+ func TestDownloadPhoto_FilePathEmpty (t * testing.T ) {
354+ handler := func (w http.ResponseWriter , r * http.Request ) {
355+ fmt .Fprintf (w , `{"ok":true,"result":{"file_id":"p1","file_path":""}}` )
356+ }
357+ ts := httptest .NewServer (http .HandlerFunc (handler ))
358+ defer ts .Close ()
359+ bot := testBot (t , ts )
360+
361+ _ , err := DownloadPhoto (bot , []string {"p1" })
362+ if err == nil {
363+ t .Fatal ("expected error for empty file_path" )
364+ }
365+ }
366+
367+ func TestDownloadVoice_LongFileIDTruncation (t * testing.T ) {
368+ // A fileID longer than 16 chars should be truncated in the filename.
369+ longID := "abcdefghijklmnopqrstuvwxyz1234567890"
370+ handler := func (w http.ResponseWriter , r * http.Request ) {
371+ if strings .Contains (r .URL .String (), "getFile" ) {
372+ fmt .Fprintf (w , `{"ok":true,"result":{"file_id":"%s","file_path":"voice/clip.oga"}}` , longID )
373+ } else {
374+ w .Write ([]byte ("data" ))
375+ }
376+ }
377+ ts := httptest .NewServer (http .HandlerFunc (handler ))
378+ defer ts .Close ()
379+ bot := testBot (t , ts )
380+
381+ path , err := DownloadVoice (bot , longID )
382+ if err != nil {
383+ t .Fatalf ("DownloadVoice error: %v" , err )
384+ }
385+ // The filename should contain a truncated (16-char) suffix.
386+ if ! strings .Contains (path , longID [:16 ]) {
387+ t .Errorf ("expected truncated fileID in path, got %q" , path )
388+ }
389+ os .Remove (path )
390+ }
391+
392+ func TestDownloadPhoto_LongFileIDTruncation (t * testing.T ) {
393+ longID := "abcdefghijklmnopqrstuvwxyz1234567890"
394+ handler := func (w http.ResponseWriter , r * http.Request ) {
395+ if strings .Contains (r .URL .String (), "getFile" ) {
396+ fmt .Fprintf (w , `{"ok":true,"result":{"file_id":"%s","file_path":"photos/img.jpg"}}` , longID )
397+ } else {
398+ w .Write ([]byte ("data" ))
399+ }
400+ }
401+ ts := httptest .NewServer (http .HandlerFunc (handler ))
402+ defer ts .Close ()
403+ bot := testBot (t , ts )
404+
405+ path , err := DownloadPhoto (bot , []string {longID })
406+ if err != nil {
407+ t .Fatalf ("DownloadPhoto error: %v" , err )
408+ }
409+ if ! strings .Contains (path , longID [:16 ]) {
410+ t .Errorf ("expected truncated fileID in path, got %q" , path )
411+ }
412+ os .Remove (path )
413+ }
414+
415+ func TestDownloadVoice_MediaDirError (t * testing.T ) {
416+ // Set HOME to a path that can't have .odek/media created.
417+ tmp := t .TempDir ()
418+ odekDir := filepath .Join (tmp , ".odek" )
419+ if err := os .MkdirAll (odekDir , 0755 ); err != nil {
420+ t .Fatalf ("mkdir: %v" , err )
421+ }
422+ // Place a file where media/ should be.
423+ if err := os .WriteFile (filepath .Join (odekDir , "media" ), []byte ("x" ), 0644 ); err != nil {
424+ t .Fatalf ("write: %v" , err )
425+ }
426+ t .Setenv ("HOME" , tmp )
427+
428+ _ , err := DownloadVoice (& Bot {BaseURL : "http://example.com" }, "fid" )
429+ if err == nil {
430+ t .Fatal ("expected error when MediaDir fails" )
431+ }
432+ }
433+
434+ func TestDownloadPhoto_MediaDirError (t * testing.T ) {
435+ tmp := t .TempDir ()
436+ odekDir := filepath .Join (tmp , ".odek" )
437+ if err := os .MkdirAll (odekDir , 0755 ); err != nil {
438+ t .Fatalf ("mkdir: %v" , err )
439+ }
440+ if err := os .WriteFile (filepath .Join (odekDir , "media" ), []byte ("x" ), 0644 ); err != nil {
441+ t .Fatalf ("write: %v" , err )
442+ }
443+ t .Setenv ("HOME" , tmp )
444+
445+ _ , err := DownloadPhoto (& Bot {BaseURL : "http://example.com" }, []string {"fid" })
446+ if err == nil {
447+ t .Fatal ("expected error when MediaDir fails" )
448+ }
449+ }
0 commit comments