proxy-pool/internal/gateway/server/routing_test.go

107 lines
3.6 KiB
Go

package server
import (
"errors"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"time"
"proxy-pool/internal/domain/routing"
"proxy-pool/internal/gateway/snapshot"
)
func TestRulesRouterReturnsMatchedUpstreams(t *testing.T) {
t.Parallel()
rules, err := routing.Compile([]routing.Rule{{
Name: "api",
Match: routing.Match{HostRegex: `^example\.test$`, Methods: []string{http.MethodGet}, PathRegex: `^/v1/`},
Upstreams: []string{"provider-a", "provider-b"},
Action: routing.ActionProxy,
}})
if err != nil {
t.Fatalf("routing.Compile() error = %v", err)
}
router := NewRulesRouter(rules)
request := httptest.NewRequest(http.MethodGet, "http://example.test/v1/items", nil)
result, err := router.Route(request)
if err != nil {
t.Fatalf("Route() error = %v", err)
}
if !reflect.DeepEqual(result.Upstreams, []string{"provider-a", "provider-b"}) {
t.Fatalf("upstreams = %v", result.Upstreams)
}
}
func TestRulesRouterReturnsStaticDirectRoute(t *testing.T) {
t.Parallel()
rules, err := routing.Compile([]routing.Rule{{
Name: "direct-api", Match: routing.Match{HostRegex: "^api\\.example\\.test$"},
Action: routing.ActionDirect,
}})
if err != nil {
t.Fatalf("routing.Compile() error = %v", err)
}
result, err := NewRulesRouter(rules).Route(httptest.NewRequest(http.MethodGet, "http://api.example.test/v1/items", nil))
if err != nil {
t.Fatalf("Route() error = %v", err)
}
if result.RoutingName != "direct-api" || result.Action != routing.ActionDirect || len(result.Upstreams) != 0 {
t.Fatalf("Route() = %+v", result)
}
}
func TestRulesRouterRejectsExplicitRejectAndMissingRoute(t *testing.T) {
t.Parallel()
rules, err := routing.Compile([]routing.Rule{{
Name: "blocked",
Match: routing.Match{HostRegex: `^blocked\.test$`},
Action: routing.ActionReject,
}})
if err != nil {
t.Fatalf("routing.Compile() error = %v", err)
}
router := NewRulesRouter(rules)
if _, err := router.Route(httptest.NewRequest(http.MethodGet, "http://blocked.test/", nil)); !errors.Is(err, ErrRouteRejected) {
t.Fatalf("blocked Route() error = %v", err)
}
if _, err := router.Route(httptest.NewRequest(http.MethodGet, "http://missing.test/", nil)); !errors.Is(err, ErrRouteNotFound) {
t.Fatalf("missing Route() error = %v", err)
}
}
func TestSnapshotRouterMatchesRulesFromCurrentSnapshot(t *testing.T) {
store := snapshot.NewStore("cluster-a", "worker-a")
envelope := snapshot.Envelope{
ClusterID: "cluster-a", WorkerID: "worker-a", Epoch: 1, Version: 1, Full: true,
ValidUntil: time.Now().Add(time.Minute),
Routing: []routing.Rule{{
Name: "gateway-api", Match: routing.Match{HostRegex: `^api\.example\.test$`},
Upstreams: []string{"provider-a"}, Action: routing.ActionProxy,
Strategy: routing.Strategy{Type: routing.StrategyWeighted, Weights: map[string]uint32{"provider-a": 3}},
OnUnavailable: routing.OnUnavailableWait, WaitTimeout: 25 * time.Millisecond,
}},
}
envelope.Checksum = snapshot.ChecksumWithRouting(envelope.Proxies, envelope.Routing)
if err := store.Apply(envelope); err != nil {
t.Fatalf("Apply(): %v", err)
}
router := NewSnapshotRouter(store)
result, err := router.Route(httptest.NewRequest(http.MethodGet, "http://api.example.test/items", nil))
if err != nil {
t.Fatalf("Route(): %v", err)
}
if result.RoutingName != "gateway-api" || result.Strategy.Type != routing.StrategyWeighted ||
!reflect.DeepEqual(result.Strategy.Weights, map[string]uint32{"provider-a": 3}) ||
result.OnUnavailable != routing.OnUnavailableWait || result.WaitTimeout != 25*time.Millisecond {
t.Fatalf("Route() = %+v", result)
}
}