Files
nilsandClaude Opus 5.5 b259e22458 First start: create the config file and open the Config view
Without a config file arr-tui refused to start. It now creates an empty
one (private, with its directory) and opens the Config view. A service
with neither address nor key is "not configured", which validation
allows: its client returns ErrNotConfigured without a request, its
indicator stays grey, it is logged once as information, and its lists
say so instead of showing errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019reK4DUGYqDUjniWYJvwUW
2026-10-21 15:16:08 +02:00

84 lines
2.6 KiB
Go

package ui
import (
"errors"
"strings"
"testing"
"github.com/gdamore/tcell/v2"
"git.stinnesbeck.com/nils/arr-tui/internal/arr"
"git.stinnesbeck.com/nils/arr-tui/internal/config"
"git.stinnesbeck.com/nils/arr-tui/internal/sabnzbd"
)
func newTestApp(t *testing.T) *App {
t.Helper()
// Unreachable services: the views only start background requests that fail.
cfg := &config.Config{
Radarr: config.Arr{Service: config.Service{URL: "http://127.0.0.1:1", APIKey: "k"}},
Sonarr: config.Arr{Service: config.Service{URL: "http://127.0.0.1:1", APIKey: "k"}},
SABnzbd: config.Service{URL: "http://127.0.0.1:1", APIKey: "k"},
UI: config.UI{RefreshSeconds: 3},
}
return New(cfg)
}
func TestOnlyFunctionKeysSwitchViews(t *testing.T) {
a := newTestApp(t)
for _, r := range "1234567\t" {
key := tcell.NewEventKey(tcell.KeyRune, r, 0)
if r == '\t' {
key = tcell.NewEventKey(tcell.KeyTab, 0, 0)
}
a.handleKey(key)
if a.active != 0 {
t.Fatalf("key %q switched to view %d", r, a.active)
}
}
for i, k := range []tcell.Key{tcell.KeyF1, tcell.KeyF2, tcell.KeyF3, tcell.KeyF4, tcell.KeyF5, tcell.KeyF6, tcell.KeyF7} {
if a.handleKey(tcell.NewEventKey(k, 0, 0)) != nil || a.active != i {
t.Errorf("F%d: active view %d, want %d", i+1, a.active, i)
}
}
}
func TestQDoesNotQuit(t *testing.T) {
a := newTestApp(t)
ev := tcell.NewEventKey(tcell.KeyRune, 'q', 0)
if got := a.handleKey(ev); got != ev {
t.Error("q must be passed on as an ordinary key, not taken as quit")
}
}
func TestStartsWithoutServices(t *testing.T) {
// The config a first start creates: nothing set up.
a := New(&config.Config{UI: config.UI{RefreshSeconds: 3}})
if a.views[a.active].name != "Config" {
t.Errorf("first start opens %q, want the Config view", a.views[a.active].name)
}
// The clients do not try to connect without an address.
_, err := a.radarr.SystemStatus(t.Context())
if !errors.Is(err, arr.ErrNotConfigured) {
t.Fatalf("radarr without address: %v", err)
}
if _, err := a.sab.Version(t.Context()); !errors.Is(err, sabnzbd.ErrNotConfigured) {
t.Fatalf("sabnzbd without address: %v", err)
}
// Not configured is grey, not red, and logged once as information.
a.SetService("Radarr", "", err)
a.SetService("Radarr", "", err)
if txt := a.services.GetText(false); !strings.Contains(txt, "○ Radarr") {
t.Errorf("indicator: %q", txt)
}
log := a.logs.root.GetText(true)
if strings.Count(log, "Radarr is not configured") != 1 || strings.Contains(log, "unreachable") {
t.Errorf("log: %q", log)
}
if !strings.Contains(a.serviceSummary(), "Radarr: not configured") {
t.Errorf("help summary: %q", a.serviceSummary())
}
}