Files
Tobias GesellchenandClaude Sonnet 4.6 89bfa8c2fb feat(discovery): quiet per-packet logs by default; CLI keeps verbose
Discovery cycles emit one line per UPnP M-SEARCH header, one per
parsed response, and one per enrichment step — by default. A typical
service-binary cycle prints ~50–80 lines for a 3-speaker LAN. Most
operators want a startup-and-summary view; the per-packet trace is
only useful for debugging.

- New SetVerbose/IsVerbose/logVerbose helpers in pkg/discovery (atomic
  bool, zero-value off).
- Chatty log.Printf calls in upnp.go and mdns.go demoted to logVerbose:
  per-header dumps, per-response dumps, per-device enrichment steps,
  M-SEARCH details, read-deadline / cancel-context noise.
- Kept at default level: discovery start ("Starting SSDP discovery
  for…"), end ("Discovery completed. Processed N responses, found N
  unique devices" + per-device summary), warnings ("Configured
  interface not found", "Failed to fetch device description", …), and
  the new "Rejecting non-Bose device" classifier.
- cmd/soundtouch-cli/discover devices grew a --verbose / -v flag that
  flips the package toggle on; the service binary leaves it at the
  zero value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 21:15:34 +02:00

74 lines
1.7 KiB
Go

package discovery
import (
"bytes"
"log"
"strings"
"testing"
)
// captureLog redirects log output into a buffer and returns the buffer
// plus a cleanup func that restores the original log destination. Used
// by the tests below to assert what logVerbose / SetVerbose actually
// produces under each toggle state.
func captureLog(t *testing.T) (*bytes.Buffer, func()) {
t.Helper()
buf := &bytes.Buffer{}
original := log.Writer()
log.SetOutput(buf)
return buf, func() {
log.SetOutput(original)
}
}
func TestVerboseToggle_DefaultIsQuiet(t *testing.T) {
// Reset to default state (zero value of atomic.Bool is false).
SetVerbose(false)
t.Cleanup(func() { SetVerbose(false) })
buf, restore := captureLog(t)
defer restore()
logVerbose("noisy: should-be-suppressed message")
if buf.Len() != 0 {
t.Errorf("expected no log output when verbose is off, got: %q", buf.String())
}
if IsVerbose() {
t.Errorf("IsVerbose() = true, want false")
}
}
func TestVerboseToggle_OnEmitsToLog(t *testing.T) {
SetVerbose(true)
t.Cleanup(func() { SetVerbose(false) })
buf, restore := captureLog(t)
defer restore()
logVerbose("trace: %s = %d", "answer", 42)
if !strings.Contains(buf.String(), "trace: answer = 42") {
t.Errorf("expected trace output, got: %q", buf.String())
}
if !IsVerbose() {
t.Errorf("IsVerbose() = false, want true")
}
}
func TestVerboseToggle_StaysOffByDefaultAfterPackageInit(t *testing.T) {
// New goroutines / new processes see the zero-value default. This
// codifies that contract for callers like cmd/soundtouch-service
// that rely on never having to call SetVerbose.
t.Cleanup(func() { SetVerbose(false) })
SetVerbose(false)
if IsVerbose() {
t.Errorf("default verbose state must be false")
}
}