proxy-pool/internal/controller/worker/server_test.go
youfak 65aee12d51
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: wire checker observations into controller
2026-07-31 18:25:07 +08:00

137 lines
4.9 KiB
Go

package worker
import (
"context"
"errors"
"net"
"testing"
"time"
controlplanev1 "proxy-pool/gen/controlplane/v1"
"proxy-pool/internal/config"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func TestNewServerRejectsInvalidOptions(t *testing.T) {
controlPlane := validServerControlPlane()
service := &grpcServiceStub{}
if _, err := NewServer(controlPlane, nil, DefaultServerOptions()); !errors.Is(err, ErrInvalidServer) {
t.Fatalf("NewServer(nil service) error = %v, want ErrInvalidServer", err)
}
if _, err := NewServer(controlPlane, service, ServerOptions{ShutdownTimeout: -time.Second}); !errors.Is(err, ErrInvalidServer) {
t.Fatalf("NewServer(negative shutdown timeout) error = %v, want ErrInvalidServer", err)
}
controlPlane.Enabled = false
if _, err := NewServer(controlPlane, service, DefaultServerOptions()); !errors.Is(err, ErrInvalidServer) {
t.Fatalf("NewServer(disabled) error = %v, want ErrInvalidServer", err)
}
}
func TestServerServesAndStopsOnContextCancellation(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("net.Listen(): %v", err)
}
controlPlane := validServerControlPlane()
controlPlane.Listen = listener.Addr().String()
service := &grpcServiceStub{registration: Registration{WorkerID: "worker-a", SessionID: "session-a", OwnershipEpoch: 5, HeartbeatInterval: time.Second, MaxStaleAge: 2 * time.Second}}
server, err := NewServer(controlPlane, service, ServerOptions{ShutdownTimeout: time.Second})
if err != nil {
t.Fatalf("NewServer(): %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
result := make(chan error, 1)
go func() { result <- server.Serve(ctx, listener) }()
connection, err := grpc.NewClient(listener.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
t.Fatalf("grpc.NewClient(): %v", err)
}
client := controlplanev1.NewWorkerControlPlaneClient(connection)
requestCtx, requestCancel := context.WithTimeout(context.Background(), 3*time.Second)
defer requestCancel()
response, err := client.RegisterWorker(requestCtx, &controlplanev1.RegisterWorkerRequest{WorkerId: "worker-a", SupportedProtocolVersion: 1})
if err != nil || response.GetSessionId() != "session-a" {
t.Fatalf("RegisterWorker() = %+v, %v", response, err)
}
if err := connection.Close(); err != nil {
t.Fatalf("connection.Close(): %v", err)
}
cancel()
select {
case err := <-result:
if err != nil {
t.Fatalf("Serve() error = %v", err)
}
case <-time.After(3 * time.Second):
t.Fatal("Serve() did not stop after context cancellation")
}
}
func TestServerRegistersCheckerServiceOnTheExistingControlPlaneListener(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("net.Listen(): %v", err)
}
controlPlane := validServerControlPlane()
controlPlane.Listen = listener.Addr().String()
checker := &checkerServiceStub{}
server, err := NewServer(controlPlane, &grpcServiceStub{}, ServerOptions{ShutdownTimeout: time.Second, Checker: checker})
if err != nil {
t.Fatalf("NewServer(): %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
result := make(chan error, 1)
go func() { result <- server.Serve(ctx, listener) }()
connection, err := grpc.NewClient(listener.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
t.Fatalf("grpc.NewClient(): %v", err)
}
response, err := controlplanev1.NewCheckerControlPlaneClient(connection).ReportObservations(context.Background(), &controlplanev1.ObservationBatch{
CheckerId: "checker-a",
})
if err != nil || response.GetAccepted() != 1 || checker.checkerID != "checker-a" {
t.Fatalf("ReportObservations() = (%+v, %v); checker=%q", response, err, checker.checkerID)
}
if err := connection.Close(); err != nil {
t.Fatalf("connection.Close(): %v", err)
}
cancel()
select {
case err := <-result:
if err != nil {
t.Fatalf("Serve() error = %v", err)
}
case <-time.After(3 * time.Second):
t.Fatal("Serve() did not stop after context cancellation")
}
}
type checkerServiceStub struct {
controlplanev1.UnimplementedCheckerControlPlaneServer
checkerID string
}
func (stub *checkerServiceStub) ReportObservations(_ context.Context, request *controlplanev1.ObservationBatch) (*controlplanev1.ReportObservationsResponse, error) {
stub.checkerID = request.GetCheckerId()
return &controlplanev1.ReportObservationsResponse{Accepted: 1}, nil
}
func validServerControlPlane() config.ControlPlane {
return config.ControlPlane{
Enabled: true,
Listen: "127.0.0.1:8443",
ProtocolVersion: 1,
HeartbeatInterval: config.Duration(10 * time.Second),
SessionTTL: config.Duration(30 * time.Second),
MaxStaleAge: config.Duration(10 * time.Second),
MaxMessageBytes: 1 << 20,
MaxRuntimeCounters: 100,
MaxConcurrentStreams: 10,
TLS: config.ControlPlaneTLS{Mode: "disabled"},
}
}