package main import ( "testing" "time" ) func TestTruncateString(t *testing.T) { tests := []struct { name string input string maxLen int want string }{ {"no truncation needed", "hello", 10, "hello"}, {"exact length", "hello", 5, "hello"}, {"truncate with ellipsis", "hello world", 8, "hello..."}, {"very short max", "hello", 3, "hel"}, {"max of 4", "hello world", 4, "h..."}, {"empty string", "", 5, ""}, // Note: truncateString operates on bytes, not runes {"unicode", "hello\u4e16\u754c", 15, "hello\u4e16\u754c"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := truncateString(tt.input, tt.maxLen) if got != tt.want { t.Errorf("truncateString(%q, %d) = %q, want %q", tt.input, tt.maxLen, got, tt.want) } }) } } func TestPluralize(t *testing.T) { tests := []struct { count int want string }{ {0, "s"}, {1, ""}, {2, "s"}, {100, "s"}, {-1, "s"}, } for _, tt := range tests { t.Run("", func(t *testing.T) { got := pluralize(tt.count) if got != tt.want { t.Errorf("pluralize(%d) = %q, want %q", tt.count, got, tt.want) } }) } } func TestFormatTimeAgo(t *testing.T) { now := time.Now() tests := []struct { name string t time.Time wantContains string }{ {"just now", now.Add(-30 * time.Second), "just now"}, {"1 minute ago", now.Add(-1 * time.Minute), "1 min ago"}, {"5 minutes ago", now.Add(-5 * time.Minute), "5 mins ago"}, {"1 hour ago", now.Add(-1 * time.Hour), "1 hour ago"}, {"3 hours ago", now.Add(-3 * time.Hour), "3 hours ago"}, {"1 day ago", now.Add(-24 * time.Hour), "1 day ago"}, {"3 days ago", now.Add(-72 * time.Hour), "3 days ago"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := formatTimeAgo(tt.t) if got != tt.wantContains { t.Errorf("formatTimeAgo() = %q, want %q", got, tt.wantContains) } }) } } func TestContainsLabel(t *testing.T) { tests := []struct { name string labels []string label string want bool }{ {"empty labels", []string{}, "bug", false}, {"single match", []string{"bug"}, "bug", true}, {"no match", []string{"feature", "enhancement"}, "bug", false}, {"match in list", []string{"bug", "feature", "urgent"}, "feature", true}, {"case sensitive", []string{"Bug"}, "bug", false}, {"nil labels", nil, "bug", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := containsLabel(tt.labels, tt.label) if got != tt.want { t.Errorf("containsLabel(%v, %q) = %v, want %v", tt.labels, tt.label, got, tt.want) } }) } } func TestGetContributorsSorted(t *testing.T) { // Test that contributors are returned in sorted order by commit count contributors := getContributorsSorted() if len(contributors) == 0 { t.Skip("No contributors defined") } // Check that we have at least some contributors if len(contributors) < 1 { t.Error("Expected at least one contributor") } // Verify first contributor has most commits (descending order) // We can't easily check counts, but we can verify the result is non-empty strings for i, c := range contributors { if c == "" { t.Errorf("Contributor at index %d is empty string", i) } } } func TestExtractIDSuffix(t *testing.T) { tests := []struct { name string id string want string }{ {"hierarchical ID", "bd-123.1.2", "2"}, {"prefix-hash ID", "bd-abc123", "abc123"}, {"simple ID", "123", "123"}, {"multi-dot hierarchical", "prefix-xyz.1.2.3", "3"}, {"dot only", "a.b", "b"}, {"dash only", "a-b", "b"}, {"empty", "", ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := extractIDSuffix(tt.id) if got != tt.want { t.Errorf("extractIDSuffix(%q) = %q, want %q", tt.id, got, tt.want) } }) } } func TestTruncate(t *testing.T) { tests := []struct { name string s string maxLen int want string }{ {"no truncation", "short", 10, "short"}, {"exact length", "exact", 5, "exact"}, {"truncate needed", "long string here", 10, "long st..."}, {"very short max", "hello world", 5, "he..."}, {"empty string", "", 5, ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := truncate(tt.s, tt.maxLen) if got != tt.want { t.Errorf("truncate(%q, %d) = %q, want %q", tt.s, tt.maxLen, got, tt.want) } }) } } func TestTruncateDescription(t *testing.T) { tests := []struct { name string desc string maxLen int want string }{ {"no truncation", "short", 10, "short"}, {"multiline takes first", "first line\nsecond line", 20, "first line"}, {"truncate with ellipsis", "a very long description here", 15, "a very long ..."}, {"multiline and truncate", "first line is long\nsecond", 10, "first l..."}, {"empty", "", 10, ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := truncateDescription(tt.desc, tt.maxLen) if got != tt.want { t.Errorf("truncateDescription(%q, %d) = %q, want %q", tt.desc, tt.maxLen, got, tt.want) } }) } } // Test showCleanupDeprecationHint - just ensure it doesn't panic func TestShowCleanupDeprecationHint(t *testing.T) { // This function just prints to stdout, so we just verify it doesn't panic showCleanupDeprecationHint() } // Test clearAutoFlushState - ensure it doesn't panic when called without initialization func TestClearAutoFlushState(t *testing.T) { // This should not panic even if flush manager isn't initialized clearAutoFlushState() }