Files
Bose-SoundTouch/pkg/service/setup/peer_probe_test.go
T
Tobias GesellchenandClaude Opus 4.7 feadc478d5 test: sweep example data in test files to RFC-5737 + placeholders
Mirrors the .md/.txt sweep across all tracked _test.go, testdata XML,
and .http integration files. Test files are self-contained (producer
+ assertion in the same file), so the matched-pair swap stays green
under `go test ./...`.

Mapping applied:
  192.168.178.[0-9]+   → 192.0.2.[same]
  192.168.1.[0-9]+     → 192.0.2.[same]
  Sound Machinechen    → Living Room SoundTouch
  A Sound Machine      → Kitchen SoundTouch
  A81B6A536A98 + case/separator variants → AABBCCDDEEFF (etc.)
  A81B6A849D99         → AABBCCDDEE01
  A81B6A849D88         → AABBCCDDEE03
  A81B6A536A09         → AABBCCDDEE04
  884AEAEEBD27         → AABBCCDDEE02
  3230304              → 1000001
  9569497              → 1000002

Two semantic fixes alongside the bulk swap:

- pkg/service/zeroconf/zeroconf_test.go: the "private 192" and
  "strips query" cases pin acceptance of RFC-1918 192.168/16. They
  must use a real 192.168 value; doc-range IPs would (correctly) be
  rejected by validateZcBaseURL. Switched to 192.168.10.10 — generic
  enough not to match any home LAN default, real enough for the
  validator. Added a comment explaining why this single test still
  carries a 192.168 literal.

- pkg/service/setup/setup_test.go: TestTestDNSRedirection mocks the
  device's `od -An -tu1` byte output, which is space-separated
  octets ("192 168 1 100"). My sed only matched the dot-separated
  form, so the mock was returning the old IP while the test
  assertions had moved to the doc range. Updated to " 192 0 2 100".

go build ./... clean. go test ./... clean (only TestDocsConsistency
remains failing, which is a pre-existing/untracked-file issue).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 22:05:13 +02:00

143 lines
3.7 KiB
Go

package setup
import (
"net/http"
"net/http/httptest"
"sync"
"testing"
"time"
)
// fakePeerObserver is a deterministic PeerObserverHandle for unit
// tests. It exposes the channel returned from Register so the test can
// signal it manually to simulate a device inbound landing.
type fakePeerObserver struct {
mu sync.Mutex
channels map[string]chan PeerHit
forgotten []string
}
func newFakePeerObserver() *fakePeerObserver {
return &fakePeerObserver{channels: map[string]chan PeerHit{}}
}
func (o *fakePeerObserver) Register(ip string) <-chan PeerHit {
o.mu.Lock()
defer o.mu.Unlock()
ch := make(chan PeerHit, 1)
o.channels[ip] = ch
return ch
}
func (o *fakePeerObserver) Forget(ip string) {
o.mu.Lock()
defer o.mu.Unlock()
delete(o.channels, ip)
o.forgotten = append(o.forgotten, ip)
}
func (o *fakePeerObserver) signal(ip string, hit PeerHit) {
o.mu.Lock()
defer o.mu.Unlock()
ch, ok := o.channels[ip]
if !ok {
return
}
select {
case ch <- hit:
default:
}
}
func peerProbeManager(onTrigger func()) *Manager {
return &Manager{
HTTPGet: func(url string) (*http.Response, error) {
if onTrigger != nil {
onTrigger()
}
rr := httptest.NewRecorder()
rr.WriteHeader(200)
return rr.Result(), nil
},
}
}
func TestRunPeerReachabilityProbe_HappyPath(t *testing.T) {
obs := newFakePeerObserver()
// On nudge, simulate the device fanning out to /updates/soundtouch
// which the middleware would signal as a hit on this IP.
m := peerProbeManager(func() {
obs.signal("192.0.2.42", PeerHit{Path: "/updates/soundtouch", At: time.Now()})
})
result, err := m.RunPeerReachabilityProbe("192.0.2.42", obs, 2*time.Second)
if err != nil {
t.Fatalf("RunPeerReachabilityProbe error: %v", err)
}
if !result.Reached {
t.Error("Reached = false, want true")
}
if result.ObservedPath != "/updates/soundtouch" {
t.Errorf("ObservedPath = %q, want %q", result.ObservedPath, "/updates/soundtouch")
}
if len(obs.forgotten) != 1 || obs.forgotten[0] != "192.0.2.42" {
t.Errorf("Forget not called for IP: forgotten = %v", obs.forgotten)
}
}
func TestRunPeerReachabilityProbe_Timeout(t *testing.T) {
obs := newFakePeerObserver()
m := peerProbeManager(nil) // nudge fires but device never responds
start := time.Now()
result, err := m.RunPeerReachabilityProbe("192.0.2.42", obs, 200*time.Millisecond)
elapsed := time.Since(start)
if err != nil {
t.Fatalf("RunPeerReachabilityProbe error: %v", err)
}
if result.Reached {
t.Error("Reached = true, want false (no hit)")
}
if elapsed < 200*time.Millisecond {
t.Errorf("returned early after %v; expected >= 200ms timeout", elapsed)
}
if len(obs.forgotten) != 1 {
t.Errorf("Forget not called after timeout: forgotten = %v", obs.forgotten)
}
}
func TestRunPeerReachabilityProbe_NilObserver(t *testing.T) {
m := peerProbeManager(nil)
_, err := m.RunPeerReachabilityProbe("192.0.2.42", nil, time.Second)
if err == nil {
t.Error("expected error for nil observer, got nil")
}
}
func TestRunPeerReachabilityProbe_EmptyIP(t *testing.T) {
m := peerProbeManager(nil)
obs := newFakePeerObserver()
_, err := m.RunPeerReachabilityProbe("", obs, time.Second)
if err == nil {
t.Error("expected error for empty deviceIP, got nil")
}
}
func TestRunPeerReachabilityProbe_NilHTTPGetTimesOut(t *testing.T) {
// With nil HTTPGet the nudge is skipped entirely; the probe just
// waits for the device to dial in on its own. Useful in tests and
// in environments where the trigger isn't safe to fire.
m := &Manager{} // HTTPGet nil
obs := newFakePeerObserver()
result, err := m.RunPeerReachabilityProbe("192.0.2.42", obs, 100*time.Millisecond)
if err != nil {
t.Fatalf("error: %v", err)
}
if result.Reached {
t.Error("Reached = true with no nudge and no signal")
}
}