package web import ( "fmt" "net/http" "strings" "testing" "time" "gitea.lab.audasmedia.com.au/sam/internet_use_control/internal/store" ) func TestUsageScreenShowsAWeek(t *testing.T) { app := newTestApp(t) dev := createTestDevice(t, app) ctx := t.Context() // Seed the whole current week: apps and domains for every day. start := weekStart(time.Now()) for i := 0; i < 7; i++ { day := start.AddDate(0, 0, i).Format("2006-01-02") if _, err := app.store.InsertTelemetry(ctx, dev.ID, day, []store.TelemetryEntry{ {App: "com.roblox.client", Seconds: (i + 1) * 600}, {App: "com.spotify.music", Seconds: 300}, {Domain: "roblox.com", Seconds: (i + 1) * 600}, }); err != nil { t.Fatalf("InsertTelemetry %s: %v", day, err) } } rec := app.do(t, http.MethodGet, fmt.Sprintf("/devices/%d/usage", dev.ID), nil) if rec.Code != http.StatusOK { t.Fatalf("GET usage = %d, want 200; body=%s", rec.Code, rec.Body.String()) } body := rec.Body.String() for _, want := range []string{ "Usage", "Top apps", "Top sites", "App time", "Site time", "com.roblox.client", "com.spotify.music", "roblox.com", fmt.Sprintf("%d", dev.ID), "Harry's Pixel", } { if !strings.Contains(body, want) { t.Errorf("usage page missing %q", want) } } // Seven day rows: the weekday abbreviations must all be present. for _, wd := range []string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"} { if !strings.Contains(body, ">"+wd+" <") { t.Errorf("usage page missing the %s row", wd) } } // A week with no data renders the empty state, never an error. rec = app.do(t, http.MethodGet, fmt.Sprintf("/devices/%d/usage?week=2020-01-01", dev.ID), nil) if rec.Code != http.StatusOK || !strings.Contains(rec.Body.String(), "No usage for this week") { t.Errorf("empty week = %d, want the empty state", rec.Code) } } func TestWeekHelpers(t *testing.T) { loc := time.UTC // A Sunday belongs to the week that started the previous Monday. sunday := time.Date(2026, 10, 18, 12, 0, 0, 0, loc) // Sunday got := weekStart(sunday) want := time.Date(2026, 10, 12, 0, 0, 0, 0, loc) if !got.Equal(want) { t.Errorf("weekStart(Sun) = %s, want %s", got, want) } // A bad ?week= value falls back to now instead of failing. now := time.Date(2026, 10, 15, 9, 0, 0, 0, loc) if got := parseWeekAnchor("not-a-date", now); !got.Equal(now) { t.Errorf("parseWeekAnchor(bad) = %s, want now", got) } if got := parseWeekAnchor("2026-10-12", now); got.Day() != 12 || got.Month() != time.October { t.Errorf("parseWeekAnchor(valid) = %s", got) } if got := formatSeconds(0); got != "0m" { t.Errorf("formatSeconds(0) = %q", got) } if got := formatSeconds(3600); got != "1h 00m" { t.Errorf("formatSeconds(3600) = %q", got) } if got := formatSeconds(30); got != "under a minute" { t.Errorf("formatSeconds(30) = %q", got) } if got := percent(50, 100); got != 50 { t.Errorf("percent(50,100) = %d", got) } if got := percent(50, 0); got != 0 { t.Errorf("percent with max 0 = %d, want 0", got) } }