Files
uptop/internal/tui/format_test.go
T
lerko 5d362fdbe6
CI / test (pull_request) Successful in 2m34s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 51s
CI / test (push) Successful in 2m32s
CI / lint (push) Successful in 56s
CI / vulncheck (push) Successful in 51s
refactor(tui): decompose god files into single-concern modules
tui.go (1032→164) and tab_sites.go (993→482) violated "small functions"
and "testable in isolation" standards. Extracted 6 new files by concern:

- format.go: pure formatting functions (fmtLatency, fmtUptime, etc.)
- sparkline.go: sparkline rendering (latency, heartbeat, group)
- update.go: Update method decomposed into 15 named handlers
- view_dashboard.go: View, dashboard composition, tab bar, footer
- view_detail.go: site detail panel
- data.go: data refresh with extracted sortSitesForDisplay/filterSites

Added 17 unit tests for the newly-testable pure functions covering
format, sparkline, sort ordering, and filter logic. No behavioral
changes — strict move-and-extract refactor.
2026-06-02 21:06:30 -04:00

152 lines
3.3 KiB
Go

package tui
import (
"testing"
"time"
"gitea.lerkolabs.com/lerkolabs/uptop/internal/models"
)
func init() {
applyTheme(themeFlexokiDark)
}
func TestLimitStr(t *testing.T) {
tests := []struct {
input string
max int
want string
}{
{"hello", 10, "hello"},
{"hello", 5, "hello"},
{"hello world", 8, "hello..."},
{"", 5, ""},
{"abc", 3, "abc"},
{"abcd", 3, "..."},
{"日本語テスト", 4, "日..."},
}
for _, tt := range tests {
got := limitStr(tt.input, tt.max)
if got != tt.want {
t.Errorf("limitStr(%q, %d) = %q, want %q", tt.input, tt.max, got, tt.want)
}
}
}
func TestSiteOrder(t *testing.T) {
tests := []struct {
name string
site models.Site
want int
}{
{"down", models.Site{Status: "DOWN"}, 0},
{"ssl exp", models.Site{Status: "SSL EXP"}, 0},
{"late", models.Site{Status: "LATE"}, 1},
{"up", models.Site{Status: "UP"}, 2},
{"pending", models.Site{Status: "PENDING"}, 3},
{"paused up", models.Site{Status: "UP", Paused: true}, 3},
{"paused down", models.Site{Status: "DOWN", Paused: true}, 3},
}
for _, tt := range tests {
got := siteOrder(tt.site)
if got != tt.want {
t.Errorf("siteOrder(%s) = %d, want %d", tt.name, got, tt.want)
}
}
}
func TestFmtDuration(t *testing.T) {
tests := []struct {
d time.Duration
want string
}{
{30 * time.Second, "30s"},
{5 * time.Minute, "5m"},
{2*time.Hour + 30*time.Minute, "2h 30m"},
{2 * time.Hour, "2h"},
{25 * time.Hour, "1d 1h"},
{48 * time.Hour, "2d"},
{49 * time.Hour, "2d 1h"},
}
for _, tt := range tests {
got := fmtDuration(tt.d)
if got != tt.want {
t.Errorf("fmtDuration(%v) = %q, want %q", tt.d, got, tt.want)
}
}
}
func TestTypeIcon(t *testing.T) {
tests := []struct {
siteType string
collapsed bool
want string
}{
{"http", false, "→"},
{"push", false, "↓"},
{"ping", false, "↔"},
{"port", false, "⊡"},
{"dns", false, "◆"},
{"group", false, "▼"},
{"group", true, "▶"},
{"unknown", false, "·"},
}
for _, tt := range tests {
got := typeIcon(tt.siteType, tt.collapsed)
if got != tt.want {
t.Errorf("typeIcon(%q, %v) = %q, want %q", tt.siteType, tt.collapsed, got, tt.want)
}
}
}
func TestFmtUptime(t *testing.T) {
tests := []struct {
name string
statuses []bool
wantSub string
}{
{"empty", nil, "—"},
{"all up", []bool{true, true, true, true}, "100.0%"},
{"half", []bool{true, false, true, false}, "50.0%"},
{"all down", []bool{false, false}, "0.0%"},
}
for _, tt := range tests {
got := fmtUptime(tt.statuses)
if !containsPlain(got, tt.wantSub) {
t.Errorf("fmtUptime(%s): %q missing %q", tt.name, got, tt.wantSub)
}
}
}
func TestFmtLatency(t *testing.T) {
tests := []struct {
d time.Duration
wantSub string
}{
{0, "—"},
{50 * time.Millisecond, "50ms"},
{300 * time.Millisecond, "300ms"},
{1500 * time.Millisecond, "1.5s"},
}
for _, tt := range tests {
got := fmtLatency(tt.d)
if !containsPlain(got, tt.wantSub) {
t.Errorf("fmtLatency(%v): %q missing %q", tt.d, got, tt.wantSub)
}
}
}
func containsPlain(styled, sub string) bool {
// ANSI-styled strings contain the substring somewhere
return len(styled) > 0 && contains(styled, sub)
}
func contains(s, sub string) bool {
for i := 0; i <= len(s)-len(sub); i++ {
if s[i:i+len(sub)] == sub {
return true
}
}
return len(sub) == 0
}