mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-23 09:52:01 +01:00
The matcherSignature function now treats empty strings as unconditional rules that match any request, returning "(any)" as the signature instead of rejecting them. This enables proper dead code detection when an unconditional terminating rule shadows later rules. Adds test coverage for detecting dead rules caused by unconditional terminating rules.
205 lines
3.4 KiB
Go
205 lines
3.4 KiB
Go
package rules
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/yusing/godoxy/internal/serialization"
|
|
)
|
|
|
|
func TestRulesValidate(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
rules string
|
|
want error
|
|
}{
|
|
{
|
|
name: "no default rule",
|
|
rules: `
|
|
header Host example.com {
|
|
pass
|
|
}`,
|
|
},
|
|
{
|
|
name: "multiple default rules",
|
|
rules: `
|
|
default {
|
|
pass
|
|
}
|
|
|
|
default {
|
|
pass
|
|
}`,
|
|
want: ErrMultipleDefaultRules,
|
|
},
|
|
{
|
|
name: "multiple responses on same condition",
|
|
rules: `
|
|
header Host example.com {
|
|
error 404 "not found"
|
|
}
|
|
|
|
header Host example.com {
|
|
error 403 "forbidden"
|
|
}
|
|
`,
|
|
want: ErrDeadRule,
|
|
},
|
|
{
|
|
name: "same condition different formatting error then proxy",
|
|
rules: `
|
|
header Host example.com & method GET {
|
|
error 404 "not found"
|
|
}
|
|
|
|
method GET
|
|
header Host example.com {
|
|
proxy http://127.0.0.1:8080
|
|
}
|
|
`,
|
|
want: ErrDeadRule,
|
|
},
|
|
{
|
|
name: "same condition with non terminating first rule",
|
|
rules: `
|
|
header Host example.com {
|
|
set resp_header X-Test first
|
|
}
|
|
|
|
header Host example.com {
|
|
error 403 "forbidden"
|
|
}
|
|
`,
|
|
want: nil,
|
|
},
|
|
{
|
|
name: "same condition with terminating handler inside if block",
|
|
rules: `
|
|
header Host example.com {
|
|
default {
|
|
error 404 "not found"
|
|
}
|
|
}
|
|
|
|
header Host example.com {
|
|
error 403 "forbidden"
|
|
}
|
|
`,
|
|
want: ErrDeadRule,
|
|
},
|
|
{
|
|
name: "same condition with terminating handler across if else block",
|
|
rules: `
|
|
header Host example.com {
|
|
method GET {
|
|
error 404 "not found"
|
|
} else {
|
|
redirect https://example.com
|
|
}
|
|
}
|
|
|
|
header Host example.com {
|
|
error 403 "forbidden"
|
|
}
|
|
`,
|
|
want: ErrDeadRule,
|
|
},
|
|
{
|
|
name: "same condition with non terminating if branch in if else block",
|
|
rules: `
|
|
header Host example.com {
|
|
method GET {
|
|
set resp_header X-Test first
|
|
} else {
|
|
error 404 "not found"
|
|
}
|
|
}
|
|
|
|
header Host example.com {
|
|
error 403 "forbidden"
|
|
}
|
|
`,
|
|
want: nil,
|
|
},
|
|
{
|
|
name: "unconditional terminating rule shadows later unconditional rule",
|
|
rules: `
|
|
{
|
|
error 404 "not found"
|
|
}
|
|
|
|
{
|
|
error 403 "forbidden"
|
|
}
|
|
`,
|
|
want: ErrDeadRule,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var rules Rules
|
|
convertible, err := serialization.ConvertString(strings.TrimSpace(tt.rules), reflect.ValueOf(&rules))
|
|
require.True(t, convertible)
|
|
require.NoError(t, err)
|
|
|
|
err = rules.Validate()
|
|
|
|
if tt.want == nil {
|
|
assert.NoError(t, err)
|
|
return
|
|
}
|
|
assert.ErrorIs(t, err, tt.want)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHasTopLevelLBrace(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want bool
|
|
}{
|
|
{
|
|
name: "escaped quote inside double quoted string",
|
|
in: `"test\"more{"`,
|
|
want: false,
|
|
},
|
|
{
|
|
name: "escaped quote inside single quoted string",
|
|
in: "'test\\'more{'",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "top-level brace outside quoted string",
|
|
in: `"test\"more" {`,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "backtick keeps existing behavior",
|
|
in: "`test\\`more{`",
|
|
want: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
assert.Equal(t, tt.want, hasTopLevelLBrace(tt.in))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRulesParse_BlockTriedThenYAMLFails_ReturnsBlockError(t *testing.T) {
|
|
input := `default {`
|
|
|
|
_, blockErr := parseBlockRules(input)
|
|
require.Error(t, blockErr)
|
|
|
|
var rules Rules
|
|
err := rules.Parse(input)
|
|
require.Error(t, err)
|
|
assert.Equal(t, blockErr.Error(), err.Error())
|
|
}
|