package bypass import ( "context" "io" "strings" "testing" "time" "github.com/go-gost/core/bypass" "github.com/go-gost/core/logger" "github.com/go-gost/x/internal/loader" xlogger "github.com/go-gost/x/logger" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // --- Option tests --- func TestWhitelistOption(t *testing.T) { var opts options WhitelistOption(true)(&opts) assert.True(t, opts.whitelist) WhitelistOption(false)(&opts) assert.False(t, opts.whitelist) } func TestMatchersOption(t *testing.T) { var opts options MatchersOption([]string{"192.168.1.1", "10.0.0.0/8"})(&opts) assert.Equal(t, []string{"192.168.1.1", "10.0.0.0/8"}, opts.matchers) } func TestReloadPeriodOption(t *testing.T) { var opts options ReloadPeriodOption(5 * time.Second)(&opts) assert.Equal(t, 5*time.Second, opts.period) } func TestFileLoaderOption(t *testing.T) { var opts options fl := &mockLoader{} FileLoaderOption(fl)(&opts) assert.Equal(t, fl, opts.fileLoader) } func TestRedisLoaderOption(t *testing.T) { var opts options rl := &mockLoader{} RedisLoaderOption(rl)(&opts) assert.Equal(t, rl, opts.redisLoader) } func TestHTTPLoaderOption(t *testing.T) { var opts options hl := &mockLoader{} HTTPLoaderOption(hl)(&opts) assert.Equal(t, hl, opts.httpLoader) } func TestLoggerOption(t *testing.T) { var opts options l := xlogger.Nop() LoggerOption(l)(&opts) assert.Equal(t, l, opts.logger) } // newSyncedBypass creates a bypass and blocks until the initial background // reload completes, so matchers are ready for immediate assertions. func newSyncedBypass(opts ...Option) *localBypass { b := NewBypass(opts...) lb := b.(*localBypass) lb.reload(context.Background()) return lb } // --- NewBypass tests --- func TestNewBypass_Defaults(t *testing.T) { b := newSyncedBypass() require.NotNil(t, b) defer b.Close() // Blacklist mode with no rules: nothing matches, so traffic goes through proxy assert.False(t, b.Contains(context.Background(), "tcp", "192.168.1.1")) } func TestNewBypass_WithLogger(t *testing.T) { b := newSyncedBypass(LoggerOption(xlogger.Nop())) require.NotNil(t, b) defer b.Close() } // --- Contains tests --- func TestContains_EmptyAddr(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"192.168.1.1"})) defer b.Close() assert.False(t, b.Contains(context.Background(), "tcp", "")) } func TestContains_NilReceiver(t *testing.T) { var lb *localBypass assert.False(t, lb.Contains(context.Background(), "tcp", "192.168.1.1")) } func TestContains_Blacklist_Matched(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"192.168.1.1"})) defer b.Close() assert.True(t, b.Contains(context.Background(), "tcp", "192.168.1.1")) } func TestContains_Blacklist_NotMatched(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"192.168.1.1"})) defer b.Close() assert.False(t, b.Contains(context.Background(), "tcp", "10.0.0.1")) } func TestContains_Whitelist_Matched(t *testing.T) { b := newSyncedBypass( WhitelistOption(true), MatchersOption([]string{"192.168.1.1"}), ) defer b.Close() assert.False(t, b.Contains(context.Background(), "tcp", "192.168.1.1")) } func TestContains_Whitelist_NotMatched(t *testing.T) { b := newSyncedBypass( WhitelistOption(true), MatchersOption([]string{"192.168.1.1"}), ) defer b.Close() assert.True(t, b.Contains(context.Background(), "tcp", "10.0.0.1")) } func TestContains_StripsPort(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"192.168.1.1"})) defer b.Close() assert.True(t, b.Contains(context.Background(), "tcp", "192.168.1.1:8080")) } func TestContains_CIDRMatch(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"10.0.0.0/8"})) defer b.Close() assert.True(t, b.Contains(context.Background(), "tcp", "10.0.0.1")) assert.False(t, b.Contains(context.Background(), "tcp", "192.168.1.1")) } func TestContains_WildcardMatch(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"*.example.com"})) defer b.Close() assert.True(t, b.Contains(context.Background(), "tcp", "foo.example.com")) assert.False(t, b.Contains(context.Background(), "tcp", "foo.other.com")) } func TestContains_IPRangeMatch(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"192.168.1.1-192.168.1.10"})) defer b.Close() assert.True(t, b.Contains(context.Background(), "tcp", "192.168.1.5")) assert.False(t, b.Contains(context.Background(), "tcp", "192.168.1.100")) } func TestContains_InvalidAddr(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"192.168.1.1"})) defer b.Close() // Invalid address should not match, return false in blacklist mode assert.False(t, b.Contains(context.Background(), "tcp", "invalid")) } func TestContains_CompositePatterns(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"192.168.1.1", "10.0.0.0/8", "*.example.com", "172.16.0.1-172.16.0.255"})) defer b.Close() assert.True(t, b.Contains(context.Background(), "tcp", "192.168.1.1")) assert.True(t, b.Contains(context.Background(), "tcp", "10.0.0.1")) assert.True(t, b.Contains(context.Background(), "tcp", "foo.example.com")) assert.True(t, b.Contains(context.Background(), "tcp", "172.16.0.50")) assert.False(t, b.Contains(context.Background(), "tcp", "8.8.8.8")) } func TestContains_IPv6(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"fd00::1"})) defer b.Close() assert.True(t, b.Contains(context.Background(), "tcp", "fd00::1")) assert.False(t, b.Contains(context.Background(), "tcp", "fd00::2")) } func TestContains_IPv6WithPort(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"::1"})) defer b.Close() // net.SplitHostPort handles [::1]:8080 format assert.True(t, b.Contains(context.Background(), "tcp", "[::1]:8080")) } func TestContains_WildcardWithPort(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"*.example.com:8080"})) defer b.Close() assert.True(t, b.Contains(context.Background(), "tcp", "foo.example.com:8080")) assert.False(t, b.Contains(context.Background(), "tcp", "foo.example.com:9090")) } // --- IsWhitelist tests --- func TestIsWhitelist_Blacklist(t *testing.T) { b := newSyncedBypass() defer b.Close() assert.False(t, b.IsWhitelist()) } func TestIsWhitelist_Whitelist(t *testing.T) { b := newSyncedBypass(WhitelistOption(true)) defer b.Close() assert.True(t, b.IsWhitelist()) } // --- parseLine tests --- func TestParseLine_Normal(t *testing.T) { lb := &localBypass{} assert.Equal(t, "192.168.1.1", lb.parseLine("192.168.1.1")) } func TestParseLine_WithComment(t *testing.T) { lb := &localBypass{} assert.Equal(t, "192.168.1.1", lb.parseLine("192.168.1.1 # a comment")) } func TestParseLine_CommentOnly(t *testing.T) { lb := &localBypass{} assert.Equal(t, "", lb.parseLine("# comment only")) } func TestParseLine_Empty(t *testing.T) { lb := &localBypass{} assert.Equal(t, "", lb.parseLine("")) } func TestParseLine_Whitespace(t *testing.T) { lb := &localBypass{} assert.Equal(t, "192.168.1.1", lb.parseLine(" 192.168.1.1 ")) } // --- parsePatterns tests --- func TestParsePatterns_NilReader(t *testing.T) { lb := &localBypass{} patterns, err := lb.parsePatterns(nil) assert.NoError(t, err) assert.Nil(t, patterns) } func TestParsePatterns_Normal(t *testing.T) { lb := &localBypass{} r := strings.NewReader("192.168.1.1\n10.0.0.1\n# comment\n\n fd00::1 \n") patterns, err := lb.parsePatterns(r) assert.NoError(t, err) assert.Equal(t, []string{"192.168.1.1", "10.0.0.1", "fd00::1"}, patterns) } func TestParsePatterns_EdgeCases(t *testing.T) { lb := &localBypass{} r := strings.NewReader("# full comment\n\n # indented comment\n192.168.1.1 # inline") patterns, err := lb.parsePatterns(r) assert.NoError(t, err) assert.Equal(t, []string{"192.168.1.1"}, patterns) } // --- matched tests (via decide) --- func TestMatched_IP(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"192.168.1.1"})) defer b.Close() assert.Equal(t, decisionBypass, b.decide("192.168.1.1")) assert.Equal(t, decisionProxy, b.decide("10.0.0.1")) } func TestMatched_CIDR(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"10.0.0.0/8"})) defer b.Close() assert.Equal(t, decisionBypass, b.decide("10.0.0.1")) assert.Equal(t, decisionProxy, b.decide("192.168.1.1")) } func TestMatched_Wildcard(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"*.example.com"})) defer b.Close() assert.Equal(t, decisionBypass, b.decide("foo.example.com")) assert.Equal(t, decisionProxy, b.decide("foo.other.com")) } func TestMatched_IPRange(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"10.0.0.1-10.0.0.100"})) defer b.Close() assert.Equal(t, decisionBypass, b.decide("10.0.0.50")) assert.Equal(t, decisionProxy, b.decide("10.0.0.200")) } // --- reload tests --- func TestReload_OnlyIPs(t *testing.T) { lb := &localBypass{ options: options{ matchers: []string{"1.1.1.1", "8.8.8.8"}, }, logger: xlogger.Nop(), } err := lb.reload(context.Background()) assert.NoError(t, err) assert.True(t, lb.patterns.matchAny("1.1.1.1")) assert.True(t, lb.patterns.matchAny("8.8.8.8")) assert.False(t, lb.patterns.matchAny("9.9.9.9")) } func TestReload_OnlyCIDRs(t *testing.T) { lb := &localBypass{ options: options{ matchers: []string{"192.168.0.0/16", "10.0.0.0/8"}, }, logger: xlogger.Nop(), } err := lb.reload(context.Background()) assert.NoError(t, err) assert.True(t, lb.patterns.matchAny("192.168.1.1")) assert.True(t, lb.patterns.matchAny("10.0.0.1")) assert.False(t, lb.patterns.matchAny("172.16.0.1")) } func TestReload_OnlyWildcards(t *testing.T) { lb := &localBypass{ options: options{ matchers: []string{"*.example.com", "*.test.org"}, }, logger: xlogger.Nop(), } err := lb.reload(context.Background()) assert.NoError(t, err) assert.True(t, lb.patterns.matchAny("foo.example.com")) assert.True(t, lb.patterns.matchAny("bar.test.org")) assert.False(t, lb.patterns.matchAny("other.com")) } func TestReload_OnlyIPRanges(t *testing.T) { lb := &localBypass{ options: options{ matchers: []string{"192.168.1.1-192.168.1.50"}, }, logger: xlogger.Nop(), } err := lb.reload(context.Background()) assert.NoError(t, err) assert.True(t, lb.patterns.matchAny("192.168.1.25")) assert.False(t, lb.patterns.matchAny("192.168.1.100")) } // --- load tests --- func TestLoad_AllNilLoaders(t *testing.T) { lb := &localBypass{ logger: xlogger.Nop(), } patterns, err := lb.load(context.Background()) assert.NoError(t, err) assert.Nil(t, patterns) } func TestLoad_FileLoaderWithList(t *testing.T) { lb := &localBypass{ options: options{ fileLoader: &mockListerLoader{list: []string{"192.168.1.1", "# comment", "10.0.0.1"}}, }, logger: xlogger.Nop(), } patterns, err := lb.load(context.Background()) assert.NoError(t, err) assert.Equal(t, []string{"192.168.1.1", "10.0.0.1"}, patterns) } func TestLoad_FileLoaderWithLoad(t *testing.T) { lb := &localBypass{ options: options{ fileLoader: &mockLoader{data: "192.168.1.1\n10.0.0.1\n"}, }, logger: xlogger.Nop(), } patterns, err := lb.load(context.Background()) assert.NoError(t, err) assert.Equal(t, []string{"192.168.1.1", "10.0.0.1"}, patterns) } func TestLoad_RedisLoaderWithList(t *testing.T) { lb := &localBypass{ options: options{ redisLoader: &mockListerLoader{list: []string{"redis-host-1", "redis-host-2"}}, }, logger: xlogger.Nop(), } patterns, err := lb.load(context.Background()) assert.NoError(t, err) assert.Equal(t, []string{"redis-host-1", "redis-host-2"}, patterns) } func TestLoad_RedisLoaderWithLoad(t *testing.T) { lb := &localBypass{ options: options{ redisLoader: &mockLoader{data: "192.168.2.1\n"}, }, logger: xlogger.Nop(), } patterns, err := lb.load(context.Background()) assert.NoError(t, err) assert.Equal(t, []string{"192.168.2.1"}, patterns) } func TestLoad_HTTPLoader(t *testing.T) { lb := &localBypass{ options: options{ httpLoader: &mockLoader{data: "10.10.10.1\n10.10.10.2\n"}, }, logger: xlogger.Nop(), } patterns, err := lb.load(context.Background()) assert.NoError(t, err) assert.Equal(t, []string{"10.10.10.1", "10.10.10.2"}, patterns) } func TestLoad_FileLoaderWithListError(t *testing.T) { lb := &localBypass{ options: options{ fileLoader: &mockListerLoader{listErr: io.ErrUnexpectedEOF}, }, logger: xlogger.Nop(), } patterns, err := lb.load(context.Background()) assert.NoError(t, err) assert.Nil(t, patterns) } func TestLoad_FileLoaderWithLoadError(t *testing.T) { lb := &localBypass{ options: options{ fileLoader: &mockLoader{loadErr: io.ErrUnexpectedEOF}, }, logger: xlogger.Nop(), } patterns, err := lb.load(context.Background()) assert.NoError(t, err) assert.Nil(t, patterns) } func TestLoad_RedisLoaderWithListError(t *testing.T) { lb := &localBypass{ options: options{ redisLoader: &mockListerLoader{listErr: io.ErrUnexpectedEOF}, }, logger: xlogger.Nop(), } patterns, err := lb.load(context.Background()) assert.NoError(t, err) assert.Nil(t, patterns) } func TestLoad_RedisLoaderWithLoadError(t *testing.T) { lb := &localBypass{ options: options{ redisLoader: &mockLoader{loadErr: io.ErrUnexpectedEOF}, }, logger: xlogger.Nop(), } patterns, err := lb.load(context.Background()) assert.NoError(t, err) assert.Nil(t, patterns) } func TestLoad_HTTPLoaderError(t *testing.T) { lb := &localBypass{ options: options{ httpLoader: &mockLoader{loadErr: io.ErrUnexpectedEOF}, }, logger: xlogger.Nop(), } patterns, err := lb.load(context.Background()) assert.NoError(t, err) assert.Nil(t, patterns) } func TestLoad_FileLoaderWithListParsing(t *testing.T) { lb := &localBypass{ options: options{ fileLoader: &mockListerLoader{list: []string{" # comment", "", " 192.168.1.1 "}}, }, logger: xlogger.Nop(), } patterns, err := lb.load(context.Background()) assert.NoError(t, err) assert.Equal(t, []string{"192.168.1.1"}, patterns) } // --- periodReload tests --- func TestPeriodReload_ZeroPeriod(t *testing.T) { lb := &localBypass{ logger: xlogger.Nop(), options: options{period: 0}, } err := lb.periodReload(context.Background()) assert.NoError(t, err) } func TestPeriodReload_NegativePeriod(t *testing.T) { lb := &localBypass{ logger: xlogger.Nop(), options: options{period: -1}, } err := lb.periodReload(context.Background()) assert.NoError(t, err) } func TestPeriodReload_WithContextCancel(t *testing.T) { lb := &localBypass{ logger: xlogger.Nop(), options: options{period: 500 * time.Millisecond}, } ctx, cancel := context.WithCancel(context.Background()) errCh := make(chan error, 1) go func() { errCh <- lb.periodReload(ctx) }() time.Sleep(1100 * time.Millisecond) cancel() select { case err := <-errCh: assert.Equal(t, context.Canceled, err) case <-time.After(3 * time.Second): t.Fatal("periodReload did not return after context cancel") } } // --- Close tests --- func TestClose_NoLoaders(t *testing.T) { b := newSyncedBypass() err := b.Close() assert.NoError(t, err) } func TestClose_WithAllLoaders(t *testing.T) { fl := &mockLoader{} rl := &mockLoader{} hl := &mockLoader{} b := newSyncedBypass( FileLoaderOption(fl), RedisLoaderOption(rl), HTTPLoaderOption(hl), ) err := b.Close() assert.NoError(t, err) assert.True(t, fl.closed) assert.True(t, rl.closed) assert.True(t, hl.closed) } // --- BypassGroup tests --- func TestBypassGroup_Empty(t *testing.T) { g := BypassGroup() assert.False(t, g.Contains(context.Background(), "tcp", "192.168.1.1")) } func TestBypassGroup_AllBlacklistAllMatch(t *testing.T) { g := BypassGroup( alwaysContains{}, alwaysContains{}, ) assert.True(t, g.Contains(context.Background(), "tcp", "192.168.1.1")) } func TestBypassGroup_BlacklistOneMatches(t *testing.T) { g := BypassGroup( alwaysContains{}, neverContains{}, ) // OR logic for blacklist: any match triggers bypass assert.True(t, g.Contains(context.Background(), "tcp", "192.168.1.1")) } func TestBypassGroup_BlacklistNoneMatches(t *testing.T) { g := BypassGroup( neverContains{}, neverContains{}, ) assert.False(t, g.Contains(context.Background(), "tcp", "192.168.1.1")) } func TestBypassGroup_WhitelistAllMatch(t *testing.T) { // AND logic for whitelist: all must match g := BypassGroup( alwaysContainsWhitelist{}, alwaysContainsWhitelist{}, ) assert.True(t, g.Contains(context.Background(), "tcp", "192.168.1.1")) } func TestBypassGroup_WhitelistOneFails(t *testing.T) { // AND logic for whitelist: if any fails, combined result is false g := BypassGroup( alwaysContainsWhitelist{}, neverContainsWhitelist{}, ) assert.False(t, g.Contains(context.Background(), "tcp", "192.168.1.1")) } func TestBypassGroup_MixedWhitelsBlacklist(t *testing.T) { // Whitelist all match, blacklist irrelevant g := BypassGroup( alwaysContainsWhitelist{}, alwaysContains{}, // blacklist — should be ignored if whitelist all pass ) // All whitelists match → status=true → blacklist skipped assert.True(t, g.Contains(context.Background(), "tcp", "192.168.1.1")) } func TestBypassGroup_MixedWhitelistFailsBlacklistMatches(t *testing.T) { // Whitelist fails → fallthrough to blacklist which matches g := BypassGroup( neverContainsWhitelist{}, alwaysContains{}, ) assert.True(t, g.Contains(context.Background(), "tcp", "192.168.1.1")) } func TestBypassGroup_MixedWhitelistFailsBlacklistNoMatch(t *testing.T) { g := BypassGroup( neverContainsWhitelist{}, neverContains{}, ) assert.False(t, g.Contains(context.Background(), "tcp", "192.168.1.1")) } func TestBypassGroup_IsWhitelist(t *testing.T) { g := BypassGroup() assert.False(t, g.IsWhitelist()) } // --- NewBypass reload error in background --- func TestNewBypass_ReloadError(t *testing.T) { fl := &mockLoader{loadErr: io.ErrUnexpectedEOF} b := newSyncedBypass( FileLoaderOption(fl), LoggerOption(xlogger.Nop()), ) defer b.Close() // Should still work, just logged a warning assert.False(t, b.Contains(context.Background(), "tcp", "192.168.1.1")) } // --- Network parameter ignored --- func TestContains_NetworkIgnoredForMatching(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"192.168.1.1"})) defer b.Close() assert.True(t, b.Contains(context.Background(), "tcp", "192.168.1.1")) assert.True(t, b.Contains(context.Background(), "udp", "192.168.1.1")) assert.True(t, b.Contains(context.Background(), "ip4", "192.168.1.1")) } // --- Debug logging doesn't panic --- func TestContains_DenyLogs(t *testing.T) { b := newSyncedBypass( MatchersOption([]string{"192.168.1.1"}), LoggerOption(xlogger.Nop()), ) defer b.Close() assert.True(t, b.Contains(context.Background(), "tcp", "192.168.1.1")) } func TestContains_WhitelistDenyLogs(t *testing.T) { b := newSyncedBypass( WhitelistOption(true), MatchersOption([]string{"192.168.1.1"}), LoggerOption(xlogger.Nop()), ) defer b.Close() assert.True(t, b.Contains(context.Background(), "tcp", "10.0.0.1")) } // --- Mock types --- type mockLoader struct { data string loadErr error closed bool } func (m *mockLoader) Load(ctx context.Context) (io.Reader, error) { if m.loadErr != nil { return nil, m.loadErr } return strings.NewReader(m.data), nil } func (m *mockLoader) Close() error { m.closed = true return nil } type mockListerLoader struct { list []string listErr error } func (m *mockListerLoader) Load(ctx context.Context) (io.Reader, error) { return strings.NewReader(strings.Join(m.list, "\n")), nil } func (m *mockListerLoader) List(ctx context.Context) ([]string, error) { if m.listErr != nil { return nil, m.listErr } return m.list, nil } func (m *mockListerLoader) Close() error { return nil } type alwaysContains struct{} func (alwaysContains) Contains(ctx context.Context, network, addr string, opts ...bypass.Option) bool { return true } func (alwaysContains) IsWhitelist() bool { return false } type neverContains struct{} func (neverContains) Contains(ctx context.Context, network, addr string, opts ...bypass.Option) bool { return false } func (neverContains) IsWhitelist() bool { return false } type alwaysContainsWhitelist struct{} func (alwaysContainsWhitelist) Contains(ctx context.Context, network, addr string, opts ...bypass.Option) bool { return true } func (alwaysContainsWhitelist) IsWhitelist() bool { return true } type neverContainsWhitelist struct{} func (neverContainsWhitelist) Contains(ctx context.Context, network, addr string, opts ...bypass.Option) bool { return false } func (neverContainsWhitelist) IsWhitelist() bool { return true } // Ensure mock types implement the interfaces var _ loader.Loader = (*mockLoader)(nil) var _ loader.Lister = (*mockListerLoader)(nil) var _ loader.Loader = (*mockListerLoader)(nil) var _ bypass.Bypass = alwaysContains{} var _ bypass.Bypass = neverContains{} var _ bypass.Bypass = alwaysContainsWhitelist{} var _ bypass.Bypass = neverContainsWhitelist{} var _ bypass.Bypass = (*localBypass)(nil) var _ bypass.Bypass = (*bypassGroup)(nil) var _ logger.Logger = xlogger.Nop() // --- bypassDecision tests --- func TestBypassDecision_String(t *testing.T) { assert.Equal(t, "bypass", decisionBypass.String()) assert.Equal(t, "proxy", decisionProxy.String()) assert.Equal(t, "unknown", bypassDecision(99).String()) } // --- patternSet.matchAny tests --- func TestPatternSet_MatchAny_IPRange(t *testing.T) { ps := classifyPatterns([]string{"192.168.1.1-192.168.1.10"}, xlogger.Nop()) assert.True(t, ps.matchAny("192.168.1.5")) assert.False(t, ps.matchAny("192.168.1.100")) } func TestPatternSet_MatchAny_Addr(t *testing.T) { ps := classifyPatterns([]string{"example.com"}, xlogger.Nop()) assert.True(t, ps.matchAny("example.com")) assert.False(t, ps.matchAny("other.com")) } func TestPatternSet_MatchAny_CIDR(t *testing.T) { ps := classifyPatterns([]string{"10.0.0.0/8"}, xlogger.Nop()) assert.True(t, ps.matchAny("10.0.0.1")) assert.True(t, ps.matchAny("10.255.255.255")) assert.False(t, ps.matchAny("192.168.1.1")) } func TestPatternSet_MatchAny_Wildcard(t *testing.T) { ps := classifyPatterns([]string{"*.example.com"}, xlogger.Nop()) assert.True(t, ps.matchAny("foo.example.com")) assert.False(t, ps.matchAny("foo.other.com")) } func TestPatternSet_MatchAny_NilSet(t *testing.T) { var ps *patternSet assert.False(t, ps.matchAny("anything")) } func TestPatternSet_MatchAny_Empty(t *testing.T) { ps := classifyPatterns(nil, xlogger.Nop()) assert.False(t, ps.matchAny("anything")) } func TestPatternSet_MatchAny_MixedTypes(t *testing.T) { ps := classifyPatterns([]string{ "192.168.1.1", // address "10.0.0.0/8", // CIDR "*.example.com", // wildcard "172.16.0.1-172.16.0.255", // IP range }, xlogger.Nop()) assert.True(t, ps.matchAny("192.168.1.1")) assert.True(t, ps.matchAny("10.0.0.1")) assert.True(t, ps.matchAny("foo.example.com")) assert.True(t, ps.matchAny("172.16.0.50")) assert.False(t, ps.matchAny("8.8.8.8")) } func TestPatternSet_MatchAny_Precedence(t *testing.T) { // IP range matches first, before address ps := classifyPatterns([]string{"192.168.1.1-192.168.1.10", "192.168.1.1"}, xlogger.Nop()) assert.True(t, ps.matchAny("192.168.1.5")) } func TestPatternSet_MatchAny_CIDRWithPort(t *testing.T) { ps := classifyPatterns([]string{"10.0.0.0/8"}, xlogger.Nop()) assert.True(t, ps.matchAny("10.0.0.1:8080")) } func TestPatternSet_MatchAny_CIDRDomainNotMatched(t *testing.T) { ps := classifyPatterns([]string{"10.0.0.0/8"}, xlogger.Nop()) assert.False(t, ps.matchAny("example.com")) } // --- classifyPatterns tests --- func TestClassifyPatterns_CIDR(t *testing.T) { ps := classifyPatterns([]string{"192.168.0.0/16"}, xlogger.Nop()) require.NotNil(t, ps) assert.True(t, ps.matchAny("192.168.1.1")) } func TestClassifyPatterns_Wildcard(t *testing.T) { ps := classifyPatterns([]string{"*.test.com"}, xlogger.Nop()) require.NotNil(t, ps) assert.True(t, ps.matchAny("foo.test.com")) } func TestClassifyPatterns_IPRange(t *testing.T) { ps := classifyPatterns([]string{"10.0.0.1-10.0.0.100"}, xlogger.Nop()) require.NotNil(t, ps) assert.True(t, ps.matchAny("10.0.0.50")) } func TestClassifyPatterns_Address(t *testing.T) { ps := classifyPatterns([]string{"192.168.1.1"}, xlogger.Nop()) require.NotNil(t, ps) assert.True(t, ps.matchAny("192.168.1.1")) } func TestClassifyPatterns_InvalidWildcardFallsToAddress(t *testing.T) { // A pattern with * that can't compile as glob falls to address matcher. // "[invalid" has * but glob.Compile may fail — let's use a simpler case. ps := classifyPatterns([]string{"plain-host"}, xlogger.Nop()) require.NotNil(t, ps) assert.True(t, ps.matchAny("plain-host")) } func TestClassifyPatterns_Empty(t *testing.T) { ps := classifyPatterns(nil, xlogger.Nop()) require.NotNil(t, ps) assert.False(t, ps.matchAny("anything")) } // --- decide tests --- func TestDecide_BlacklistMatched(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"192.168.1.1"})) defer b.Close() assert.Equal(t, decisionBypass, b.decide("192.168.1.1")) } func TestDecide_BlacklistNotMatched(t *testing.T) { b := newSyncedBypass(MatchersOption([]string{"192.168.1.1"})) defer b.Close() assert.Equal(t, decisionProxy, b.decide("10.0.0.1")) } func TestDecide_WhitelistMatched(t *testing.T) { b := newSyncedBypass(WhitelistOption(true), MatchersOption([]string{"192.168.1.1"})) defer b.Close() assert.Equal(t, decisionProxy, b.decide("192.168.1.1")) } func TestDecide_WhitelistNotMatched(t *testing.T) { b := newSyncedBypass(WhitelistOption(true), MatchersOption([]string{"192.168.1.1"})) defer b.Close() assert.Equal(t, decisionBypass, b.decide("10.0.0.1")) } func TestDecide_NilPatterns(t *testing.T) { lb := &localBypass{logger: xlogger.Nop()} assert.Equal(t, decisionProxy, lb.decide("anything")) } // --- evaluate (bypassGroup) tests --- func TestEvaluate_AllBlacklistAllMatch(t *testing.T) { g := BypassGroup(alwaysContains{}, alwaysContains{}).(*bypassGroup) assert.Equal(t, decisionBypass, g.evaluate(context.Background(), "tcp", "any", )) } func TestEvaluate_BlacklistNoneMatch(t *testing.T) { g := BypassGroup(neverContains{}, neverContains{}).(*bypassGroup) assert.Equal(t, decisionProxy, g.evaluate(context.Background(), "tcp", "any")) } func TestEvaluate_WhitelistAllMatch(t *testing.T) { g := BypassGroup(alwaysContainsWhitelist{}, alwaysContainsWhitelist{}).(*bypassGroup) assert.Equal(t, decisionBypass, g.evaluate(context.Background(), "tcp", "any")) } func TestEvaluate_WhitelistOneFails(t *testing.T) { g := BypassGroup(alwaysContainsWhitelist{}, neverContainsWhitelist{}).(*bypassGroup) assert.Equal(t, decisionProxy, g.evaluate(context.Background(), "tcp", "any")) } func TestEvaluate_WhitelistFailsBlacklistMatches(t *testing.T) { g := BypassGroup(neverContainsWhitelist{}, alwaysContains{}).(*bypassGroup) assert.Equal(t, decisionBypass, g.evaluate(context.Background(), "tcp", "any")) } func TestEvaluate_WhitelistFailsBlacklistNoMatch(t *testing.T) { g := BypassGroup(neverContainsWhitelist{}, neverContains{}).(*bypassGroup) assert.Equal(t, decisionProxy, g.evaluate(context.Background(), "tcp", "any")) } func TestEvaluate_Empty(t *testing.T) { g := BypassGroup().(*bypassGroup) assert.Equal(t, decisionProxy, g.evaluate(context.Background(), "tcp", "any")) } // --- hasLoaders tests --- func TestHasLoaders_None(t *testing.T) { lb := &localBypass{options: options{}} assert.False(t, lb.hasLoaders()) } func TestHasLoaders_WithFile(t *testing.T) { lb := &localBypass{options: options{fileLoader: &mockLoader{}}} assert.True(t, lb.hasLoaders()) } func TestHasLoaders_WithPeriod(t *testing.T) { lb := &localBypass{options: options{period: time.Second}} assert.True(t, lb.hasLoaders()) }