252 lines
6.6 KiB
Go
252 lines
6.6 KiB
Go
package lib
|
|
|
|
import (
|
|
"testing"
|
|
|
|
configstoreTables "github.com/maximhq/bifrost/framework/configstore/tables"
|
|
)
|
|
|
|
func TestHeaderMatchesPattern(t *testing.T) {
|
|
tests := []struct {
|
|
pattern string
|
|
headerName string
|
|
want bool
|
|
}{
|
|
// Exact match
|
|
{"anthropic-beta", "anthropic-beta", true},
|
|
{"anthropic-beta", "anthropic-alpha", false},
|
|
|
|
// Case insensitive exact match
|
|
{"Anthropic-Beta", "anthropic-beta", true},
|
|
{"anthropic-beta", "Anthropic-Beta", true},
|
|
|
|
// Star matches all
|
|
{"*", "anything", true},
|
|
{"*", "", true},
|
|
|
|
// Prefix wildcard
|
|
{"anthropic-*", "anthropic-beta", true},
|
|
{"anthropic-*", "anthropic-version", true},
|
|
{"anthropic-*", "anthropic-", true},
|
|
{"anthropic-*", "openai-version", false},
|
|
{"anthropic-*", "anthropic", false},
|
|
|
|
// Case insensitive prefix wildcard
|
|
{"Anthropic-*", "anthropic-beta", true},
|
|
{"anthropic-*", "Anthropic-Beta", true},
|
|
|
|
// No match
|
|
{"foo", "bar", false},
|
|
{"", "foo", false},
|
|
|
|
// Pattern without wildcard doesn't prefix match
|
|
{"anthropic-", "anthropic-beta", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.pattern+"_"+tt.headerName, func(t *testing.T) {
|
|
got := HeaderMatchesPattern(tt.pattern, tt.headerName)
|
|
if got != tt.want {
|
|
t.Errorf("HeaderMatchesPattern(%q, %q) = %v, want %v", tt.pattern, tt.headerName, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewHeaderMatcher_Nil(t *testing.T) {
|
|
m := NewHeaderMatcher(nil)
|
|
if m != nil {
|
|
t.Fatal("expected nil matcher for nil config")
|
|
}
|
|
// nil matcher should allow everything
|
|
if !m.ShouldAllow("anything") {
|
|
t.Error("nil matcher should allow all headers")
|
|
}
|
|
if m.HasAllowlist() {
|
|
t.Error("nil matcher should have no allowlist")
|
|
}
|
|
}
|
|
|
|
func TestNewHeaderMatcher_Empty(t *testing.T) {
|
|
m := NewHeaderMatcher(&configstoreTables.GlobalHeaderFilterConfig{})
|
|
if m == nil {
|
|
t.Fatal("expected non-nil matcher for empty config")
|
|
}
|
|
if m.HasAllowlist() {
|
|
t.Error("empty config should have no allowlist")
|
|
}
|
|
if !m.ShouldAllow("anything") {
|
|
t.Error("empty config should allow all headers")
|
|
}
|
|
}
|
|
|
|
func TestHeaderMatcher_ExactAllowlist(t *testing.T) {
|
|
m := NewHeaderMatcher(&configstoreTables.GlobalHeaderFilterConfig{
|
|
Allowlist: []string{"anthropic-beta", "custom-id"},
|
|
})
|
|
if !m.ShouldAllow("anthropic-beta") {
|
|
t.Error("should allow anthropic-beta")
|
|
}
|
|
if !m.ShouldAllow("custom-id") {
|
|
t.Error("should allow custom-id")
|
|
}
|
|
if m.ShouldAllow("openai-version") {
|
|
t.Error("should not allow openai-version")
|
|
}
|
|
// Case insensitive
|
|
if !m.ShouldAllow("Anthropic-Beta") {
|
|
t.Error("should allow Anthropic-Beta (case insensitive)")
|
|
}
|
|
}
|
|
|
|
func TestHeaderMatcher_WildcardAllowlist(t *testing.T) {
|
|
m := NewHeaderMatcher(&configstoreTables.GlobalHeaderFilterConfig{
|
|
Allowlist: []string{"anthropic-*"},
|
|
})
|
|
if !m.ShouldAllow("anthropic-beta") {
|
|
t.Error("should allow anthropic-beta")
|
|
}
|
|
if !m.ShouldAllow("anthropic-version") {
|
|
t.Error("should allow anthropic-version")
|
|
}
|
|
if m.ShouldAllow("openai-version") {
|
|
t.Error("should not allow openai-version")
|
|
}
|
|
}
|
|
|
|
func TestHeaderMatcher_StarAllowlist(t *testing.T) {
|
|
m := NewHeaderMatcher(&configstoreTables.GlobalHeaderFilterConfig{
|
|
Allowlist: []string{"*"},
|
|
})
|
|
if !m.ShouldAllow("anything") {
|
|
t.Error("* should allow anything")
|
|
}
|
|
if !m.ShouldAllow("") {
|
|
t.Error("* should allow empty string")
|
|
}
|
|
}
|
|
|
|
func TestHeaderMatcher_ExactDenylist(t *testing.T) {
|
|
m := NewHeaderMatcher(&configstoreTables.GlobalHeaderFilterConfig{
|
|
Denylist: []string{"secret-token"},
|
|
})
|
|
if m.ShouldAllow("secret-token") {
|
|
t.Error("should deny secret-token")
|
|
}
|
|
if !m.ShouldAllow("public-key") {
|
|
t.Error("should allow public-key")
|
|
}
|
|
}
|
|
|
|
func TestHeaderMatcher_WildcardDenylist(t *testing.T) {
|
|
m := NewHeaderMatcher(&configstoreTables.GlobalHeaderFilterConfig{
|
|
Denylist: []string{"x-internal-*"},
|
|
})
|
|
if m.ShouldAllow("x-internal-id") {
|
|
t.Error("should deny x-internal-id")
|
|
}
|
|
if m.ShouldAllow("x-internal-secret") {
|
|
t.Error("should deny x-internal-secret")
|
|
}
|
|
if !m.ShouldAllow("x-external-id") {
|
|
t.Error("should allow x-external-id")
|
|
}
|
|
}
|
|
|
|
func TestHeaderMatcher_StarDenylist(t *testing.T) {
|
|
m := NewHeaderMatcher(&configstoreTables.GlobalHeaderFilterConfig{
|
|
Denylist: []string{"*"},
|
|
})
|
|
if m.ShouldAllow("anything") {
|
|
t.Error("* denylist should deny everything")
|
|
}
|
|
}
|
|
|
|
func TestHeaderMatcher_AllowlistWithDenylist(t *testing.T) {
|
|
m := NewHeaderMatcher(&configstoreTables.GlobalHeaderFilterConfig{
|
|
Allowlist: []string{"*"},
|
|
Denylist: []string{"x-internal-*"},
|
|
})
|
|
if !m.ShouldAllow("anthropic-beta") {
|
|
t.Error("should allow anthropic-beta")
|
|
}
|
|
if m.ShouldAllow("x-internal-id") {
|
|
t.Error("should deny x-internal-id (denylist overrides)")
|
|
}
|
|
}
|
|
|
|
func TestHeaderMatcher_AllowlistPrefixWithDenylistExact(t *testing.T) {
|
|
m := NewHeaderMatcher(&configstoreTables.GlobalHeaderFilterConfig{
|
|
Allowlist: []string{"anthropic-*"},
|
|
Denylist: []string{"anthropic-dangerous"},
|
|
})
|
|
if !m.ShouldAllow("anthropic-beta") {
|
|
t.Error("should allow anthropic-beta")
|
|
}
|
|
if m.ShouldAllow("anthropic-dangerous") {
|
|
t.Error("should deny anthropic-dangerous")
|
|
}
|
|
if m.ShouldAllow("openai-version") {
|
|
t.Error("should not allow openai-version (not in allowlist)")
|
|
}
|
|
}
|
|
|
|
func TestHeaderMatcher_CaseInsensitive(t *testing.T) {
|
|
m := NewHeaderMatcher(&configstoreTables.GlobalHeaderFilterConfig{
|
|
Allowlist: []string{"Anthropic-*"},
|
|
Denylist: []string{"X-Internal-*"},
|
|
})
|
|
if !m.ShouldAllow("anthropic-beta") {
|
|
t.Error("should allow anthropic-beta (case insensitive)")
|
|
}
|
|
if m.ShouldAllow("x-internal-id") {
|
|
t.Error("should deny x-internal-id (case insensitive)")
|
|
}
|
|
}
|
|
|
|
func TestHeaderMatcher_MatchesAllow(t *testing.T) {
|
|
m := NewHeaderMatcher(&configstoreTables.GlobalHeaderFilterConfig{
|
|
Allowlist: []string{"anthropic-*", "custom-id"},
|
|
})
|
|
if !m.MatchesAllow("anthropic-beta") {
|
|
t.Error("should match anthropic-beta")
|
|
}
|
|
if !m.MatchesAllow("custom-id") {
|
|
t.Error("should match custom-id")
|
|
}
|
|
if m.MatchesAllow("openai-version") {
|
|
t.Error("should not match openai-version")
|
|
}
|
|
}
|
|
|
|
func TestHeaderMatcher_MatchesDeny(t *testing.T) {
|
|
m := NewHeaderMatcher(&configstoreTables.GlobalHeaderFilterConfig{
|
|
Denylist: []string{"secret-*", "blocked"},
|
|
})
|
|
if !m.MatchesDeny("secret-token") {
|
|
t.Error("should match secret-token")
|
|
}
|
|
if !m.MatchesDeny("blocked") {
|
|
t.Error("should match blocked")
|
|
}
|
|
if m.MatchesDeny("allowed") {
|
|
t.Error("should not match allowed")
|
|
}
|
|
}
|
|
|
|
func TestHeaderMatcher_HasAllowlist(t *testing.T) {
|
|
m := NewHeaderMatcher(&configstoreTables.GlobalHeaderFilterConfig{
|
|
Allowlist: []string{"foo"},
|
|
})
|
|
if !m.HasAllowlist() {
|
|
t.Error("should have allowlist")
|
|
}
|
|
|
|
m2 := NewHeaderMatcher(&configstoreTables.GlobalHeaderFilterConfig{
|
|
Denylist: []string{"bar"},
|
|
})
|
|
if m2.HasAllowlist() {
|
|
t.Error("should not have allowlist")
|
|
}
|
|
}
|