mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-19 07:43:54 +01:00
* Add comprehensive post-request rules support for response phase * Enable response body, status, and header manipulation via set commands * Refactor command handlers to support both request and response phases * Implement response modifier system for post-request template execution * Support response-based rule matching with status and header checks * Add comprehensive benchmarks for matcher performance * Refactor authentication and proxying commands for unified error handling * Support negated conditions with ! * Enhance error handling, error formatting and validation * Routes: add `rule_file` field with rule preset support * Environment variable substitution: now supports variables without `GODOXY_` prefix * new conditions: * `on resp_header <key> [<value>]` * `on status <status>` * new commands: * `require_auth` * `set resp_header <key> <template>` * `set resp_body <template>` * `set status <code>` * `log <level> <path> <template>` * `notify <level> <provider> <title_template> <body_template>`
170 lines
3.3 KiB
Go
170 lines
3.3 KiB
Go
package rules
|
|
|
|
import (
|
|
"testing"
|
|
|
|
expect "github.com/yusing/goutils/testing"
|
|
)
|
|
|
|
func TestExtractExpr(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
wantT MatcherType
|
|
wantExpr string
|
|
}{
|
|
{
|
|
name: "string implicit",
|
|
in: "foo",
|
|
wantT: MatcherTypeString,
|
|
wantExpr: "foo",
|
|
},
|
|
{
|
|
name: "string explicit",
|
|
in: "string(`foo`)",
|
|
wantT: MatcherTypeString,
|
|
wantExpr: "foo",
|
|
},
|
|
{
|
|
name: "glob",
|
|
in: "glob(foo)",
|
|
wantT: MatcherTypeGlob,
|
|
wantExpr: "foo",
|
|
},
|
|
{
|
|
name: "glob quoted",
|
|
in: "glob(`foo`)",
|
|
wantT: MatcherTypeGlob,
|
|
wantExpr: "foo",
|
|
},
|
|
{
|
|
name: "regex",
|
|
in: "regex(^[A-Z]+$)",
|
|
wantT: MatcherTypeRegex,
|
|
wantExpr: "^[A-Z]+$",
|
|
},
|
|
{
|
|
name: "regex quoted",
|
|
in: "regex(`^[A-Z]+$`)",
|
|
wantT: MatcherTypeRegex,
|
|
wantExpr: "^[A-Z]+$",
|
|
},
|
|
{
|
|
name: "regex with parentheses",
|
|
in: "regex(test(group))",
|
|
wantT: MatcherTypeRegex,
|
|
wantExpr: "test(group)",
|
|
},
|
|
{
|
|
name: "regex complex",
|
|
in: `regex("^(_next/static|_next/image|favicon.ico).*$")`,
|
|
wantT: MatcherTypeRegex,
|
|
wantExpr: "^(_next/static|_next/image|favicon.ico).*$",
|
|
},
|
|
{
|
|
name: "quoted expr",
|
|
in: "glob(`'foo'`)",
|
|
wantT: MatcherTypeGlob,
|
|
wantExpr: "'foo'",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
typ, expr, err := ExtractExpr(tt.in)
|
|
expect.NoError(t, err)
|
|
expect.Equal(t, tt.wantT, typ)
|
|
expect.Equal(t, tt.wantExpr, expr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExtractExprInvalid(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "missing closing quote",
|
|
in: "glob(`foo)",
|
|
wantErr: "unterminated quotes",
|
|
},
|
|
{
|
|
name: "missing closing bracket",
|
|
in: "glob(`foo",
|
|
wantErr: "unterminated brackets",
|
|
},
|
|
{
|
|
name: "invalid matcher type",
|
|
in: "invalid(`foo`)",
|
|
wantErr: "invalid matcher type: invalid",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
_, _, err := ExtractExpr(tt.in)
|
|
expect.HasError(t, err)
|
|
expect.ErrorContains(t, err, tt.wantErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNegated(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
expr string
|
|
in string
|
|
want bool
|
|
}{
|
|
{
|
|
name: "negated_string_match",
|
|
expr: "!string(`foo`)",
|
|
in: "foo",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "negated_string_no_match",
|
|
expr: "!string(`foo`)",
|
|
in: "bar",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "negated_glob_match",
|
|
expr: "!glob(`foo`)",
|
|
in: "foo",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "negated_glob_no_match",
|
|
expr: "!glob(`foo`)",
|
|
in: "bar",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "negated_regex_match",
|
|
expr: "!regex(`^(_next/static|_next/image|favicon.ico).*$`)",
|
|
in: "favicon.ico",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "negated_regex_no_match",
|
|
expr: "!regex(`^(_next/static|_next/image|favicon.ico).*$`)",
|
|
in: "bar",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "negated_regex_no_match2",
|
|
expr: "!regex(`^(_next/static|_next/image|favicon.ico).*$`)",
|
|
in: "/",
|
|
want: true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
matcher, err := ParseMatcher(tt.expr)
|
|
expect.NoError(t, err)
|
|
expect.Equal(t, tt.want, matcher(tt.in))
|
|
})
|
|
}
|
|
}
|