728 lines
19 KiB
Go
728 lines
19 KiB
Go
package admission
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/go-gost/core/admission"
|
|
"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)
|
|
}
|
|
|
|
// newSyncedAdmission creates an admission controller and blocks until the initial
|
|
// background reload completes, so matchers are ready for immediate assertions.
|
|
func newSyncedAdmission(opts ...Option) *localAdmission {
|
|
a := NewAdmission(opts...)
|
|
la := a.(*localAdmission)
|
|
// Force a synchronous reload so tests don't race with the background goroutine.
|
|
la.reload(context.Background())
|
|
return la
|
|
}
|
|
|
|
// --- NewAdmission tests ---
|
|
|
|
func TestNewAdmission_Defaults(t *testing.T) {
|
|
adm := newSyncedAdmission()
|
|
require.NotNil(t, adm)
|
|
defer adm.Close()
|
|
|
|
// Should admit by default (blacklist mode with no rules)
|
|
assert.True(t, adm.Admit(context.Background(), "tcp", "192.168.1.1"))
|
|
}
|
|
|
|
func TestNewAdmission_WithLogger(t *testing.T) {
|
|
adm := newSyncedAdmission(LoggerOption(xlogger.Nop()))
|
|
require.NotNil(t, adm)
|
|
defer adm.Close()
|
|
}
|
|
|
|
// --- Admit tests ---
|
|
|
|
func TestAdmit_EmptyAddr(t *testing.T) {
|
|
adm := newSyncedAdmission(MatchersOption([]string{"192.168.1.1"}))
|
|
defer adm.Close()
|
|
|
|
assert.True(t, adm.Admit(context.Background(), "tcp", ""))
|
|
}
|
|
|
|
func TestAdmit_NilReceiver(t *testing.T) {
|
|
var p *localAdmission
|
|
assert.True(t, p.Admit(context.Background(), "tcp", "192.168.1.1"))
|
|
}
|
|
|
|
func TestAdmit_Blacklist_Matched(t *testing.T) {
|
|
adm := newSyncedAdmission(MatchersOption([]string{"192.168.1.1"}))
|
|
defer adm.Close()
|
|
|
|
// In blacklist mode, matching IP is denied
|
|
assert.False(t, adm.Admit(context.Background(), "tcp", "192.168.1.1"))
|
|
}
|
|
|
|
func TestAdmit_Blacklist_NotMatched(t *testing.T) {
|
|
adm := newSyncedAdmission(MatchersOption([]string{"192.168.1.1"}))
|
|
defer adm.Close()
|
|
|
|
// In blacklist mode, non-matching IP is admitted
|
|
assert.True(t, adm.Admit(context.Background(), "tcp", "10.0.0.1"))
|
|
}
|
|
|
|
func TestAdmit_Whitelist_Matched(t *testing.T) {
|
|
adm := newSyncedAdmission(
|
|
WhitelistOption(true),
|
|
MatchersOption([]string{"192.168.1.1"}),
|
|
)
|
|
defer adm.Close()
|
|
|
|
// In whitelist mode, matching IP is admitted
|
|
assert.True(t, adm.Admit(context.Background(), "tcp", "192.168.1.1"))
|
|
}
|
|
|
|
func TestAdmit_Whitelist_NotMatched(t *testing.T) {
|
|
adm := newSyncedAdmission(
|
|
WhitelistOption(true),
|
|
MatchersOption([]string{"192.168.1.1"}),
|
|
)
|
|
defer adm.Close()
|
|
|
|
// In whitelist mode, non-matching IP is denied
|
|
assert.False(t, adm.Admit(context.Background(), "tcp", "10.0.0.1"))
|
|
}
|
|
|
|
func TestAdmit_StripsPort(t *testing.T) {
|
|
adm := newSyncedAdmission(MatchersOption([]string{"192.168.1.1"}))
|
|
defer adm.Close()
|
|
|
|
// Address with port should have port stripped before matching
|
|
assert.False(t, adm.Admit(context.Background(), "tcp", "192.168.1.1:8080"))
|
|
}
|
|
|
|
func TestAdmit_CIDRMatch(t *testing.T) {
|
|
adm := newSyncedAdmission(MatchersOption([]string{"10.0.0.0/8"}))
|
|
defer adm.Close()
|
|
|
|
assert.False(t, adm.Admit(context.Background(), "tcp", "10.0.0.1"))
|
|
assert.True(t, adm.Admit(context.Background(), "tcp", "192.168.1.1"))
|
|
}
|
|
|
|
func TestAdmit_InvalidAddr(t *testing.T) {
|
|
adm := newSyncedAdmission(MatchersOption([]string{"192.168.1.1"}))
|
|
defer adm.Close()
|
|
|
|
// Invalid IP should not match and be admitted in blacklist mode
|
|
assert.True(t, adm.Admit(context.Background(), "tcp", "invalid"))
|
|
}
|
|
|
|
// --- parseLine tests ---
|
|
|
|
func TestParseLine_Normal(t *testing.T) {
|
|
adm := &localAdmission{}
|
|
assert.Equal(t, "192.168.1.1", adm.parseLine("192.168.1.1"))
|
|
}
|
|
|
|
func TestParseLine_WithComment(t *testing.T) {
|
|
adm := &localAdmission{}
|
|
assert.Equal(t, "192.168.1.1", adm.parseLine("192.168.1.1 # a comment"))
|
|
}
|
|
|
|
func TestParseLine_CommentOnly(t *testing.T) {
|
|
adm := &localAdmission{}
|
|
assert.Equal(t, "", adm.parseLine("# comment only"))
|
|
}
|
|
|
|
func TestParseLine_Empty(t *testing.T) {
|
|
adm := &localAdmission{}
|
|
assert.Equal(t, "", adm.parseLine(""))
|
|
}
|
|
|
|
func TestParseLine_Whitespace(t *testing.T) {
|
|
adm := &localAdmission{}
|
|
assert.Equal(t, "192.168.1.1", adm.parseLine(" 192.168.1.1 "))
|
|
}
|
|
|
|
// --- parsePatterns tests ---
|
|
|
|
func TestParsePatterns_NilReader(t *testing.T) {
|
|
adm := &localAdmission{}
|
|
patterns, err := adm.parsePatterns(nil)
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, patterns)
|
|
}
|
|
|
|
func TestParsePatterns_Normal(t *testing.T) {
|
|
adm := &localAdmission{}
|
|
r := strings.NewReader("192.168.1.1\n10.0.0.1\n# comment\n\n fd00::1 \n")
|
|
patterns, err := adm.parsePatterns(r)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, []string{"192.168.1.1", "10.0.0.1", "fd00::1"}, patterns)
|
|
}
|
|
|
|
// --- matched tests ---
|
|
|
|
func TestMatched_IP(t *testing.T) {
|
|
adm := newSyncedAdmission(MatchersOption([]string{"192.168.1.1"}))
|
|
defer adm.Close()
|
|
|
|
assert.True(t, adm.matched("192.168.1.1"))
|
|
assert.False(t, adm.matched("10.0.0.1"))
|
|
}
|
|
|
|
func TestMatched_CIDR(t *testing.T) {
|
|
adm := newSyncedAdmission(MatchersOption([]string{"10.0.0.0/8"}))
|
|
defer adm.Close()
|
|
|
|
assert.True(t, adm.matched("10.0.0.1"))
|
|
assert.False(t, adm.matched("192.168.1.1"))
|
|
}
|
|
|
|
func TestMatched_BothIPAndCIDR(t *testing.T) {
|
|
adm := newSyncedAdmission(MatchersOption([]string{"192.168.1.1", "10.0.0.0/8"}))
|
|
defer adm.Close()
|
|
|
|
assert.True(t, adm.matched("192.168.1.1"))
|
|
assert.True(t, adm.matched("10.0.0.1"))
|
|
assert.False(t, adm.matched("172.16.0.1"))
|
|
}
|
|
|
|
// --- reload tests ---
|
|
|
|
func TestReload_WithHostnameResolution(t *testing.T) {
|
|
adm := newSyncedAdmission(
|
|
MatchersOption([]string{"localhost"}),
|
|
LoggerOption(xlogger.Nop()),
|
|
)
|
|
defer adm.Close()
|
|
|
|
// After reload, localhost should be resolved to 127.0.0.1 or ::1
|
|
err := adm.reload(context.Background())
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
// --- load tests ---
|
|
|
|
func TestLoad_AllNilLoaders(t *testing.T) {
|
|
adm := &localAdmission{
|
|
logger: xlogger.Nop(),
|
|
}
|
|
patterns, err := adm.load(context.Background())
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, patterns)
|
|
}
|
|
|
|
func TestLoad_FileLoaderWithList(t *testing.T) {
|
|
adm := &localAdmission{
|
|
options: options{
|
|
fileLoader: &mockListerLoader{list: []string{"192.168.1.1", "# comment", "10.0.0.1"}},
|
|
},
|
|
logger: xlogger.Nop(),
|
|
}
|
|
patterns, err := adm.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) {
|
|
adm := &localAdmission{
|
|
options: options{
|
|
fileLoader: &mockLoader{data: "192.168.1.1\n10.0.0.1\n"},
|
|
},
|
|
logger: xlogger.Nop(),
|
|
}
|
|
patterns, err := adm.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) {
|
|
adm := &localAdmission{
|
|
options: options{
|
|
redisLoader: &mockListerLoader{list: []string{"redis-host-1", "redis-host-2"}},
|
|
},
|
|
logger: xlogger.Nop(),
|
|
}
|
|
patterns, err := adm.load(context.Background())
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, []string{"redis-host-1", "redis-host-2"}, patterns)
|
|
}
|
|
|
|
func TestLoad_RedisLoaderWithLoad(t *testing.T) {
|
|
adm := &localAdmission{
|
|
options: options{
|
|
redisLoader: &mockLoader{data: "192.168.2.1\n"},
|
|
},
|
|
logger: xlogger.Nop(),
|
|
}
|
|
patterns, err := adm.load(context.Background())
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, []string{"192.168.2.1"}, patterns)
|
|
}
|
|
|
|
func TestLoad_HTTPLoader(t *testing.T) {
|
|
adm := &localAdmission{
|
|
options: options{
|
|
httpLoader: &mockLoader{data: "10.10.10.1\n10.10.10.2\n"},
|
|
},
|
|
logger: xlogger.Nop(),
|
|
}
|
|
patterns, err := adm.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) {
|
|
adm := &localAdmission{
|
|
options: options{
|
|
fileLoader: &mockListerLoader{listErr: io.ErrUnexpectedEOF},
|
|
},
|
|
logger: xlogger.Nop(),
|
|
}
|
|
patterns, err := adm.load(context.Background())
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, patterns)
|
|
}
|
|
|
|
func TestLoad_FileLoaderWithLoadError(t *testing.T) {
|
|
adm := &localAdmission{
|
|
options: options{
|
|
fileLoader: &mockLoader{loadErr: io.ErrUnexpectedEOF},
|
|
},
|
|
logger: xlogger.Nop(),
|
|
}
|
|
patterns, err := adm.load(context.Background())
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, patterns)
|
|
}
|
|
|
|
func TestLoad_RedisLoaderWithListError(t *testing.T) {
|
|
adm := &localAdmission{
|
|
options: options{
|
|
redisLoader: &mockListerLoader{listErr: io.ErrUnexpectedEOF},
|
|
},
|
|
logger: xlogger.Nop(),
|
|
}
|
|
patterns, err := adm.load(context.Background())
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, patterns)
|
|
}
|
|
|
|
func TestLoad_RedisLoaderWithLoadError(t *testing.T) {
|
|
adm := &localAdmission{
|
|
options: options{
|
|
redisLoader: &mockLoader{loadErr: io.ErrUnexpectedEOF},
|
|
},
|
|
logger: xlogger.Nop(),
|
|
}
|
|
patterns, err := adm.load(context.Background())
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, patterns)
|
|
}
|
|
|
|
func TestLoad_HTTPLoaderError(t *testing.T) {
|
|
adm := &localAdmission{
|
|
options: options{
|
|
httpLoader: &mockLoader{loadErr: io.ErrUnexpectedEOF},
|
|
},
|
|
logger: xlogger.Nop(),
|
|
}
|
|
patterns, err := adm.load(context.Background())
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, patterns)
|
|
}
|
|
|
|
func TestLoad_FileLoaderWithListParsing(t *testing.T) {
|
|
adm := &localAdmission{
|
|
options: options{
|
|
fileLoader: &mockListerLoader{list: []string{" # comment", "", " 192.168.1.1 "}},
|
|
},
|
|
logger: xlogger.Nop(),
|
|
}
|
|
patterns, err := adm.load(context.Background())
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, []string{"192.168.1.1"}, patterns)
|
|
}
|
|
|
|
// --- periodReload tests ---
|
|
|
|
func TestPeriodReload_ZeroPeriod(t *testing.T) {
|
|
adm := &localAdmission{
|
|
logger: xlogger.Nop(),
|
|
options: options{period: 0},
|
|
}
|
|
err := adm.periodReload(context.Background())
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestPeriodReload_NegativePeriod(t *testing.T) {
|
|
adm := &localAdmission{
|
|
logger: xlogger.Nop(),
|
|
options: options{period: -1},
|
|
}
|
|
err := adm.periodReload(context.Background())
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestPeriodReload_WithContextCancel(t *testing.T) {
|
|
// 500ms < 1s triggers the clamping branch (period = time.Second),
|
|
// then sleep > 1s to let at least one ticker cycle fire before cancel.
|
|
adm := &localAdmission{
|
|
logger: xlogger.Nop(),
|
|
options: options{period: 500 * time.Millisecond},
|
|
}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
errCh := make(chan error, 1)
|
|
go func() {
|
|
errCh <- adm.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) {
|
|
adm := newSyncedAdmission()
|
|
err := adm.Close()
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestClose_WithAllLoaders(t *testing.T) {
|
|
fl := &mockLoader{}
|
|
rl := &mockLoader{}
|
|
hl := &mockLoader{}
|
|
|
|
adm := newSyncedAdmission(
|
|
FileLoaderOption(fl),
|
|
RedisLoaderOption(rl),
|
|
HTTPLoaderOption(hl),
|
|
)
|
|
err := adm.Close()
|
|
assert.NoError(t, err)
|
|
|
|
assert.True(t, fl.closed)
|
|
assert.True(t, rl.closed)
|
|
assert.True(t, hl.closed)
|
|
}
|
|
|
|
// --- DNS resolution in reload ---
|
|
|
|
func TestReload_DNSResolution(t *testing.T) {
|
|
adm := &localAdmission{
|
|
ipMatcher: nil,
|
|
cidrMatcher: nil,
|
|
options: options{
|
|
matchers: []string{"localhost"},
|
|
},
|
|
logger: xlogger.Nop(),
|
|
}
|
|
err := adm.reload(context.Background())
|
|
assert.NoError(t, err)
|
|
|
|
// localhost should resolve to 127.0.0.1 (or ::1)
|
|
assert.True(t, adm.matched("127.0.0.1") || adm.matched("::1"))
|
|
}
|
|
|
|
// --- AdmissionGroup tests ---
|
|
|
|
func TestAdmissionGroup_Empty(t *testing.T) {
|
|
g := AdmissionGroup()
|
|
assert.True(t, g.Admit(context.Background(), "tcp", "192.168.1.1"))
|
|
}
|
|
|
|
func TestAdmissionGroup_AllAdmit(t *testing.T) {
|
|
g := AdmissionGroup(
|
|
alwaysAdmit{},
|
|
alwaysAdmit{},
|
|
)
|
|
assert.True(t, g.Admit(context.Background(), "tcp", "192.168.1.1"))
|
|
}
|
|
|
|
func TestAdmissionGroup_OneDenies(t *testing.T) {
|
|
g := AdmissionGroup(
|
|
alwaysAdmit{},
|
|
alwaysDeny{},
|
|
)
|
|
assert.False(t, g.Admit(context.Background(), "tcp", "192.168.1.1"))
|
|
}
|
|
|
|
func TestAdmissionGroup_NilMember(t *testing.T) {
|
|
g := AdmissionGroup(
|
|
nil,
|
|
alwaysAdmit{},
|
|
nil,
|
|
)
|
|
assert.True(t, g.Admit(context.Background(), "tcp", "192.168.1.1"))
|
|
}
|
|
|
|
func TestAdmissionGroup_ShortCircuit(t *testing.T) {
|
|
// The first deny should short-circuit, never calling the second.
|
|
callCount := 0
|
|
tracking := &trackingAdmission{admit: false, callCount: &callCount}
|
|
g := AdmissionGroup(tracking, alwaysAdmit{})
|
|
assert.False(t, g.Admit(context.Background(), "tcp", "192.168.1.1"))
|
|
assert.Equal(t, 1, callCount)
|
|
}
|
|
|
|
// --- 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 alwaysAdmit struct{}
|
|
|
|
func (alwaysAdmit) Admit(ctx context.Context, network, addr string, opts ...admission.Option) bool {
|
|
return true
|
|
}
|
|
|
|
type alwaysDeny struct{}
|
|
|
|
func (alwaysDeny) Admit(ctx context.Context, network, addr string, opts ...admission.Option) bool {
|
|
return false
|
|
}
|
|
|
|
type trackingAdmission struct {
|
|
admit bool
|
|
callCount *int
|
|
}
|
|
|
|
func (t *trackingAdmission) Admit(ctx context.Context, network, addr string, opts ...admission.Option) bool {
|
|
*t.callCount++
|
|
return t.admit
|
|
}
|
|
|
|
// Ensure mock types implement the interfaces.
|
|
var _ loader.Loader = (*mockLoader)(nil)
|
|
var _ loader.Lister = (*mockListerLoader)(nil)
|
|
var _ loader.Loader = (*mockListerLoader)(nil)
|
|
var _ admission.Admission = alwaysAdmit{}
|
|
var _ admission.Admission = alwaysDeny{}
|
|
|
|
// TestAdmit_IPv6 tests that IPv6 addresses are handled correctly.
|
|
func TestAdmit_IPv6(t *testing.T) {
|
|
adm := newSyncedAdmission(MatchersOption([]string{"fd00::1"}))
|
|
defer adm.Close()
|
|
|
|
assert.False(t, adm.Admit(context.Background(), "tcp", "fd00::1"))
|
|
assert.True(t, adm.Admit(context.Background(), "tcp", "fd00::2"))
|
|
}
|
|
|
|
// TestAdmit_IPv6WithPort tests that IPv6 addresses with port brackets are handled.
|
|
func TestAdmit_IPv6WithPort(t *testing.T) {
|
|
adm := newSyncedAdmission(MatchersOption([]string{"::1"}))
|
|
defer adm.Close()
|
|
|
|
// net.SplitHostPort handles [::1]:8080 format
|
|
assert.False(t, adm.Admit(context.Background(), "tcp", "[::1]:8080"))
|
|
}
|
|
|
|
// TestNewAdmission_ReloadErrorInBackground verifies that the initial reload
|
|
// in the background goroutine doesn't panic, even if it fails.
|
|
func TestNewAdmission_ReloadError(t *testing.T) {
|
|
fl := &mockLoader{loadErr: io.ErrUnexpectedEOF}
|
|
adm := newSyncedAdmission(
|
|
FileLoaderOption(fl),
|
|
LoggerOption(xlogger.Nop()),
|
|
)
|
|
defer adm.Close()
|
|
|
|
// Should still work, just logged a warning
|
|
assert.True(t, adm.Admit(context.Background(), "tcp", "192.168.1.1"))
|
|
}
|
|
|
|
// TestAdmissionGroup_NilMemberFirst tests nil as first member.
|
|
func TestAdmissionGroup_NilMemberFirst(t *testing.T) {
|
|
g := AdmissionGroup(nil, alwaysDeny{})
|
|
assert.False(t, g.Admit(context.Background(), "tcp", "192.168.1.1"))
|
|
}
|
|
|
|
// TestAdmissionGroup_AllNil tests all nil members.
|
|
func TestAdmissionGroup_AllNil(t *testing.T) {
|
|
g := AdmissionGroup(nil, nil)
|
|
assert.True(t, g.Admit(context.Background(), "tcp", "192.168.1.1"))
|
|
}
|
|
|
|
// TestLoggerDefault tests that default logger is set when nil is passed.
|
|
func TestLoggerOption_Nil(t *testing.T) {
|
|
var opts options
|
|
LoggerOption(nil)(&opts)
|
|
assert.Nil(t, opts.logger)
|
|
}
|
|
|
|
// TestAdmit_IpMatcher test case where only IP matcher is valid.
|
|
func TestReload_OnlyIPs(t *testing.T) {
|
|
adm := &localAdmission{
|
|
options: options{
|
|
matchers: []string{"1.1.1.1", "8.8.8.8"},
|
|
},
|
|
logger: xlogger.Nop(),
|
|
}
|
|
err := adm.reload(context.Background())
|
|
assert.NoError(t, err)
|
|
assert.True(t, adm.matched("1.1.1.1"))
|
|
assert.True(t, adm.matched("8.8.8.8"))
|
|
assert.False(t, adm.matched("9.9.9.9"))
|
|
}
|
|
|
|
// TestReload_OnlyCIDRs tests reload with only CIDR patterns.
|
|
func TestReload_OnlyCIDRs(t *testing.T) {
|
|
adm := &localAdmission{
|
|
options: options{
|
|
matchers: []string{"192.168.0.0/16", "10.0.0.0/8"},
|
|
},
|
|
logger: xlogger.Nop(),
|
|
}
|
|
err := adm.reload(context.Background())
|
|
assert.NoError(t, err)
|
|
assert.True(t, adm.matched("192.168.1.1"))
|
|
assert.True(t, adm.matched("10.0.0.1"))
|
|
assert.False(t, adm.matched("172.16.0.1"))
|
|
}
|
|
|
|
// TestAdmit_LoggerDebugOutput tests that debug logging happens on deny.
|
|
func TestAdmit_DenyLogs(t *testing.T) {
|
|
adm := newSyncedAdmission(
|
|
MatchersOption([]string{"192.168.1.1"}),
|
|
LoggerOption(xlogger.Nop()),
|
|
)
|
|
defer adm.Close()
|
|
|
|
// Should not panic; debug log is written
|
|
assert.False(t, adm.Admit(context.Background(), "tcp", "192.168.1.1"))
|
|
}
|
|
|
|
// TestAdmit_WhitelistDenyLogs tests logging for whitelist deny.
|
|
func TestAdmit_WhitelistDenyLogs(t *testing.T) {
|
|
adm := newSyncedAdmission(
|
|
WhitelistOption(true),
|
|
MatchersOption([]string{"192.168.1.1"}),
|
|
LoggerOption(xlogger.Nop()),
|
|
)
|
|
defer adm.Close()
|
|
|
|
assert.False(t, adm.Admit(context.Background(), "tcp", "10.0.0.1"))
|
|
}
|
|
|
|
// TestParsePatterns_WithCommentsAndEmptyLines tests edge cases.
|
|
func TestParsePatterns_EdgeCases(t *testing.T) {
|
|
adm := &localAdmission{}
|
|
r := strings.NewReader("# full comment\n\n # indented comment\n192.168.1.1 # inline")
|
|
patterns, err := adm.parsePatterns(r)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, []string{"192.168.1.1"}, patterns)
|
|
}
|
|
|
|
func TestAdmit_NetworkIgnoredForMatching(t *testing.T) {
|
|
// Network parameter doesn't affect matching behavior
|
|
adm := newSyncedAdmission(MatchersOption([]string{"192.168.1.1"}))
|
|
defer adm.Close()
|
|
|
|
assert.False(t, adm.Admit(context.Background(), "tcp", "192.168.1.1"))
|
|
assert.False(t, adm.Admit(context.Background(), "udp", "192.168.1.1"))
|
|
assert.False(t, adm.Admit(context.Background(), "ip4", "192.168.1.1"))
|
|
}
|
|
|
|
// Compile-time interface check
|
|
var _ logger.Logger = xlogger.Nop()
|
|
|
|
// Ensure admission.Admission is implemented
|
|
var _ admission.Admission = (*localAdmission)(nil)
|