Skip to content

Commit c43f28f

Browse files
hugohclaude
andcommitted
feat(deps): upgrade resty v2 → v3 (resty.dev/v3 v3.0.0-rc.2)
- Update import path to resty.dev/v3 - Rename resp.IsError() → IsStatusFailure(), IsSuccess() → IsStatusSuccess() - Replace removed resp.Body() with resp.Bytes() - Add Close() error to Gateway interface and GatewayCommon - Add SetRetryAllowNonIdempotent(true) to preserve POST retry behaviour - Move Nokia SID cookie from client-level to per-request injection, since v3 has no ClearCookies() API; update tests accordingly - Update BaseURL field access to BaseURL() method call Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 579e6b1 commit c43f28f

8 files changed

Lines changed: 63 additions & 54 deletions

File tree

arcadyan.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func (a *ArcadyanGateway) Login(ctx context.Context) error {
6767
return fmt.Errorf("login request failed: %w", err)
6868
}
6969

70-
if resp.IsError() {
70+
if resp.IsStatusFailure() {
7171
return NewAuthError(resp.StatusCode(), resp.String(), nil)
7272
}
7373

@@ -102,7 +102,7 @@ func (a *ArcadyanGateway) Reboot(ctx context.Context) error {
102102
return fmt.Errorf("reboot request failed: %w", err)
103103
}
104104

105-
if resp.IsError() {
105+
if resp.IsStatusFailure() {
106106
status := resp.StatusCode()
107107
if status == http.StatusUnauthorized || status == http.StatusForbidden {
108108
a.logout()
@@ -129,12 +129,12 @@ func (a *ArcadyanGateway) Request(ctx context.Context, method, path string) (*In
129129
return nil, fmt.Errorf("request failed: %w", err)
130130
}
131131

132-
if resp.IsError() {
132+
if resp.IsStatusFailure() {
133133
return nil, fmt.Errorf("%w: HTTP %d", ErrRequestFailed, resp.StatusCode())
134134
}
135135

136136
contentType := resp.Header().Get("Content-Type")
137-
body := resp.Body()
137+
body := resp.Bytes()
138138

139139
var data map[string]any
140140
if strings.HasPrefix(contentType, jsonContentType) {
@@ -172,7 +172,7 @@ func (a *ArcadyanGateway) Status(ctx context.Context) (*StatusResult, error) {
172172
if webResult.Error == nil {
173173
webResult.Error = NewGatewayError("status", 0, "failed to get registration status", err)
174174
}
175-
case resp.IsError():
175+
case resp.IsStatusFailure():
176176
if webResult.Error == nil {
177177
webResult.Error = NewGatewayError(
178178
"status",
@@ -199,7 +199,7 @@ func (a *ArcadyanGateway) Signal(ctx context.Context) (*SignalResult, error) {
199199
return nil, NewGatewayError("signal", 0, "failed to get signal info", err)
200200
}
201201

202-
if resp.IsError() {
202+
if resp.IsStatusFailure() {
203203
return nil, NewGatewayError(
204204
"signal",
205205
resp.StatusCode(),

arcadyan_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import (
66
"testing"
77
"time"
88

9-
"github.com/go-resty/resty/v2"
109
"github.com/stretchr/testify/assert"
1110
"github.com/stretchr/testify/require"
11+
"resty.dev/v3"
1212
)
1313

1414
func newArcadyan(gc *GatewayCommon, token string, exp time.Time) *ArcadyanGateway {

gateway.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ import (
66
"net"
77
"strings"
88

9-
"github.com/go-resty/resty/v2"
9+
"resty.dev/v3"
1010
)
1111

1212
// Gateway defines the interface for T-Mobile gateway implementations.
1313
//
1414
// Implementations are not safe for concurrent use: Login mutates shared
1515
// client state such as auth headers and cookies.
1616
type Gateway interface {
17+
Close() error
1718
Login(ctx context.Context) error
1819
Reboot(ctx context.Context) error
1920
Request(ctx context.Context, method, path string) (*InfoResult, error)
@@ -54,6 +55,7 @@ func NewGatewayCommon(cfg *GatewayConfig) *GatewayCommon {
5455

5556
if cfg.Retries > 0 {
5657
client.SetRetryCount(cfg.Retries)
58+
client.SetRetryAllowNonIdempotent(true)
5759
}
5860

5961
if cfg.Debug {
@@ -66,6 +68,15 @@ func NewGatewayCommon(cfg *GatewayConfig) *GatewayCommon {
6668
}
6769
}
6870

71+
// Close releases resources held by the underlying HTTP client.
72+
func (gc *GatewayCommon) Close() error {
73+
if err := gc.client.Close(); err != nil {
74+
return fmt.Errorf("close client: %w", err)
75+
}
76+
77+
return nil
78+
}
79+
6980
// CheckWebInterface checks if the gateway web interface is accessible.
7081
func (gc *GatewayCommon) CheckWebInterface(ctx context.Context) *StatusResult {
7182
resp, err := gc.client.R().SetContext(ctx).Head("/")
@@ -79,7 +90,7 @@ func (gc *GatewayCommon) CheckWebInterface(ctx context.Context) *StatusResult {
7990
}
8091

8192
result.StatusCode = resp.StatusCode()
82-
result.WebInterfaceUp = resp.IsSuccess()
93+
result.WebInterfaceUp = resp.IsStatusSuccess()
8394

8495
return result
8596
}

gateway_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import (
77
"testing"
88
"time"
99

10-
"github.com/go-resty/resty/v2"
1110
"github.com/stretchr/testify/assert"
11+
"resty.dev/v3"
1212
)
1313

1414
const testServerErrMsg = "server error"
@@ -85,7 +85,7 @@ func TestNewGatewayCommon_HostForms(t *testing.T) {
8585
for _, tc := range cases {
8686
t.Run(tc.host, func(t *testing.T) {
8787
gc := NewGatewayCommon(&GatewayConfig{Host: tc.host})
88-
assert.Equal(t, tc.want, gc.client.BaseURL)
88+
assert.Equal(t, tc.want, gc.client.BaseURL())
8989
})
9090
}
9191
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ module github.com/hugoh/tmhi-gateway/v2
33
go 1.26.0
44

55
require (
6-
github.com/go-resty/resty/v2 v2.17.2
76
github.com/stretchr/testify v1.11.1
7+
resty.dev/v3 v3.0.0-rc.2
88
)
99

1010
require (

go.sum

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
22
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3-
github.com/go-resty/resty/v2 v2.17.2 h1:FQW5oHYcIlkCNrMD2lloGScxcHJ0gkjshV3qcQAyHQk=
4-
github.com/go-resty/resty/v2 v2.17.2/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
53
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
64
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
75
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
86
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
97
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
108
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
11-
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
12-
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
139
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
1410
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1511
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
1612
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
13+
resty.dev/v3 v3.0.0-rc.2 h1:NE6RqPkUd27eQ5dFSb5tfyT3s6XX9ZySp9qZ7Nw1bn0=
14+
resty.dev/v3 v3.0.0-rc.2/go.mod h1:NTOerrC/4T7/FE6tXIZGIysXXBdgNqwMZuKtxpea9NM=

nokia.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ func (n *NokiaGateway) Login(ctx context.Context) error {
6565

6666
n.credentials.SID = loginResp.Sid
6767
n.credentials.csrfToken = loginResp.CsrfToken
68-
//nolint:gosec // Secure/HttpOnly/SameSite only apply to response cookies, not outgoing requests.
69-
n.client.SetCookie(&http.Cookie{Name: sidCookieName, Value: n.credentials.SID})
7068

7169
return nil
7270
}
@@ -85,12 +83,17 @@ func (n *NokiaGateway) Reboot(ctx context.Context) error {
8583
return nil
8684
}
8785

88-
resp, err := n.client.R().SetContext(ctx).SetFormData(formData).Post("/reboot_web_app.cgi")
86+
//nolint:gosec // Secure/HttpOnly/SameSite only apply to response cookies, not outgoing requests.
87+
resp, err := n.client.R().
88+
SetContext(ctx).
89+
SetCookie(&http.Cookie{Name: sidCookieName, Value: n.credentials.SID}).
90+
SetFormData(formData).
91+
Post("/reboot_web_app.cgi")
8992
if err != nil {
9093
return fmt.Errorf("error sending reboot request: %w", err)
9194
}
9295

93-
if resp.IsError() {
96+
if resp.IsStatusFailure() {
9497
status := resp.StatusCode()
9598
if status == http.StatusUnauthorized || status == http.StatusForbidden {
9699
n.logout()
@@ -131,9 +134,6 @@ func (n *NokiaGateway) isLoggedIn() bool {
131134

132135
func (n *NokiaGateway) logout() {
133136
n.credentials = nokiaLoginData{}
134-
// resty.Client.SetCookie/SetCookies both append; assign directly to
135-
// replace the slice so re-login doesn't accumulate stale sid cookies.
136-
n.client.Cookies = nil
137137
}
138138

139139
func (n *NokiaGateway) getCredentials(
@@ -166,7 +166,7 @@ func (n *NokiaGateway) getCredentials(
166166
return nil, NewAuthError(0, "login request failed", err)
167167
}
168168

169-
if resp.IsError() {
169+
if resp.IsStatusFailure() {
170170
return nil, NewAuthError(resp.StatusCode(), resp.String(), nil)
171171
}
172172

@@ -191,7 +191,7 @@ func (n *NokiaGateway) getNonce(ctx context.Context) (*nokiaNonce, error) {
191191
return nil, fmt.Errorf("error getting nonce: %w", err)
192192
}
193193

194-
if resp.IsError() {
194+
if resp.IsStatusFailure() {
195195
return nil, NewGatewayError("nonce", resp.StatusCode(), resp.String(), ErrAuthentication)
196196
}
197197

nokia_test.go

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import (
66
"strings"
77
"testing"
88

9-
"github.com/go-resty/resty/v2"
109
"github.com/stretchr/testify/assert"
1110
"github.com/stretchr/testify/require"
11+
"resty.dev/v3"
1212
)
1313

1414
const (
@@ -331,25 +331,37 @@ func TestNokiaGateway_NotImplemented(t *testing.T) {
331331
})
332332
}
333333

334-
func TestNokiaGateway_logout_ClearsCookie(t *testing.T) {
335-
ts := newTestServer(t, func(w http.ResponseWriter, _ *http.Request) {
334+
func TestNokiaGateway_logout_ClearsCredentials(t *testing.T) {
335+
gw := newNokia(&GatewayCommon{config: &GatewayConfig{}}, testValidSID, testValidToken)
336+
337+
gw.logout()
338+
339+
assert.False(t, gw.isLoggedIn(), "logout should clear credentials")
340+
}
341+
342+
func TestNokiaGateway_Reboot_SendsSIDCookie(t *testing.T) {
343+
// Reboot must include the current session SID in the request cookie.
344+
var gotCookie string
345+
346+
ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
336347
w.WriteHeader(http.StatusOK)
348+
349+
if r.Method == http.MethodPost && r.URL.Path == "/reboot_web_app.cgi" {
350+
if c, err := r.Cookie(sidCookieName); err == nil {
351+
gotCookie = c.Value
352+
}
353+
}
337354
})
338355

339356
gw := nokiaTestGw(ts, nokiaConfig(ts), testValidSID, testValidToken)
340-
//nolint:gosec // Secure/HttpOnly/SameSite only apply to response cookies, not outgoing requests.
341-
gw.client.SetCookie(&http.Cookie{Name: sidCookieName, Value: testValidSID})
342-
343-
gw.logout()
344357

345-
assert.False(t, gw.isLoggedIn(), "logout should clear credentials")
346-
assert.Empty(t, gw.client.Cookies, "logout should clear all resty cookies")
358+
require.NoError(t, gw.Reboot(t.Context()))
359+
assert.Equal(t, testValidSID, gotCookie, "reboot request must carry the session SID cookie")
347360
}
348361

349-
func TestNokiaGateway_Reboot_ReloginNoDuplicateCookie(t *testing.T) {
350-
// After reboot (which calls logout), a subsequent Login should not
351-
// accumulate a second sid cookie from the previous session.
352-
callCount := 0
362+
func TestNokiaGateway_Reboot_ReloginHasFreshSID(t *testing.T) {
363+
// After reboot (which calls logout), a subsequent Login must use fresh credentials,
364+
// not carry over the old SID.
353365
ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
354366
w.Header().Set("Content-Type", "application/json")
355367
w.WriteHeader(http.StatusOK)
@@ -358,34 +370,22 @@ func TestNokiaGateway_Reboot_ReloginNoDuplicateCookie(t *testing.T) {
358370
case r.Method == http.MethodGet:
359371
_, _ = w.Write([]byte(testNonceBody))
360372
case r.Method == http.MethodPost && r.URL.Path == loginWebAppCGI:
361-
callCount++
362373
_, _ = w.Write([]byte(testLoginRespBody))
363-
case r.Method == http.MethodPost && r.URL.Path == "/reboot_web_app.cgi":
364-
// pass
365374
}
366375
})
367376

368377
gw := nokiaTestGw(ts, nokiaConfig(ts), testValidSID, testValidToken)
369-
//nolint:gosec // Secure/HttpOnly/SameSite only apply to response cookies, not outgoing requests.
370-
gw.client.SetCookie(&http.Cookie{Name: sidCookieName, Value: testValidSID})
371378

372379
require.NoError(t, gw.Reboot(t.Context()))
373380
require.NoError(t, gw.Login(t.Context()))
374381

375-
sidCookies := 0
376-
377-
for _, c := range gw.client.Cookies {
378-
if c.Name == sidCookieName {
379-
sidCookies++
380-
}
381-
}
382-
383382
assert.Equal(
384383
t,
385-
1,
386-
sidCookies,
387-
"re-login after reboot must not accumulate duplicate sid cookies",
384+
"testSid",
385+
gw.credentials.SID,
386+
"re-login after reboot must have fresh SID, not the old one",
388387
)
388+
assert.NotEqual(t, testValidSID, gw.credentials.SID)
389389
}
390390

391391
func TestNewNokiaGateway(t *testing.T) {

0 commit comments

Comments
 (0)