| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- package repository
- import (
- "context"
- "io"
- "net/http"
- "net/http/httptest"
- "strings"
- "testing"
- "github.com/stretchr/testify/require"
- "github.com/stretchr/testify/suite"
- )
- type ProxyProbeServiceSuite struct {
- suite.Suite
- ctx context.Context
- proxySrv *httptest.Server
- prober *proxyProbeService
- }
- func (s *ProxyProbeServiceSuite) SetupTest() {
- s.ctx = context.Background()
- s.prober = &proxyProbeService{
- allowPrivateHosts: true,
- }
- }
- func (s *ProxyProbeServiceSuite) TearDownTest() {
- if s.proxySrv != nil {
- s.proxySrv.Close()
- s.proxySrv = nil
- }
- }
- func (s *ProxyProbeServiceSuite) setupProxyServer(handler http.HandlerFunc) {
- s.proxySrv = newLocalTestServer(s.T(), handler)
- }
- func (s *ProxyProbeServiceSuite) TestProbeProxy_InvalidProxyURL() {
- _, _, err := s.prober.ProbeProxy(s.ctx, "://bad")
- require.Error(s.T(), err)
- require.ErrorContains(s.T(), err, "failed to create proxy client")
- }
- func (s *ProxyProbeServiceSuite) TestProbeProxy_UnsupportedProxyScheme() {
- _, _, err := s.prober.ProbeProxy(s.ctx, "ftp://127.0.0.1:1")
- require.Error(s.T(), err)
- require.ErrorContains(s.T(), err, "failed to create proxy client")
- }
- func (s *ProxyProbeServiceSuite) TestProbeProxy_Success_IPAPI() {
- s.setupProxyServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- // 检查是否是 ip-api 请求
- if strings.Contains(r.RequestURI, "ip-api.com") {
- w.Header().Set("Content-Type", "application/json")
- _, _ = io.WriteString(w, `{"status":"success","query":"1.2.3.4","city":"c","regionName":"r","country":"cc","countryCode":"CC"}`)
- return
- }
- // 其他请求返回错误
- w.WriteHeader(http.StatusServiceUnavailable)
- }))
- info, latencyMs, err := s.prober.ProbeProxy(s.ctx, s.proxySrv.URL)
- require.NoError(s.T(), err, "ProbeProxy")
- require.GreaterOrEqual(s.T(), latencyMs, int64(0), "unexpected latency")
- require.Equal(s.T(), "1.2.3.4", info.IP)
- require.Equal(s.T(), "c", info.City)
- require.Equal(s.T(), "r", info.Region)
- require.Equal(s.T(), "cc", info.Country)
- require.Equal(s.T(), "CC", info.CountryCode)
- }
- func (s *ProxyProbeServiceSuite) TestProbeProxy_Success_IPifyFallback() {
- s.setupProxyServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- // ip-api 失败
- if strings.Contains(r.RequestURI, "ip-api.com") {
- w.WriteHeader(http.StatusServiceUnavailable)
- return
- }
- // ipify 成功
- if strings.Contains(r.RequestURI, "api64.ipify.org") {
- w.Header().Set("Content-Type", "application/json")
- _, _ = io.WriteString(w, `{"ip": "5.6.7.8"}`)
- return
- }
- w.WriteHeader(http.StatusServiceUnavailable)
- }))
- info, latencyMs, err := s.prober.ProbeProxy(s.ctx, s.proxySrv.URL)
- require.NoError(s.T(), err, "ProbeProxy should fallback to ipify")
- require.GreaterOrEqual(s.T(), latencyMs, int64(0), "unexpected latency")
- require.Equal(s.T(), "5.6.7.8", info.IP)
- }
- func (s *ProxyProbeServiceSuite) TestProbeProxy_AllFailed() {
- s.setupProxyServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.WriteHeader(http.StatusServiceUnavailable)
- }))
- _, _, err := s.prober.ProbeProxy(s.ctx, s.proxySrv.URL)
- require.Error(s.T(), err)
- require.ErrorContains(s.T(), err, "all probe URLs failed")
- }
- func (s *ProxyProbeServiceSuite) TestProbeProxy_InvalidJSON() {
- s.setupProxyServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if strings.Contains(r.RequestURI, "ip-api.com") {
- w.Header().Set("Content-Type", "application/json")
- _, _ = io.WriteString(w, "not-json")
- return
- }
- // ipify 也返回无效响应
- if strings.Contains(r.RequestURI, "api64.ipify.org") {
- w.Header().Set("Content-Type", "application/json")
- _, _ = io.WriteString(w, "not-json")
- return
- }
- w.WriteHeader(http.StatusServiceUnavailable)
- }))
- _, _, err := s.prober.ProbeProxy(s.ctx, s.proxySrv.URL)
- require.Error(s.T(), err)
- require.ErrorContains(s.T(), err, "all probe URLs failed")
- }
- func (s *ProxyProbeServiceSuite) TestProbeProxy_ProxyServerClosed() {
- s.setupProxyServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
- s.proxySrv.Close()
- _, _, err := s.prober.ProbeProxy(s.ctx, s.proxySrv.URL)
- require.Error(s.T(), err, "expected error when proxy server is closed")
- }
- func (s *ProxyProbeServiceSuite) TestParseIPAPI_Success() {
- body := []byte(`{"status":"success","query":"1.2.3.4","city":"Beijing","regionName":"Beijing","country":"China","countryCode":"CN"}`)
- info, latencyMs, err := s.prober.parseIPAPI(body, 100)
- require.NoError(s.T(), err)
- require.Equal(s.T(), int64(100), latencyMs)
- require.Equal(s.T(), "1.2.3.4", info.IP)
- require.Equal(s.T(), "Beijing", info.City)
- require.Equal(s.T(), "Beijing", info.Region)
- require.Equal(s.T(), "China", info.Country)
- require.Equal(s.T(), "CN", info.CountryCode)
- }
- func (s *ProxyProbeServiceSuite) TestParseIPAPI_Failure() {
- body := []byte(`{"status":"fail","message":"rate limited"}`)
- _, _, err := s.prober.parseIPAPI(body, 100)
- require.Error(s.T(), err)
- require.ErrorContains(s.T(), err, "rate limited")
- }
- func (s *ProxyProbeServiceSuite) TestParseIPify_Success() {
- body := []byte(`{"ip": "2001:db8::1"}`)
- info, latencyMs, err := s.prober.parseIPify(body, 50)
- require.NoError(s.T(), err)
- require.Equal(s.T(), int64(50), latencyMs)
- require.Equal(s.T(), "2001:db8::1", info.IP)
- }
- func (s *ProxyProbeServiceSuite) TestParseIPify_NoIP() {
- body := []byte(`{"ip": ""}`)
- _, _, err := s.prober.parseIPify(body, 50)
- require.Error(s.T(), err)
- require.ErrorContains(s.T(), err, "no IP found")
- }
- func (s *ProxyProbeServiceSuite) TestParseChatGPTTrace_Success() {
- body := []byte("fl=abc\nh=chatgpt.com\nip=203.0.113.5\nts=1700000000\nloc=US\ntz=UTC\n")
- info, latencyMs, err := s.prober.parseChatGPTTrace(body, 320)
- require.NoError(s.T(), err)
- require.Equal(s.T(), int64(320), latencyMs)
- require.Equal(s.T(), "203.0.113.5", info.IP)
- require.Equal(s.T(), "US", info.CountryCode)
- }
- func (s *ProxyProbeServiceSuite) TestParseChatGPTTrace_NoIP() {
- body := []byte("fl=abc\nh=chatgpt.com\nloc=US\n")
- _, _, err := s.prober.parseChatGPTTrace(body, 100)
- require.Error(s.T(), err)
- require.ErrorContains(s.T(), err, "chatgpt-trace: no ip= found")
- }
- func TestProxyProbeServiceSuite(t *testing.T) {
- suite.Run(t, new(ProxyProbeServiceSuite))
- }
|