bouncer/internal/rule/http/request_test.go

325 lines
6.8 KiB
Go

package http
import (
"context"
"fmt"
"net/http"
"testing"
"forge.cadoles.com/cadoles/bouncer/internal/rule"
"github.com/pkg/errors"
)
func TestSetRequestHost(t *testing.T) {
type Vars struct {
NewHost string `expr:"newHost"`
}
engine := createRuleEngine[Vars](t,
rule.WithExpr(setRequestHostFunc()),
rule.WithRules(
"set_host(ctx, vars.newHost)",
),
)
req, err := http.NewRequest("GET", "http://example.net", nil)
if err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
ctx := context.Background()
ctx = WithRequest(ctx, req)
vars := Vars{
NewHost: "foobar",
}
if _, err := engine.Apply(ctx, vars); err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
if e, g := vars.NewHost, req.Host; e != g {
t.Errorf("req.Host: expected '%v', got '%v'", e, g)
}
}
func TestSetRequestURL(t *testing.T) {
type Vars struct {
NewURL string `expr:"newURL"`
}
engine := createRuleEngine[Vars](t,
rule.WithExpr(setRequestURLFunc()),
rule.WithRules(
"set_url(ctx, vars.newURL)",
),
)
req, err := http.NewRequest("GET", "http://example.net", nil)
if err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
ctx := context.Background()
ctx = WithRequest(ctx, req)
vars := Vars{
NewURL: "http://localhost",
}
if _, err := engine.Apply(ctx, vars); err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
if e, g := vars.NewURL, req.URL.String(); e != g {
t.Errorf("req.URL.String(): expected '%v', got '%v'", e, g)
}
}
func TestAddRequestHeader(t *testing.T) {
type Vars struct {
NewHeaderKey string `expr:"newHeaderKey"`
NewHeaderValue string `expr:"newHeaderValue"`
}
engine := createRuleEngine[Vars](t,
rule.WithExpr(addRequestHeaderFunc()),
rule.WithRules(
"add_header(ctx, vars.newHeaderKey, vars.newHeaderValue)",
),
)
req, err := http.NewRequest("GET", "http://example.net", nil)
if err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
ctx := context.Background()
ctx = WithRequest(ctx, req)
vars := Vars{
NewHeaderKey: "X-My-Header",
NewHeaderValue: "foobar",
}
if _, err := engine.Apply(ctx, vars); err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
if e, g := vars.NewHeaderValue, req.Header.Get(vars.NewHeaderKey); e != g {
t.Errorf("req.Header.Get(vars.NewHeaderKey): expected '%v', got '%v'", e, g)
}
}
func TestSetRequestHeader(t *testing.T) {
type Vars struct {
HeaderKey string `expr:"headerKey"`
HeaderValue string `expr:"headerValue"`
}
engine := createRuleEngine[Vars](t,
rule.WithExpr(setRequestHeaderFunc()),
rule.WithRules(
"set_header(ctx, vars.headerKey, vars.headerValue)",
),
)
req, err := http.NewRequest("GET", "http://example.net", nil)
if err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
vars := Vars{
HeaderKey: "X-My-Header",
HeaderValue: "foobar",
}
req.Header.Set(vars.HeaderKey, "test")
ctx := context.Background()
ctx = WithRequest(ctx, req)
if _, err := engine.Apply(ctx, vars); err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
if e, g := vars.HeaderValue, req.Header.Get(vars.HeaderKey); e != g {
t.Errorf("req.Header.Get(vars.HeaderKey): expected '%v', got '%v'", e, g)
}
}
func TestDelRequestHeaders(t *testing.T) {
type Vars struct {
HeaderPattern string `expr:"headerPattern"`
}
engine := createRuleEngine[Vars](t,
rule.WithExpr(delRequestHeadersFunc()),
rule.WithRules(
"del_headers(ctx, vars.headerPattern)",
),
)
req, err := http.NewRequest("GET", "http://example.net", nil)
if err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
vars := Vars{
HeaderPattern: "X-My-*",
}
req.Header.Set("X-My-Header", "test")
ctx := context.Background()
ctx = WithRequest(ctx, req)
if _, err := engine.Apply(ctx, vars); err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
if val := req.Header.Get("X-My-Header"); val != "" {
t.Errorf("req.Header.Get(\"X-My-Header\") should be empty, got '%v'", val)
}
}
func TestAddRequestCookie(t *testing.T) {
type TestCase struct {
Cookie map[string]any
Check func(t *testing.T, tc TestCase, req *http.Request)
ShouldFail bool
}
testCases := []TestCase{
{
Cookie: map[string]any{
"name": "test",
},
Check: func(t *testing.T, tc TestCase, req *http.Request) {
cookie, err := req.Cookie(tc.Cookie["name"].(string))
if err != nil {
t.Errorf("%+v", errors.WithStack(err))
return
}
if e, g := tc.Cookie["name"], cookie.Name; e != g {
t.Errorf("cookie.Name: expected '%v', got '%v'", e, g)
}
},
},
{
Cookie: map[string]any{
"name": "foo",
"value": "test",
},
Check: func(t *testing.T, tc TestCase, req *http.Request) {
cookie, err := req.Cookie(tc.Cookie["name"].(string))
if err != nil {
t.Errorf("%+v", errors.WithStack(err))
return
}
if e, g := tc.Cookie["name"], cookie.Name; e != g {
t.Errorf("cookie.Name: expected '%v', got '%v'", e, g)
}
if e, g := tc.Cookie["value"], cookie.Value; e != g {
t.Errorf("cookie.Value: expected '%v', got '%v'", e, g)
}
},
},
}
for idx, tc := range testCases {
t.Run(fmt.Sprintf("Case_%d", idx), func(t *testing.T) {
type Vars struct {
NewCookie map[string]any `expr:"new_cookie"`
}
engine := createRuleEngine[Vars](t,
rule.WithExpr(addRequestCookieFunc()),
rule.WithRules(
`add_cookie(ctx, vars.new_cookie)`,
),
)
req, err := http.NewRequest("GET", "http://example.net", nil)
if err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
vars := Vars{
NewCookie: tc.Cookie,
}
ctx := context.Background()
ctx = WithRequest(ctx, req)
if _, err := engine.Apply(ctx, vars); err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
if tc.ShouldFail {
t.Error("engine.Apply() should have failed")
}
if tc.Check != nil {
tc.Check(t, tc, req)
}
})
}
}
func TestGetRequestCookie(t *testing.T) {
type Vars struct {
CookieName string `expr:"cookieName"`
}
engine := createRuleEngine[Vars](t,
rule.WithExpr(getRequestCookieFunc()),
rule.WithRules(
"let cookie = get_cookie(ctx, vars.cookieName); cookie.value",
),
)
req, err := http.NewRequest("GET", "http://example.net", nil)
if err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
vars := Vars{
CookieName: "foo",
}
cookie := &http.Cookie{
Name: vars.CookieName,
Value: "bar",
}
req.AddCookie(cookie)
ctx := context.Background()
ctx = WithRequest(ctx, req)
results, err := engine.Apply(ctx, vars)
if err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
if e, g := cookie.Value, results[0]; e != g {
t.Errorf("result[0]: expected '%v', got '%v'", e, g)
}
}
func createRuleEngine[V any](t *testing.T, funcs ...rule.OptionFunc) *rule.Engine[V] {
engine, err := rule.NewEngine[V](funcs...)
if err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
return engine
}