proxy-pool/internal/checker/probe/probe_test.go
youfak 864320a2c8
Some checks are pending
ci / proto (push) Waiting to run
ci / test (ubuntu-latest) (push) Waiting to run
ci / test (windows-latest) (push) Waiting to run
ci / race (push) Waiting to run
ci / integration (push) Waiting to run
feat: support egress probe task contracts
2026-07-31 22:32:49 +08:00

100 lines
3.7 KiB
Go

package probe
import (
"context"
"net"
"net/http"
"net/http/httptest"
"strconv"
"testing"
"time"
controlplanev1 "proxy-pool/gen/controlplane/v1"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/timestamppb"
)
func TestExecutorBasicConfirmsProxyHandshakeEvenWhenProbeTargetFails(t *testing.T) {
proxy := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
if request.URL.String() != "http://example.invalid/" {
t.Errorf("proxy request URL = %q", request.URL)
}
if value := request.Header.Get("Proxy-Authorization"); value != "Basic dXNlcjpzZWNyZXQ=" {
t.Errorf("Proxy-Authorization = %q", value)
}
response.WriteHeader(http.StatusBadGateway)
}))
defer proxy.Close()
task := validTask(t, proxy.URL, controlplanev1.CheckLevel_CHECK_LEVEL_BASIC)
result := NewExecutor().Execute(context.Background(), task)
if !result.Success || result.FailureClass != "" || result.Latency <= 0 {
t.Fatalf("Execute(BASIC) = %+v", result)
}
}
func splitProxyAddress(t *testing.T, address string) (string, uint32) {
t.Helper()
host, rawPort, err := net.SplitHostPort(address[len("http://"):])
if err != nil {
t.Fatalf("net.SplitHostPort(%q): %v", address, err)
}
port, err := strconv.ParseUint(rawPort, 10, 16)
if err != nil {
t.Fatalf("strconv.ParseUint(%q): %v", rawPort, err)
}
return host, uint32(port)
}
func TestExecutorTargetReportsHTTPFailureAsFact(t *testing.T) {
proxy := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, _ *http.Request) {
response.WriteHeader(http.StatusServiceUnavailable)
}))
defer proxy.Close()
task := validTask(t, proxy.URL, controlplanev1.CheckLevel_CHECK_LEVEL_TARGET)
task.RoutingName = "route-a"
task.TargetUrl = "http://target.example/check"
result := NewExecutor().Execute(context.Background(), task)
if result.Success || result.FailureClass != FailureTargetHTTPStatus || result.Latency <= 0 {
t.Fatalf("Execute(TARGET) = %+v", result)
}
}
func TestExecutorRejectsEgressTaskWithRoutingProfile(t *testing.T) {
proxy := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, _ *http.Request) {
response.WriteHeader(http.StatusOK)
}))
defer proxy.Close()
task := validTask(t, proxy.URL, controlplanev1.CheckLevel_CHECK_LEVEL_EGRESS)
task.RoutingName = "route-a"
task.TargetUrl = "https://egress.example/identity"
result := NewExecutor().Execute(context.Background(), task)
if result.Success || result.FailureClass != FailureInvalidTask {
t.Fatalf("Execute(EGRESS with routing) = %+v", result)
}
}
func TestExecutorReportsUnsupportedProtocolAsFact(t *testing.T) {
now := time.Now().UTC()
result := NewExecutor().Execute(context.Background(), &controlplanev1.CheckTask{
TaskId: "task-a", ProxyId: "proxy-a", Protocol: controlplanev1.ProxyProtocol_PROXY_PROTOCOL_SOCKS5,
Host: "proxy.example", Port: 1080, Level: controlplanev1.CheckLevel_CHECK_LEVEL_BASIC,
Timeout: durationpb.New(time.Second), Attempt: 1, MaxAttempts: 1, Deadline: timestamppb.New(now.Add(time.Second)),
})
if result.Success || result.FailureClass != FailureUnsupportedProxy {
t.Fatalf("Execute(SOCKS5) = %+v", result)
}
}
func validTask(t *testing.T, proxyAddress string, level controlplanev1.CheckLevel) *controlplanev1.CheckTask {
t.Helper()
host, port := splitProxyAddress(t, proxyAddress)
now := time.Now().UTC()
return &controlplanev1.CheckTask{
TaskId: "task-a", ProxyId: "proxy-a", Protocol: controlplanev1.ProxyProtocol_PROXY_PROTOCOL_HTTP,
Host: host, Port: port, Level: level, Username: "user", Password: "secret",
Timeout: durationpb.New(time.Second), Attempt: 1, MaxAttempts: 1,
Deadline: timestamppb.New(now.Add(time.Second)),
}
}