@@ -2,9 +2,12 @@ package repository
22
33import (
44 "context"
5+ "errors"
6+ "fmt"
57 "io"
68 "net/http"
79 "net/http/httptest"
10+ "net/url"
811 "strings"
912 "testing"
1013
@@ -71,24 +74,24 @@ func (s *ProxyProbeServiceSuite) TestProbeProxy_Success_IPAPI() {
7174 require .Equal (s .T (), "CC" , info .CountryCode )
7275}
7376
74- func (s * ProxyProbeServiceSuite ) TestProbeProxy_Success_HTTPBinFallback () {
77+ func (s * ProxyProbeServiceSuite ) TestProbeProxy_Success_IPifyFallback () {
7578 s .setupProxyServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
7679 // ip-api 失败
7780 if strings .Contains (r .RequestURI , "ip-api.com" ) {
7881 w .WriteHeader (http .StatusServiceUnavailable )
7982 return
8083 }
81- // httpbin 成功
82- if strings .Contains (r .RequestURI , "httpbin .org" ) {
84+ // ipify 成功
85+ if strings .Contains (r .RequestURI , "ipify .org" ) {
8386 w .Header ().Set ("Content-Type" , "application/json" )
84- _ , _ = io .WriteString (w , `{"origin ": "5.6.7.8"}` )
87+ _ , _ = io .WriteString (w , `{"ip ": "5.6.7.8"}` )
8588 return
8689 }
8790 w .WriteHeader (http .StatusServiceUnavailable )
8891 }))
8992
9093 info , latencyMs , err := s .prober .ProbeProxy (s .ctx , s .proxySrv .URL )
91- require .NoError (s .T (), err , "ProbeProxy should fallback to httpbin " )
94+ require .NoError (s .T (), err , "ProbeProxy should fallback to ipify " )
9295 require .GreaterOrEqual (s .T (), latencyMs , int64 (0 ), "unexpected latency" )
9396 require .Equal (s .T (), "5.6.7.8" , info .IP )
9497}
@@ -101,6 +104,13 @@ func (s *ProxyProbeServiceSuite) TestProbeProxy_AllFailed() {
101104 _ , _ , err := s .prober .ProbeProxy (s .ctx , s .proxySrv .URL )
102105 require .Error (s .T (), err )
103106 require .ErrorContains (s .T (), err , "all probe URLs failed" )
107+
108+ // 聚合信息里每个探测点各占一段,且不泄漏完整探测 URL
109+ msg := err .Error ()
110+ require .Contains (s .T (), msg , "ip-api: " )
111+ require .Contains (s .T (), msg , "ipify: " )
112+ require .NotContains (s .T (), msg , "http://" )
113+ require .Less (s .T (), len (msg ), 240 , "聚合提示应保持精简: %s" , msg )
104114}
105115
106116func (s * ProxyProbeServiceSuite ) TestProbeProxy_InvalidJSON () {
@@ -110,8 +120,8 @@ func (s *ProxyProbeServiceSuite) TestProbeProxy_InvalidJSON() {
110120 _ , _ = io .WriteString (w , "not-json" )
111121 return
112122 }
113- // httpbin 也返回无效响应
114- if strings .Contains (r .RequestURI , "httpbin .org" ) {
123+ // ipify 也返回无效响应
124+ if strings .Contains (r .RequestURI , "ipify .org" ) {
115125 w .Header ().Set ("Content-Type" , "application/json" )
116126 _ , _ = io .WriteString (w , "not-json" )
117127 return
@@ -151,21 +161,67 @@ func (s *ProxyProbeServiceSuite) TestParseIPAPI_Failure() {
151161 require .ErrorContains (s .T (), err , "rate limited" )
152162}
153163
154- func (s * ProxyProbeServiceSuite ) TestParseHTTPBin_Success () {
155- body := []byte (`{"origin ": "9.8.7.6"}` )
156- info , latencyMs , err := s .prober .parseHTTPBin (body , 50 )
164+ func (s * ProxyProbeServiceSuite ) TestParseIPify_Success () {
165+ body := []byte (`{"ip ": "9.8.7.6"}` )
166+ info , latencyMs , err := s .prober .parseIPify (body , 50 )
157167 require .NoError (s .T (), err )
158168 require .Equal (s .T (), int64 (50 ), latencyMs )
159169 require .Equal (s .T (), "9.8.7.6" , info .IP )
160170}
161171
162- func (s * ProxyProbeServiceSuite ) TestParseHTTPBin_NoIP () {
163- body := []byte (`{"origin ": ""}` )
164- _ , _ , err := s .prober .parseHTTPBin (body , 50 )
172+ func (s * ProxyProbeServiceSuite ) TestParseIPify_NoIP () {
173+ body := []byte (`{"ip ": ""}` )
174+ _ , _ , err := s .prober .parseIPify (body , 50 )
165175 require .Error (s .T (), err )
166176 require .ErrorContains (s .T (), err , "no IP found" )
167177}
168178
179+ func TestSummarizeProbeError (t * testing.T ) {
180+ longReason := strings .Repeat ("超时原因" , 40 )
181+ cases := []struct {
182+ name string
183+ err error
184+ expect string
185+ }{
186+ {"nil" , nil , "unknown" },
187+ {"canceled" , fmt .Errorf ("proxy connection failed: %w" , context .Canceled ), "canceled" },
188+ {"deadline" , fmt .Errorf ("proxy connection failed: %w" , context .DeadlineExceeded ), "timeout" },
189+ {
190+ "net timeout" ,
191+ & url.Error {Op : "Get" , URL : "http://api64.ipify.org?format=json" , Err : timeoutErr {}},
192+ "timeout" ,
193+ },
194+ {
195+ "strips probe url" ,
196+ fmt .Errorf ("proxy connection failed: %w" , & url.Error {
197+ Op : "Get" ,
198+ URL : "http://ip-api.com/json/?lang=zh-CN" ,
199+ Err : errors .New ("connection refused" ),
200+ }),
201+ "proxy connection failed: connection refused" ,
202+ },
203+ {"status code" , errors .New ("request failed with status: 503" ), "request failed with status: 503" },
204+ }
205+
206+ for _ , tc := range cases {
207+ t .Run (tc .name , func (t * testing.T ) {
208+ require .Equal (t , tc .expect , summarizeProbeError (tc .err ))
209+ })
210+ }
211+
212+ t .Run ("truncates long reason" , func (t * testing.T ) {
213+ got := summarizeProbeError (errors .New (longReason ))
214+ require .Equal (t , maxProbeReasonLen + 1 , len ([]rune (got )), "应按 rune 截断并追加省略号" )
215+ require .True (t , strings .HasSuffix (got , "…" ))
216+ })
217+ }
218+
219+ type timeoutErr struct {}
220+
221+ func (timeoutErr ) Error () string { return "i/o timeout" }
222+ func (timeoutErr ) Timeout () bool { return true }
223+ func (timeoutErr ) Temporary () bool { return true }
224+
169225func TestProxyProbeServiceSuite (t * testing.T ) {
170226 suite .Run (t , new (ProxyProbeServiceSuite ))
171227}
0 commit comments