Skip to content

Commit b44accb

Browse files
committed
test: improve code coverage across ws, tool, root, and telegram packages
Phase 1 - Quick Wins: - internal/ws: Add test verifying WebSocket constants (ws_test.go) - internal/tool: Add clarify_test.go covering all ClarifyTool methods (Name, Description, Schema, Call with success/error paths, NewClarifyTool) → coverage: 42.3% → 100.0% - Root package: Add tests for token tracking methods (TotalInputTokens, TotalOutputTokens, TotalCacheCreationTokens, TotalCacheReadTokens, TotalCachedTokens) and Memory() nil-receiver safety → coverage: 70.3% → 77.1% Phase 2 - Telegram package: - download.go: Add tests for MkdirAll error, DownloadFile error, empty extension fallback (voice+photo), long fileID truncation, empty file_path, MediaDir error propagation → download.go: 75.6% → ~92% avg - plan.go: Add tests for Slugify public wrapper, long input, all-special-chars, planPath, ensurePlansDir MkdirAll error, ListPlans ReadDir error + non-.md skipping, ReadPlan empty slug, no dir, ReadDir error, DeletePlan empty/no dir/ReadDir/Remove errors, MostRecentPlan error, prefix matching edge cases → plan.go functions: ~60% → ~87% avg Telegram package overall: 84.0% → 86.9%
1 parent ea83c1f commit b44accb

7 files changed

Lines changed: 1358 additions & 0 deletions

File tree

internal/telegram/download_test.go

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)