style(setup): un-stutter exported type names and tighten range loops

- Rename SetupStateMachine → setup.StateMachine, SetupSessionConfig →
  setup.SessionConfig, SetupSession → setup.Session, and
  DialSetupSession → setup.DialSession. The Setup* prefix only stutters
  in package context (`setup.SetupSession`); the renamed forms read
  cleaner at every call site (revive: exported).
- Iterate r.Network.Interfaces.Interfaces by index in cmd_setup.go
  rather than by value — each NetworkInterface is 168 bytes and the
  per-iteration copy was unnecessary (gocritic: rangeValCopy).

Test fixtures (fakeSetupSession → fakeSession, TestSetupSession_* →
TestSession_*) renamed by the same substring replacement to keep
naming consistent inside the package.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tobias Gesellchen
2026-05-13 18:43:36 +02:00
co-authored by Claude Opus 4.7
parent a1ae10650f
commit 9e384840ba
6 changed files with 78 additions and 76 deletions
+10 -8
View File
@@ -129,7 +129,9 @@ func renderInspectReport(r *setup.InspectReport) {
} else if r.Network != nil {
fmt.Println("Network")
for _, iface := range r.Network.Interfaces.Interfaces {
for i := range r.Network.Interfaces.Interfaces {
iface := &r.Network.Interfaces.Interfaces[i]
fmt.Printf(" %s\n", iface.Type)
fmt.Printf(" state : %s\n", iface.State)
@@ -1034,9 +1036,9 @@ func renderPlanState(deviceIP string, inspect *setup.InspectReport, summary *set
currentSSID := ""
if inspect.Network != nil {
for _, iface := range inspect.Network.Interfaces.Interfaces {
if iface.SSID != "" {
currentSSID = iface.SSID
for i := range inspect.Network.Interfaces.Interfaces {
if ssid := inspect.Network.Interfaces.Interfaces[i].SSID; ssid != "" {
currentSSID = ssid
break
}
}
@@ -1259,9 +1261,9 @@ func inspectedSSID(r *setup.InspectReport) string {
return ""
}
for _, iface := range r.Network.Interfaces.Interfaces {
if iface.SSID != "" {
return iface.SSID
for i := range r.Network.Interfaces.Interfaces {
if ssid := r.Network.Interfaces.Interfaces[i].SSID; ssid != "" {
return ssid
}
}
@@ -1359,7 +1361,7 @@ func runPairBare(c *cli.Context, deviceIP, accountID string) error {
fmt.Printf("pre /info deviceID=%s margeAccountUUID=%q margeURL=%q\n",
info.DeviceID, info.MargeAccountUUID, info.MargeURL)
session, err := setup.DialSetupSession(deviceIP, info.DeviceID, setup.SetupSessionConfig{
session, err := setup.DialSession(deviceIP, info.DeviceID, setup.SessionConfig{
StepTimeout: c.Duration("step-timeout"),
})
if err != nil {
+3 -3
View File
@@ -173,14 +173,14 @@ func (m *Manager) ExecuteInitPlan(ctx context.Context, plan InitPlan, progress P
emit(StepDialWebSocket, "dial websocket", StatusRunning, nil)
if m.NewSetupSession == nil {
nilErr := errors.New("Manager.NewSetupSession is nil — call NewManager or set it explicitly")
if m.NewSession == nil {
nilErr := errors.New("Manager.NewSession is nil — call NewManager or set it explicitly")
emit(StepDialWebSocket, "dial websocket", StatusFailed, nilErr)
return plan, nilErr
}
session, err := m.NewSetupSession(plan.DeviceIP, info.DeviceID, plan.StepTimeout)
session, err := m.NewSession(plan.DeviceIP, info.DeviceID, plan.StepTimeout)
if err != nil {
emit(StepDialWebSocket, "dial websocket", StatusFailed, err)
return plan, fmt.Errorf("dial websocket: %w", err)
+22 -22
View File
@@ -11,15 +11,15 @@ import (
"time"
)
// fakeSetupSession is a SetupStateMachine that records the order of
// fakeSession is a StateMachine that records the order of
// invocations and lets each test inject per-step errors.
type fakeSetupSession struct {
type fakeSession struct {
calls []string
errors map[string]error
closed bool
}
func (f *fakeSetupSession) record(name string) error {
func (f *fakeSession) record(name string) error {
if e, ok := f.errors[name]; ok && e != nil {
return e
}
@@ -29,34 +29,34 @@ func (f *fakeSetupSession) record(name string) error {
return nil
}
func (f *fakeSetupSession) Start(_ context.Context) error { return f.record("Start") }
func (f *fakeSetupSession) Enter(_ context.Context) error { return f.record("Enter") }
func (f *fakeSetupSession) Leave(_ context.Context) error { return f.record("Leave") }
func (f *fakeSetupSession) IdentifyLeave(_ context.Context) error {
func (f *fakeSession) Start(_ context.Context) error { return f.record("Start") }
func (f *fakeSession) Enter(_ context.Context) error { return f.record("Enter") }
func (f *fakeSession) Leave(_ context.Context) error { return f.record("Leave") }
func (f *fakeSession) IdentifyLeave(_ context.Context) error {
return f.record("IdentifyLeave")
}
func (f *fakeSetupSession) IdentifyEnter(_ context.Context, timeoutMs int) error {
func (f *fakeSession) IdentifyEnter(_ context.Context, timeoutMs int) error {
return f.record(fmt.Sprintf("IdentifyEnter(%d)", timeoutMs))
}
func (f *fakeSetupSession) SetLanguage(_ context.Context, code int) error {
func (f *fakeSession) SetLanguage(_ context.Context, code int) error {
return f.record(fmt.Sprintf("SetLanguage(%d)", code))
}
func (f *fakeSetupSession) SetName(_ context.Context, name string) error {
func (f *fakeSession) SetName(_ context.Context, name string) error {
return f.record("SetName(" + name + ")")
}
func (f *fakeSetupSession) SetMargeAccount(_ context.Context, accountID, token string) error {
func (f *fakeSession) SetMargeAccount(_ context.Context, accountID, token string) error {
return f.record(fmt.Sprintf("SetMargeAccount(%s,%s)", accountID, token))
}
func (f *fakeSetupSession) PushCustomerSupportInfo(_ context.Context) error {
func (f *fakeSession) PushCustomerSupportInfo(_ context.Context) error {
return f.record("PushCustomerSupportInfo")
}
func (f *fakeSetupSession) Close() error {
func (f *fakeSession) Close() error {
f.closed = true
return nil
}
@@ -90,13 +90,13 @@ func (f *fakeInfoResponder) get(_ string) (*http.Response, error) {
}, nil
}
func newTestManagerWithFakes(t *testing.T, info *fakeInfoResponder, sess *fakeSetupSession) *Manager {
func newTestManagerWithFakes(t *testing.T, info *fakeInfoResponder, sess *fakeSession) *Manager {
t.Helper()
m := &Manager{
ServerURL: "http://aftertouch.local:8000",
HTTPGet: info.get,
NewSetupSession: func(_, _ string, _ time.Duration) (SetupStateMachine, error) {
NewSession: func(_, _ string, _ time.Duration) (StateMachine, error) {
return sess, nil
},
}
@@ -110,7 +110,7 @@ func TestExecuteInitPlan_FactoryReset_GeneratesAccountAndRunsAllSteps(t *testing
paired: "",
postInitPaired: "", // filled below after we know which ID was generated
}
sess := &fakeSetupSession{}
sess := &fakeSession{}
m := newTestManagerWithFakes(t, info, sess)
// Intercept the generated account ID so we can prime the post-init
@@ -176,7 +176,7 @@ func TestExecuteInitPlan_ReusesExistingAccountUUID(t *testing.T) {
paired: "9876543",
postInitPaired: "9876543",
}
sess := &fakeSetupSession{}
sess := &fakeSession{}
m := newTestManagerWithFakes(t, info, sess)
plan := InitPlan{
@@ -202,7 +202,7 @@ func TestExecuteInitPlan_GeneratesAccountWhenDeviceUUIDInvalid(t *testing.T) {
paired: "not-7-digits",
postInitPaired: "", // we'll learn the generated ID from the result
}
sess := &fakeSetupSession{}
sess := &fakeSession{}
m := newTestManagerWithFakes(t, info, sess)
// Pre-generate so the post-init /info knows what to return.
@@ -231,7 +231,7 @@ func TestExecuteInitPlan_GeneratesAccountWhenDeviceUUIDInvalid(t *testing.T) {
func TestExecuteInitPlan_RejectsInvalidSuppliedAccountID(t *testing.T) {
info := &fakeInfoResponder{deviceID: "X", paired: ""}
sess := &fakeSetupSession{}
sess := &fakeSession{}
m := newTestManagerWithFakes(t, info, sess)
plan := InitPlan{
@@ -256,7 +256,7 @@ func TestExecuteInitPlan_RejectsInvalidSuppliedAccountID(t *testing.T) {
func TestExecuteInitPlan_StopsAtFirstFailedStep(t *testing.T) {
info := &fakeInfoResponder{deviceID: "X", paired: "", postInitPaired: "1234567"}
sess := &fakeSetupSession{
sess := &fakeSession{
errors: map[string]error{
"Enter": errors.New("device dropped the SETUP_ENTER frame"),
},
@@ -294,7 +294,7 @@ func TestExecuteInitPlan_StopsAtFirstFailedStep(t *testing.T) {
func TestExecuteInitPlan_EmptyDeviceNameSkipsNameStep(t *testing.T) {
info := &fakeInfoResponder{deviceID: "X", paired: "", postInitPaired: "1234567"}
sess := &fakeSetupSession{}
sess := &fakeSession{}
m := newTestManagerWithFakes(t, info, sess)
plan := InitPlan{
@@ -341,7 +341,7 @@ func TestExecuteInitPlan_FailsOnPostInitVerifyMismatch(t *testing.T) {
paired: "",
postInitPaired: "9999999", // not equal to plan.AccountID
}
sess := &fakeSetupSession{}
sess := &fakeSession{}
m := newTestManagerWithFakes(t, info, sess)
plan := InitPlan{
+5 -5
View File
@@ -139,10 +139,10 @@ type Manager struct {
NewSSH func(host string) SSHClient
NewTelnet func(host string) TelnetClient
// NewSetupSession opens the WebSocket setup state-machine session used
// NewSession opens the WebSocket setup state-machine session used
// by ExecuteInitPlan. Tests inject an in-memory fake; the production
// default is DialSetupSession.
NewSetupSession func(deviceIP, deviceID string, stepTimeout time.Duration) (SetupStateMachine, error)
// default is DialSession.
NewSession func(deviceIP, deviceID string, stepTimeout time.Duration) (StateMachine, error)
// GetDNSRunning is an optional callback to check the actual state of the DNS server.
GetDNSRunning func() (bool, string)
@@ -167,8 +167,8 @@ func NewManager(serverURL string, ds *datastore.DataStore, cm *certmanager.Certi
NewTelnet: func(host string) TelnetClient {
return telnet.NewClient(host)
},
NewSetupSession: func(deviceIP, deviceID string, stepTimeout time.Duration) (SetupStateMachine, error) {
return DialSetupSession(deviceIP, deviceID, SetupSessionConfig{StepTimeout: stepTimeout})
NewSession: func(deviceIP, deviceID string, stepTimeout time.Duration) (StateMachine, error) {
return DialSession(deviceIP, deviceID, SessionConfig{StepTimeout: stepTimeout})
},
HTTPGet: http.Get,
MgmtUsername: "admin",
+23 -23
View File
@@ -23,10 +23,10 @@ const (
LanguageEnglish = 2
)
// SetupStateMachine is the surface the InitPlan orchestrator drives. The
// concrete WebSocket-backed implementation is *SetupSession; tests inject
// an in-memory fake via Manager.NewSetupSession.
type SetupStateMachine interface {
// StateMachine is the surface the InitPlan orchestrator drives. The
// concrete WebSocket-backed implementation is *Session; tests inject
// an in-memory fake via Manager.NewSession.
type StateMachine interface {
Start(ctx context.Context) error
IdentifyEnter(ctx context.Context, timeoutMs int) error
SetLanguage(ctx context.Context, code int) error
@@ -39,9 +39,9 @@ type SetupStateMachine interface {
Close() error
}
// SetupSessionConfig configures DialSetupSession. Zero values pick safe
// SessionConfig configures DialSession. Zero values pick safe
// defaults; in production callers normally pass an empty struct.
type SetupSessionConfig struct {
type SessionConfig struct {
// StepTimeout caps the per-message wait for an ack frame. Default 8 s.
StepTimeout time.Duration
// DialTimeout caps the WebSocket handshake. Default 10 s.
@@ -54,25 +54,25 @@ type SetupSessionConfig struct {
WSPort int
}
// SetupSession is a synchronous request/response WebSocket session driving
// Session is a synchronous request/response WebSocket session driving
// the speaker's setup state machine. It is deliberately separate from
// pkg/client.WebSocketClient (which is event-oriented, auto-reconnecting,
// and stateful) — setup is a short, linear sequence and benefits from a
// purpose-built transport.
type SetupSession struct {
type Session struct {
deviceID string
conn *websocket.Conn
reqID atomic.Int64
stepTimeout time.Duration
}
// DialSetupSession opens a WebSocket to the speaker at deviceIP and
// DialSession opens a WebSocket to the speaker at deviceIP and
// returns a session ready to drive the SETUP state machine. deviceID is
// required because every <msg> envelope embeds it in the header; obtain
// it from /info before calling.
func DialSetupSession(deviceIP, deviceID string, cfg SetupSessionConfig) (*SetupSession, error) {
func DialSession(deviceIP, deviceID string, cfg SessionConfig) (*Session, error) {
if deviceID == "" {
return nil, errors.New("DialSetupSession: deviceID is required for message routing")
return nil, errors.New("DialSession: deviceID is required for message routing")
}
scheme := cfg.WSScheme
@@ -117,11 +117,11 @@ func DialSetupSession(deviceIP, deviceID string, cfg SetupSessionConfig) (*Setup
step = defaultSetupStepTimeout
}
return &SetupSession{deviceID: deviceID, conn: conn, stepTimeout: step}, nil
return &Session{deviceID: deviceID, conn: conn, stepTimeout: step}, nil
}
// Close sends a normal-closure frame and closes the underlying socket.
func (s *SetupSession) Close() error {
func (s *Session) Close() error {
if s.conn == nil {
return nil
}
@@ -144,7 +144,7 @@ func (s *SetupSession) Close() error {
// Pushed <updates> and <SoundTouchSdkInfo> frames are ignored. The ack
// payload is consumed for error detection (<error …/>) only and never
// returned — every caller discards it.
func (s *SetupSession) sendStep(ctx context.Context, route, method, body string) error {
func (s *Session) sendStep(ctx context.Context, route, method, body string) error {
if s.conn == nil {
return errors.New("setup session: connection closed")
}
@@ -199,13 +199,13 @@ func (s *SetupSession) sendStep(ctx context.Context, route, method, body string)
}
// Start sends SETUP_START.
func (s *SetupSession) Start(ctx context.Context) error {
func (s *Session) Start(ctx context.Context) error {
return s.sendStep(ctx, "setup", "POST", `<setupState state="SETUP_START"/>`)
}
// IdentifyEnter sends SETUP_IDENTIFY_DEVICE_ENTER. timeoutMs defaults to
// the value observed in captures (300 000 ms).
func (s *SetupSession) IdentifyEnter(ctx context.Context, timeoutMs int) error {
func (s *Session) IdentifyEnter(ctx context.Context, timeoutMs int) error {
if timeoutMs <= 0 {
timeoutMs = 300000
}
@@ -216,23 +216,23 @@ func (s *SetupSession) IdentifyEnter(ctx context.Context, timeoutMs int) error {
}
// SetLanguage POSTs sysLanguage. Code 2 = English.
func (s *SetupSession) SetLanguage(ctx context.Context, code int) error {
func (s *Session) SetLanguage(ctx context.Context, code int) error {
body := fmt.Sprintf(`<sysLanguage>%d</sysLanguage>`, code)
return s.sendStep(ctx, "language", "POST", body)
}
// Enter sends SETUP_ENTER.
func (s *SetupSession) Enter(ctx context.Context) error {
func (s *Session) Enter(ctx context.Context) error {
return s.sendStep(ctx, "setup", "POST", `<setupState state="SETUP_ENTER"/>`)
}
// IdentifyLeave sends SETUP_IDENTIFY_DEVICE_LEAVE.
func (s *SetupSession) IdentifyLeave(ctx context.Context) error {
func (s *Session) IdentifyLeave(ctx context.Context) error {
return s.sendStep(ctx, "setup", "POST", `<setupState state="SETUP_IDENTIFY_DEVICE_LEAVE"/>`)
}
// SetName POSTs a device-name change. An empty name is a no-op.
func (s *SetupSession) SetName(ctx context.Context, name string) error {
func (s *Session) SetName(ctx context.Context, name string) error {
if name == "" {
return nil
}
@@ -246,7 +246,7 @@ func (s *SetupSession) SetName(ctx context.Context, name string) error {
// authToken defaults to "Bearer aftertouch" when empty — our local
// service does not validate it, but a non-empty value matches the
// official app's shape.
func (s *SetupSession) SetMargeAccount(ctx context.Context, accountID, authToken string) error {
func (s *Session) SetMargeAccount(ctx context.Context, accountID, authToken string) error {
if accountID == "" {
return errors.New("SetMargeAccount: accountID is required")
}
@@ -264,13 +264,13 @@ func (s *SetupSession) SetMargeAccount(ctx context.Context, accountID, authToken
}
// Leave sends SETUP_LEAVE.
func (s *SetupSession) Leave(ctx context.Context) error {
func (s *Session) Leave(ctx context.Context) error {
return s.sendStep(ctx, "setup", "POST", `<setupState state="SETUP_LEAVE"/>`)
}
// PushCustomerSupportInfo triggers the post-setup telemetry sync. Harmless
// on our local service.
func (s *SetupSession) PushCustomerSupportInfo(ctx context.Context) error {
func (s *Session) PushCustomerSupportInfo(ctx context.Context) error {
return s.sendStep(ctx, "pushCustomerSupportInfoToMarge", "GET", "")
}
+15 -15
View File
@@ -15,7 +15,7 @@ import (
)
// fakeSpeaker is a minimal WebSocket endpoint that records frames sent by
// SetupSession and responds with canned replies. Each test wires its own
// Session and responds with canned replies. Each test wires its own
// reply policy by setting reply.
type fakeSpeaker struct {
server *httptest.Server
@@ -74,7 +74,7 @@ func newFakeSpeaker(t *testing.T) *fakeSpeaker {
}
// ackFor builds a minimal echo reply that carries the same requestID as
// the incoming frame, so the SetupSession's correlation logic accepts it.
// the incoming frame, so the Session's correlation logic accepts it.
func ackFor(frame string) string {
id := extractAttr(frame, `requestID="`, `"`)
return fmt.Sprintf(`<msg><header url="setup"><response requestID="%s"/></header><body><status>ok</status></body></msg>`, id)
@@ -106,10 +106,10 @@ func (f *fakeSpeaker) recordedFrames() []string {
return out
}
// dialFakeSession opens a SetupSession against the fake speaker. We turn
// dialFakeSession opens a Session against the fake speaker. We turn
// the httptest server URL inside-out (http → ws, keep host:port) so the
// dialer reaches our handler.
func dialFakeSession(t *testing.T, f *fakeSpeaker, deviceID string) *SetupSession {
func dialFakeSession(t *testing.T, f *fakeSpeaker, deviceID string) *Session {
t.Helper()
u, err := url.Parse(f.server.URL)
@@ -117,13 +117,13 @@ func dialFakeSession(t *testing.T, f *fakeSpeaker, deviceID string) *SetupSessio
t.Fatalf("parse server URL: %v", err)
}
s, err := DialSetupSession(u.Host, deviceID, SetupSessionConfig{
s, err := DialSession(u.Host, deviceID, SessionConfig{
StepTimeout: 2 * time.Second,
DialTimeout: 2 * time.Second,
WSScheme: "ws",
})
if err != nil {
t.Fatalf("DialSetupSession: %v", err)
t.Fatalf("DialSession: %v", err)
}
t.Cleanup(func() { _ = s.Close() })
@@ -131,7 +131,7 @@ func dialFakeSession(t *testing.T, f *fakeSpeaker, deviceID string) *SetupSessio
return s
}
func TestSetupSession_SendsCanonicalEnvelopes(t *testing.T) {
func TestSession_SendsCanonicalEnvelopes(t *testing.T) {
f := newFakeSpeaker(t)
s := dialFakeSession(t, f, "AABBCCDDEEFF")
@@ -189,7 +189,7 @@ func TestSetupSession_SendsCanonicalEnvelopes(t *testing.T) {
mustContain(t, frames[8], `url="pushCustomerSupportInfoToMarge"`, `method="GET"`)
}
func TestSetupSession_RequestIDsAreUniquePerStep(t *testing.T) {
func TestSession_RequestIDsAreUniquePerStep(t *testing.T) {
f := newFakeSpeaker(t)
s := dialFakeSession(t, f, "X")
ctx := context.Background()
@@ -215,7 +215,7 @@ func TestSetupSession_RequestIDsAreUniquePerStep(t *testing.T) {
}
}
func TestSetupSession_IgnoresUpdatesFramesBeforeAck(t *testing.T) {
func TestSession_IgnoresUpdatesFramesBeforeAck(t *testing.T) {
f := newFakeSpeaker(t)
f.reply = func(frame string) []string {
id := extractAttr(frame, `requestID="`, `"`)
@@ -234,7 +234,7 @@ func TestSetupSession_IgnoresUpdatesFramesBeforeAck(t *testing.T) {
}
}
func TestSetupSession_SurfacesDeviceErrors(t *testing.T) {
func TestSession_SurfacesDeviceErrors(t *testing.T) {
f := newFakeSpeaker(t)
f.reply = func(frame string) []string {
return []string{
@@ -254,14 +254,14 @@ func TestSetupSession_SurfacesDeviceErrors(t *testing.T) {
}
}
func TestSetupSession_RejectsEmptyDeviceID(t *testing.T) {
_, err := DialSetupSession("127.0.0.1:8080", "", SetupSessionConfig{})
func TestSession_RejectsEmptyDeviceID(t *testing.T) {
_, err := DialSession("127.0.0.1:8080", "", SessionConfig{})
if err == nil {
t.Fatal("expected error for empty deviceID")
}
}
func TestSetupSession_RejectsEmptyAccountID(t *testing.T) {
func TestSession_RejectsEmptyAccountID(t *testing.T) {
f := newFakeSpeaker(t)
s := dialFakeSession(t, f, "X")
@@ -271,7 +271,7 @@ func TestSetupSession_RejectsEmptyAccountID(t *testing.T) {
}
}
func TestSetupSession_EmptyNameIsNoOp(t *testing.T) {
func TestSession_EmptyNameIsNoOp(t *testing.T) {
f := newFakeSpeaker(t)
s := dialFakeSession(t, f, "X")
@@ -284,7 +284,7 @@ func TestSetupSession_EmptyNameIsNoOp(t *testing.T) {
}
}
func TestSetupSession_XMLAttributeEscape(t *testing.T) {
func TestSession_XMLAttributeEscape(t *testing.T) {
// Device names with special characters must not break the envelope.
f := newFakeSpeaker(t)
s := dialFakeSession(t, f, `quoted"<id>`)