mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-02-03 22:18:39 +00:00
Remove StopOnFirstMatch field that was defined but never used in the actual matching logic. The current implementation always evaluates all rule types and applies a fixed precedence (Domain > MAC > Network), making the StopOnFirstMatch field unnecessary. Changes: - Remove StopOnFirstMatch from MatchingConfig structs - Update DefaultMatchingConfig() function - Update all test cases and references - Simplify configuration to only include Order field This cleanup removes dead code and simplifies the configuration API without changing any functional behavior.
217 lines
6.1 KiB
Go
217 lines
6.1 KiB
Go
package rulematcher
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/Control-D-Inc/ctrld/testhelper"
|
|
)
|
|
|
|
func TestMatchingEngine(t *testing.T) {
|
|
cfg := testhelper.SampleConfig(t)
|
|
// Convert Cidrs to IPNets like in the original test
|
|
for _, nc := range cfg.Network {
|
|
for _, cidr := range nc.Cidrs {
|
|
_, ipNet, err := net.ParseCIDR(cidr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
nc.IPNets = append(nc.IPNets, ipNet)
|
|
}
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
config *MatchingConfig
|
|
request *MatchRequest
|
|
expected *MatchingResult
|
|
}{
|
|
{
|
|
name: "Default config - network match first",
|
|
config: DefaultMatchingConfig(),
|
|
request: &MatchRequest{
|
|
SourceIP: net.ParseIP("192.168.0.1"),
|
|
SourceMac: "14:45:A0:67:83:0A",
|
|
Domain: "example.ru",
|
|
Policy: cfg.Listener["0"].Policy,
|
|
Config: cfg,
|
|
},
|
|
expected: &MatchingResult{
|
|
Upstreams: []string{"upstream.1"},
|
|
MatchedPolicy: "My Policy",
|
|
MatchedNetwork: "network.0 (unenforced)",
|
|
MatchedRule: "*.ru",
|
|
Matched: true,
|
|
SrcAddr: "192.168.0.1",
|
|
MatchedRuleType: "domain",
|
|
MatchingOrder: []RuleType{RuleTypeNetwork, RuleTypeMac, RuleTypeDomain},
|
|
},
|
|
},
|
|
{
|
|
name: "Custom order - domain first",
|
|
config: &MatchingConfig{
|
|
Order: []RuleType{RuleTypeDomain, RuleTypeNetwork, RuleTypeMac},
|
|
},
|
|
request: &MatchRequest{
|
|
SourceIP: net.ParseIP("192.168.0.1"),
|
|
SourceMac: "14:45:A0:67:83:0A",
|
|
Domain: "example.ru",
|
|
Policy: cfg.Listener["0"].Policy,
|
|
Config: cfg,
|
|
},
|
|
expected: &MatchingResult{
|
|
Upstreams: []string{"upstream.1"},
|
|
MatchedPolicy: "My Policy",
|
|
MatchedNetwork: "network.0 (unenforced)",
|
|
MatchedRule: "*.ru",
|
|
Matched: true,
|
|
SrcAddr: "192.168.0.1",
|
|
MatchedRuleType: "domain",
|
|
MatchingOrder: []RuleType{RuleTypeDomain, RuleTypeNetwork, RuleTypeMac},
|
|
},
|
|
},
|
|
{
|
|
name: "Custom order - MAC first",
|
|
config: &MatchingConfig{
|
|
Order: []RuleType{RuleTypeMac, RuleTypeNetwork, RuleTypeDomain},
|
|
},
|
|
request: &MatchRequest{
|
|
SourceIP: net.ParseIP("192.168.0.1"),
|
|
SourceMac: "14:45:A0:67:83:0A",
|
|
Domain: "example.ru",
|
|
Policy: cfg.Listener["0"].Policy,
|
|
Config: cfg,
|
|
},
|
|
expected: &MatchingResult{
|
|
Upstreams: []string{"upstream.1"},
|
|
MatchedPolicy: "My Policy",
|
|
MatchedNetwork: "network.0 (unenforced)",
|
|
MatchedRule: "*.ru",
|
|
Matched: true,
|
|
SrcAddr: "192.168.0.1",
|
|
MatchedRuleType: "domain",
|
|
MatchingOrder: []RuleType{RuleTypeMac, RuleTypeNetwork, RuleTypeDomain},
|
|
},
|
|
},
|
|
{
|
|
name: "No policy",
|
|
config: DefaultMatchingConfig(),
|
|
request: &MatchRequest{
|
|
SourceIP: net.ParseIP("192.168.0.1"),
|
|
SourceMac: "14:45:A0:67:83:0A",
|
|
Domain: "example.ru",
|
|
Policy: nil,
|
|
Config: cfg,
|
|
},
|
|
expected: &MatchingResult{
|
|
Upstreams: []string{},
|
|
MatchedPolicy: "no policy",
|
|
MatchedNetwork: "no network",
|
|
MatchedRule: "no rule",
|
|
Matched: false,
|
|
SrcAddr: "192.168.0.1",
|
|
MatchedRuleType: "",
|
|
MatchingOrder: []RuleType{RuleTypeNetwork, RuleTypeMac, RuleTypeDomain},
|
|
},
|
|
},
|
|
{
|
|
name: "No matches",
|
|
config: DefaultMatchingConfig(),
|
|
request: &MatchRequest{
|
|
SourceIP: net.ParseIP("10.0.0.1"),
|
|
SourceMac: "00:11:22:33:44:55",
|
|
Domain: "example.com",
|
|
Policy: cfg.Listener["0"].Policy,
|
|
Config: cfg,
|
|
},
|
|
expected: &MatchingResult{
|
|
Upstreams: []string{},
|
|
MatchedPolicy: "My Policy",
|
|
MatchedNetwork: "no network",
|
|
MatchedRule: "no rule",
|
|
Matched: false,
|
|
SrcAddr: "10.0.0.1",
|
|
MatchedRuleType: "",
|
|
MatchingOrder: []RuleType{RuleTypeNetwork, RuleTypeMac, RuleTypeDomain},
|
|
},
|
|
},
|
|
{
|
|
name: "MAC rule overrides network rule",
|
|
config: DefaultMatchingConfig(),
|
|
request: &MatchRequest{
|
|
SourceIP: net.ParseIP("192.168.0.1"),
|
|
SourceMac: "14:45:A0:67:83:0A",
|
|
Domain: "example.com", // This domain doesn't match any domain rules
|
|
Policy: cfg.Listener["0"].Policy,
|
|
Config: cfg,
|
|
},
|
|
expected: &MatchingResult{
|
|
Upstreams: []string{"upstream.2"},
|
|
MatchedPolicy: "My Policy",
|
|
MatchedNetwork: "14:45:a0:67:83:0a",
|
|
MatchedRule: "no rule",
|
|
Matched: true,
|
|
SrcAddr: "192.168.0.1",
|
|
MatchedRuleType: "mac",
|
|
MatchingOrder: []RuleType{RuleTypeNetwork, RuleTypeMac, RuleTypeDomain},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
engine := NewMatchingEngine(tc.config)
|
|
result := engine.FindUpstreams(context.Background(), tc.request)
|
|
|
|
assert.Equal(t, tc.expected.Upstreams, result.Upstreams)
|
|
assert.Equal(t, tc.expected.MatchedPolicy, result.MatchedPolicy)
|
|
assert.Equal(t, tc.expected.MatchedNetwork, result.MatchedNetwork)
|
|
assert.Equal(t, tc.expected.MatchedRule, result.MatchedRule)
|
|
assert.Equal(t, tc.expected.Matched, result.Matched)
|
|
assert.Equal(t, tc.expected.SrcAddr, result.SrcAddr)
|
|
assert.Equal(t, tc.expected.MatchedRuleType, result.MatchedRuleType)
|
|
assert.Equal(t, tc.expected.MatchingOrder, result.MatchingOrder)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDefaultMatchingConfig(t *testing.T) {
|
|
config := DefaultMatchingConfig()
|
|
|
|
assert.Equal(t, []RuleType{RuleTypeNetwork, RuleTypeMac, RuleTypeDomain}, config.Order)
|
|
}
|
|
|
|
func TestMatchingEngineWithInvalidRuleType(t *testing.T) {
|
|
cfg := testhelper.SampleConfig(t)
|
|
// Convert Cidrs to IPNets like in the original test
|
|
for _, nc := range cfg.Network {
|
|
for _, cidr := range nc.Cidrs {
|
|
_, ipNet, err := net.ParseCIDR(cidr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
nc.IPNets = append(nc.IPNets, ipNet)
|
|
}
|
|
}
|
|
|
|
config := &MatchingConfig{
|
|
Order: []RuleType{RuleType("invalid"), RuleTypeNetwork},
|
|
}
|
|
|
|
engine := NewMatchingEngine(config)
|
|
request := &MatchRequest{
|
|
SourceIP: net.ParseIP("192.168.0.1"),
|
|
Policy: cfg.Listener["0"].Policy,
|
|
Config: cfg,
|
|
}
|
|
|
|
result := engine.FindUpstreams(context.Background(), request)
|
|
|
|
// Should still work, just skip the invalid rule type
|
|
assert.True(t, result.Matched)
|
|
assert.Equal(t, "network", result.MatchedRuleType)
|
|
}
|