Add Starlink status lines for the physical gateway pane (closes #3) (#7)
check / check (push) Failing after 4s
check / check (push) Failing after 4s
Detection is a TCP connect to the dish endpoint bound to the physical interface; once a dish answers, get_status is read every 5s and two status lines render under the physical pane, red on disconnect or alert. No dish: nothing drawn, no RPC. Minimal vendored proto bindings for get_status only. Independent review passed (PR 7 comment 100012). Model: opus-4-8 (implementation and review); model: claude-fable-5 (merge)
This commit was merged in pull request #7.
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
package monitor_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.eeqj.de/sneak/rtnetmon/internal/monitor"
|
||||
"git.eeqj.de/sneak/rtnetmon/internal/starlink"
|
||||
)
|
||||
|
||||
// fakeDish is a Starlink client for tests: no dish, no network. It records
|
||||
// how often each method is called and returns programmed results.
|
||||
type fakeDish struct {
|
||||
mu sync.Mutex
|
||||
probeOK bool
|
||||
status starlink.Status
|
||||
statusErr error
|
||||
probeCalls int
|
||||
statusCalls int
|
||||
}
|
||||
|
||||
func (f *fakeDish) Probe(_ context.Context) bool {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
f.probeCalls++
|
||||
|
||||
return f.probeOK
|
||||
}
|
||||
|
||||
func (f *fakeDish) Status(_ context.Context) (starlink.Status, error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
f.statusCalls++
|
||||
|
||||
return f.status, f.statusErr
|
||||
}
|
||||
|
||||
func (f *fakeDish) counts() (int, int) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
return f.probeCalls, f.statusCalls
|
||||
}
|
||||
|
||||
func newTwoPaneMonitor() *monitor.Monitor {
|
||||
return monitor.NewMonitor([]monitor.IfaceSpec{
|
||||
{Name: ifaceTest0, Label: "A"},
|
||||
{Name: ifaceTest1, Label: "B"},
|
||||
}, "")
|
||||
}
|
||||
|
||||
func TestEnableStarlinkSelectsPane(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mon := newTwoPaneMonitor()
|
||||
mon.EnableStarlink(ifaceTest1)
|
||||
|
||||
if !mon.StarlinkEnabled() {
|
||||
t.Fatal("StarlinkEnabled = false, want true")
|
||||
}
|
||||
|
||||
if got := mon.StarlinkPaneName(); got != ifaceTest1 {
|
||||
t.Errorf("StarlinkPaneName = %q, want %q", got, ifaceTest1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnableStarlinkUnknownInterface(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mon := newTwoPaneMonitor()
|
||||
mon.EnableStarlink("does-not-exist")
|
||||
|
||||
if mon.StarlinkEnabled() {
|
||||
t.Error("StarlinkEnabled = true for an unknown interface, want false")
|
||||
}
|
||||
}
|
||||
|
||||
// TestStarlinkNoDishNoStatus verifies that when no dish answers, no status
|
||||
// is ever fetched and nothing is marked detected.
|
||||
func TestStarlinkNoDishNoStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mon := newTwoPaneMonitor()
|
||||
fake := &fakeDish{probeOK: false}
|
||||
mon.SetStarlinkClient(ifaceTest1, fake)
|
||||
|
||||
ctx := context.Background()
|
||||
mon.StarlinkProbeStep(ctx)
|
||||
mon.StarlinkRefreshStep(ctx)
|
||||
|
||||
detected, haveStatus, _ := mon.StarlinkSnapshot()
|
||||
if detected || haveStatus {
|
||||
t.Errorf("detected=%v haveStatus=%v, want both false", detected, haveStatus)
|
||||
}
|
||||
|
||||
if _, status := fake.counts(); status != 0 {
|
||||
t.Errorf("status calls = %d, want 0 (no probing noise)", status)
|
||||
}
|
||||
}
|
||||
|
||||
// TestStarlinkDetectedFetchesStatus verifies that once a dish answers,
|
||||
// detection triggers a status fetch and later refreshes fetch again.
|
||||
func TestStarlinkDetectedFetchesStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mon := newTwoPaneMonitor()
|
||||
want := starlink.Status{
|
||||
State: "CONNECTED",
|
||||
Uptime: 2 * time.Hour,
|
||||
DownlinkMbps: 100,
|
||||
}
|
||||
fake := &fakeDish{probeOK: true, status: want}
|
||||
mon.SetStarlinkClient(ifaceTest1, fake)
|
||||
|
||||
ctx := context.Background()
|
||||
mon.StarlinkProbeStep(ctx)
|
||||
|
||||
detected, haveStatus, got := mon.StarlinkSnapshot()
|
||||
if !detected || !haveStatus {
|
||||
t.Fatalf("detected=%v haveStatus=%v, want both true", detected, haveStatus)
|
||||
}
|
||||
|
||||
if got != want {
|
||||
t.Errorf("status = %+v, want %+v", got, want)
|
||||
}
|
||||
|
||||
mon.StarlinkRefreshStep(ctx)
|
||||
|
||||
probe, status := fake.counts()
|
||||
if probe != 1 {
|
||||
t.Errorf("probe calls = %d, want 1", probe)
|
||||
}
|
||||
|
||||
if status != 2 {
|
||||
t.Errorf("status calls = %d, want 2", status)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user