package registry import ( "context" "errors" "net" "sync" "testing" "github.com/go-gost/core/admission" "github.com/go-gost/core/auth" "github.com/go-gost/core/bypass" "github.com/go-gost/core/chain" "github.com/go-gost/core/connector" "github.com/go-gost/core/dialer" "github.com/go-gost/core/handler" "github.com/go-gost/core/hop" "github.com/go-gost/core/hosts" "github.com/go-gost/core/ingress" "github.com/go-gost/core/limiter" "github.com/go-gost/core/limiter/conn" "github.com/go-gost/core/limiter/rate" "github.com/go-gost/core/limiter/traffic" "github.com/go-gost/core/listener" "github.com/go-gost/core/logger" "github.com/go-gost/core/metadata" "github.com/go-gost/core/observer" "github.com/go-gost/core/recorder" reg "github.com/go-gost/core/registry" "github.com/go-gost/core/resolver" "github.com/go-gost/core/router" "github.com/go-gost/core/sd" "github.com/go-gost/core/selector" "github.com/go-gost/core/service" ) // --- Base registry[T] tests --- func TestRegistry_RegisterAndGet(t *testing.T) { r := new(registry[string]) if err := r.Register("a", "alpha"); err != nil { t.Fatalf("Register: %v", err) } if v := r.Get("a"); v != "alpha" { t.Fatalf("Get(%q) = %q, want %q", "a", v, "alpha") } } func TestRegistry_RegisterEmptyName(t *testing.T) { r := new(registry[string]) if err := r.Register("", "value"); err != nil { t.Fatalf("Register empty name: %v", err) } if r.IsRegistered("") { t.Fatal("empty name should not be registered") } } func TestRegistry_RegisterDuplicate(t *testing.T) { r := new(registry[int]) if err := r.Register("x", 1); err != nil { t.Fatal(err) } if err := r.Register("x", 2); !errors.Is(err, ErrDup) { t.Fatalf("duplicate Register = %v, want ErrDup", err) } if v := r.Get("x"); v != 1 { t.Fatalf("Get after dup attempt = %d, want 1", v) } } func TestRegistry_GetUnknown(t *testing.T) { r := new(registry[string]) if v := r.Get("missing"); v != "" { t.Fatalf("Get(unknown) = %q, want zero value", v) } } func TestRegistry_GetEmptyName(t *testing.T) { r := new(registry[int]) r.Register("x", 42) if v := r.Get(""); v != 0 { t.Fatalf("Get(empty) = %d, want 0", v) } } func TestRegistry_GetAll(t *testing.T) { r := new(registry[string]) r.Register("a", "alpha") r.Register("b", "beta") m := r.GetAll() if len(m) != 2 { t.Fatalf("GetAll len = %d, want 2", len(m)) } if m["a"] != "alpha" || m["b"] != "beta" { t.Fatalf("GetAll = %v", m) } } func TestRegistry_GetAllEmpty(t *testing.T) { r := new(registry[int]) m := r.GetAll() if m == nil || len(m) != 0 { t.Fatalf("GetAll on empty = %v, want empty non-nil map", m) } } func TestRegistry_IsRegistered(t *testing.T) { r := new(registry[string]) r.Register("k", "v") if !r.IsRegistered("k") { t.Fatal("IsRegistered(k) = false, want true") } if r.IsRegistered("nope") { t.Fatal("IsRegistered(nope) = true, want false") } } func TestRegistry_Unregister(t *testing.T) { r := new(registry[string]) r.Register("x", "val") r.Unregister("x") if r.IsRegistered("x") { t.Fatal("still registered after Unregister") } if v := r.Get("x"); v != "" { t.Fatalf("Get after unregister = %q, want zero", v) } } func TestRegistry_UnregisterUnknown(t *testing.T) { r := new(registry[string]) r.Unregister("nope") // should not panic } type closeableService struct { closed bool } func (s *closeableService) Serve() error { return nil } func (s *closeableService) Addr() net.Addr { return nil } func (s *closeableService) Close() error { s.closed = true return nil } func TestRegistry_UnregisterCloses(t *testing.T) { r := new(registry[service.Service]) svc := &closeableService{} r.Register("svc", svc) r.Unregister("svc") if !svc.closed { t.Fatal("Unregister did not call Close on io.Closer") } } type closeErrService struct{} func (s *closeErrService) Serve() error { return nil } func (s *closeErrService) Addr() net.Addr { return nil } func (s *closeErrService) Close() error { return errors.New("close err") } func TestRegistry_UnregisterCloseError(t *testing.T) { logger.SetDefault(&mockLogger{}) r := new(registry[service.Service]) svc := &closeErrService{} r.Register("svc", svc) r.Unregister("svc") // logs error but should not panic if r.IsRegistered("svc") { t.Fatal("should be unregistered even if Close errors") } } func TestRegistry_ConcurrentAccess(t *testing.T) { r := new(registry[int]) var wg sync.WaitGroup for i := range 100 { wg.Add(1) go func(i int) { defer wg.Done() name := string(rune('a' + i%26)) r.Register(name, i) r.Get(name) r.IsRegistered(name) r.GetAll() }(i) } wg.Wait() } // --- Global accessor tests --- func TestGlobalRegistryAccessors(t *testing.T) { if ListenerRegistry() == nil { t.Error("ListenerRegistry() returned nil") } if HandlerRegistry() == nil { t.Error("HandlerRegistry() returned nil") } if DialerRegistry() == nil { t.Error("DialerRegistry() returned nil") } if ConnectorRegistry() == nil { t.Error("ConnectorRegistry() returned nil") } if ServiceRegistry() == nil { t.Error("ServiceRegistry() returned nil") } if ChainRegistry() == nil { t.Error("ChainRegistry() returned nil") } if HopRegistry() == nil { t.Error("HopRegistry() returned nil") } if AutherRegistry() == nil { t.Error("AutherRegistry() returned nil") } if AdmissionRegistry() == nil { t.Error("AdmissionRegistry() returned nil") } if BypassRegistry() == nil { t.Error("BypassRegistry() returned nil") } if ResolverRegistry() == nil { t.Error("ResolverRegistry() returned nil") } if HostsRegistry() == nil { t.Error("HostsRegistry() returned nil") } if RecorderRegistry() == nil { t.Error("RecorderRegistry() returned nil") } if TrafficLimiterRegistry() == nil { t.Error("TrafficLimiterRegistry() returned nil") } if ConnLimiterRegistry() == nil { t.Error("ConnLimiterRegistry() returned nil") } if RateLimiterRegistry() == nil { t.Error("RateLimiterRegistry() returned nil") } if IngressRegistry() == nil { t.Error("IngressRegistry() returned nil") } if RouterRegistry() == nil { t.Error("RouterRegistry() returned nil") } if SDRegistry() == nil { t.Error("SDRegistry() returned nil") } if ObserverRegistry() == nil { t.Error("ObserverRegistry() returned nil") } if LoggerRegistry() == nil { t.Error("LoggerRegistry() returned nil") } } // Verify each accessor returns the same instance on repeated calls. func TestGlobalRegistrySingleton(t *testing.T) { if ListenerRegistry() != ListenerRegistry() { t.Error("ListenerRegistry not singleton") } if HandlerRegistry() != HandlerRegistry() { t.Error("HandlerRegistry not singleton") } if DialerRegistry() != DialerRegistry() { t.Error("DialerRegistry not singleton") } if ConnectorRegistry() != ConnectorRegistry() { t.Error("ConnectorRegistry not singleton") } if ServiceRegistry() != ServiceRegistry() { t.Error("ServiceRegistry not singleton") } if ChainRegistry() != ChainRegistry() { t.Error("ChainRegistry not singleton") } if HopRegistry() != HopRegistry() { t.Error("HopRegistry not singleton") } if AutherRegistry() != AutherRegistry() { t.Error("AutherRegistry not singleton") } if AdmissionRegistry() != AdmissionRegistry() { t.Error("AdmissionRegistry not singleton") } if BypassRegistry() != BypassRegistry() { t.Error("BypassRegistry not singleton") } if ResolverRegistry() != ResolverRegistry() { t.Error("ResolverRegistry not singleton") } if HostsRegistry() != HostsRegistry() { t.Error("HostsRegistry not singleton") } if RecorderRegistry() != RecorderRegistry() { t.Error("RecorderRegistry not singleton") } if TrafficLimiterRegistry() != TrafficLimiterRegistry() { t.Error("TrafficLimiterRegistry not singleton") } if ConnLimiterRegistry() != ConnLimiterRegistry() { t.Error("ConnLimiterRegistry not singleton") } if RateLimiterRegistry() != RateLimiterRegistry() { t.Error("RateLimiterRegistry not singleton") } if IngressRegistry() != IngressRegistry() { t.Error("IngressRegistry not singleton") } if RouterRegistry() != RouterRegistry() { t.Error("RouterRegistry not singleton") } if SDRegistry() != SDRegistry() { t.Error("SDRegistry not singleton") } if ObserverRegistry() != ObserverRegistry() { t.Error("ObserverRegistry not singleton") } if LoggerRegistry() != LoggerRegistry() { t.Error("LoggerRegistry not singleton") } } // --- Admission wrapper tests --- type mockAdmission struct { admit bool } func (m *mockAdmission) Admit(_ context.Context, _, _ string, _ ...admission.Option) bool { return m.admit } func TestAdmissionRegistry_GetEmpty(t *testing.T) { r := new(admissionRegistry) if v := r.Get(""); v != nil { t.Fatal("Get(empty) should return nil") } } func TestAdmissionRegistry_Delegate(t *testing.T) { r := new(admissionRegistry) mock := &mockAdmission{admit: true} r.Register("test", mock) w := r.Get("test") if w == nil { t.Fatal("Get returned nil") } if !w.Admit(context.Background(), "tcp", "1.2.3.4") { t.Fatal("Admit should delegate to mock") } } func TestAdmissionRegistry_HotReload(t *testing.T) { r := new(admissionRegistry) r.Register("h", &mockAdmission{admit: false}) w := r.Get("h") if w.Admit(context.Background(), "tcp", "x") { t.Fatal("expected false from first mock") } // Hot-reload: replace the underlying value. r.Register("h", &mockAdmission{admit: true}) // will fail with ErrDup r.Unregister("h") r.Register("h", &mockAdmission{admit: true}) // Same wrapper should now see the new value. if !w.Admit(context.Background(), "tcp", "x") { t.Fatal("wrapper did not pick up re-registered value") } } func TestAdmissionRegistry_NilUnderlying(t *testing.T) { r := new(admissionRegistry) r.Register("missing", &mockAdmission{}) r.Unregister("missing") w := r.Get("missing") if w == nil { t.Fatal("Get should return wrapper even if underlying is nil") } // Should not panic, should return false. if w.Admit(context.Background(), "tcp", "x") { t.Fatal("nil underlying should return false") } } // --- Auther wrapper tests --- type mockAuther struct { id string valid bool } func (m *mockAuther) Authenticate(_ context.Context, _, _ string, _ ...auth.Option) (string, bool) { return m.id, m.valid } func TestAutherRegistry_GetEmpty(t *testing.T) { r := new(autherRegistry) if v := r.Get(""); v != nil { t.Fatal("Get(empty) should return nil") } } func TestAutherRegistry_Delegate(t *testing.T) { r := new(autherRegistry) r.Register("a", &mockAuther{id: "user1", valid: true}) w := r.Get("a") id, ok := w.Authenticate(context.Background(), "user", "pass") if !ok || id != "user1" { t.Fatalf("Authenticate = (%q, %v), want (user1, true)", id, ok) } } func TestAutherRegistry_NilUnderlying(t *testing.T) { r := new(autherRegistry) w := r.Get("ghost") // No underlying value registered → should return ("", true) per wrapper default. id, ok := w.Authenticate(context.Background(), "u", "p") if !ok || id != "" { t.Fatalf("nil underlying = (%q, %v), want (\"\", true)", id, ok) } } // --- Bypass wrapper tests --- type mockBypass struct { contains bool whitelist bool } func (m *mockBypass) Contains(_ context.Context, _, _ string, _ ...bypass.Option) bool { return m.contains } func (m *mockBypass) IsWhitelist() bool { return m.whitelist } func TestBypassRegistry_Delegate(t *testing.T) { r := new(bypassRegistry) r.Register("b", &mockBypass{contains: true, whitelist: true}) w := r.Get("b") if !w.Contains(context.Background(), "tcp", "1.2.3.4") { t.Fatal("Contains should delegate") } if !w.IsWhitelist() { t.Fatal("IsWhitelist should delegate") } } func TestBypassRegistry_NilUnderlying(t *testing.T) { r := new(bypassRegistry) w := r.Get("ghost") if w.Contains(context.Background(), "tcp", "x") { t.Fatal("nil underlying Contains should return false") } if w.IsWhitelist() { t.Fatal("nil underlying IsWhitelist should return false") } } // --- Chain wrapper tests --- type mockChainer struct { route chain.Route md metadata.Metadata } func (m *mockChainer) Route(_ context.Context, _, _ string, _ ...chain.RouteOption) chain.Route { return m.route } func (m *mockChainer) Marker() selector.Marker { return nil } func (m *mockChainer) Metadata() metadata.Metadata { return m.md } func TestChainRegistry_Delegate(t *testing.T) { r := new(chainRegistry) r.Register("c", &mockChainer{}) w := r.Get("c") if w == nil { t.Fatal("Get returned nil") } if w.Route(context.Background(), "tcp", "addr") != nil { t.Fatal("mock returns nil route") } } func TestChainRegistry_NilUnderlying(t *testing.T) { r := new(chainRegistry) w := r.Get("ghost") if w.Route(context.Background(), "tcp", "x") != nil { t.Fatal("nil underlying Route should return nil") } } // --- Hop wrapper tests --- type mockHop struct { node *chain.Node } func (m *mockHop) Select(_ context.Context, _ ...hop.SelectOption) *chain.Node { return m.node } func (m *mockHop) Nodes() []*chain.Node { return nil } func TestHopRegistry_Delegate(t *testing.T) { r := new(hopRegistry) r.Register("h", &mockHop{}) w := r.Get("h") if w.Select(context.Background()) != nil { t.Fatal("mock Select returns nil") } } func TestHopRegistry_NilUnderlying(t *testing.T) { r := new(hopRegistry) w := r.Get("ghost") if w.Select(context.Background()) != nil { t.Fatal("nil underlying Select should return nil") } } // --- Hosts wrapper tests --- type mockHosts struct { ips []net.IP ok bool } func (m *mockHosts) Lookup(_ context.Context, _, _ string, _ ...hosts.Option) ([]net.IP, bool) { return m.ips, m.ok } func TestHostsRegistry_Delegate(t *testing.T) { r := new(hostsRegistry) ips := []net.IP{net.ParseIP("1.2.3.4")} r.Register("h", &mockHosts{ips: ips, ok: true}) w := r.Get("h") result, ok := w.Lookup(context.Background(), "tcp", "example.com") if !ok || len(result) != 1 { t.Fatalf("Lookup = (%v, %v)", result, ok) } } func TestHostsRegistry_NilUnderlying(t *testing.T) { r := new(hostsRegistry) w := r.Get("ghost") ips, ok := w.Lookup(context.Background(), "tcp", "x") if ok || ips != nil { t.Fatal("nil underlying should return (nil, false)") } } // --- Ingress wrapper tests --- type mockIngress struct { rule *ingress.Rule set bool } func (m *mockIngress) GetRule(_ context.Context, _ string, _ ...ingress.Option) *ingress.Rule { return m.rule } func (m *mockIngress) SetRule(_ context.Context, _ *ingress.Rule, _ ...ingress.Option) bool { return m.set } func TestIngressRegistry_Delegate(t *testing.T) { r := new(ingressRegistry) r.Register("i", &mockIngress{set: true}) w := r.Get("i") if w.GetRule(context.Background(), "example.com") != nil { t.Fatal("mock GetRule returns nil") } if !w.SetRule(context.Background(), nil) { t.Fatal("SetRule should delegate") } } func TestIngressRegistry_NilUnderlying(t *testing.T) { r := new(ingressRegistry) w := r.Get("ghost") if w.GetRule(context.Background(), "x") != nil { t.Fatal("nil GetRule should return nil") } if w.SetRule(context.Background(), nil) { t.Fatal("nil SetRule should return false") } } // --- TrafficLimiter wrapper tests --- type mockTrafficLimiter struct { in traffic.Limiter out traffic.Limiter } func (m *mockTrafficLimiter) In(_ context.Context, _ string, _ ...limiter.Option) traffic.Limiter { return m.in } func (m *mockTrafficLimiter) Out(_ context.Context, _ string, _ ...limiter.Option) traffic.Limiter { return m.out } func TestTrafficLimiterRegistry_Delegate(t *testing.T) { r := new(trafficLimiterRegistry) r.Register("tl", &mockTrafficLimiter{}) w := r.Get("tl") if w.In(context.Background(), "k") != nil { t.Fatal("mock In returns nil") } if w.Out(context.Background(), "k") != nil { t.Fatal("mock Out returns nil") } } func TestTrafficLimiterRegistry_NilUnderlying(t *testing.T) { r := new(trafficLimiterRegistry) w := r.Get("ghost") if w.In(context.Background(), "k") != nil { t.Fatal("nil In should return nil") } if w.Out(context.Background(), "k") != nil { t.Fatal("nil Out should return nil") } } // --- ConnLimiter wrapper tests --- type mockConnLimiter struct { lim conn.Limiter } func (m *mockConnLimiter) Limiter(_ string) conn.Limiter { return m.lim } func TestConnLimiterRegistry_Delegate(t *testing.T) { r := new(connLimiterRegistry) r.Register("cl", &mockConnLimiter{}) w := r.Get("cl") if w.Limiter("k") != nil { t.Fatal("mock returns nil limiter") } } func TestConnLimiterRegistry_NilUnderlying(t *testing.T) { r := new(connLimiterRegistry) w := r.Get("ghost") if w.Limiter("k") != nil { t.Fatal("nil underlying should return nil") } } // --- RateLimiter wrapper tests --- type mockRateLimiter struct { lim rate.Limiter } func (m *mockRateLimiter) Limiter(_ string) rate.Limiter { return m.lim } func TestRateLimiterRegistry_Delegate(t *testing.T) { r := new(rateLimiterRegistry) r.Register("rl", &mockRateLimiter{}) w := r.Get("rl") if w.Limiter("k") != nil { t.Fatal("mock returns nil limiter") } } func TestRateLimiterRegistry_NilUnderlying(t *testing.T) { r := new(rateLimiterRegistry) w := r.Get("ghost") if w.Limiter("k") != nil { t.Fatal("nil underlying should return nil") } } // --- Observer wrapper tests --- type mockObserver struct { err error } func (m *mockObserver) Observe(_ context.Context, _ []observer.Event, _ ...observer.Option) error { return m.err } func TestObserverRegistry_Delegate(t *testing.T) { r := new(observerRegistry) r.Register("o", &mockObserver{}) w := r.Get("o") if err := w.Observe(context.Background(), nil); err != nil { t.Fatalf("Observe = %v", err) } } func TestObserverRegistry_NilUnderlying(t *testing.T) { r := new(observerRegistry) w := r.Get("ghost") if err := w.Observe(context.Background(), nil); err != nil { t.Fatal("nil underlying Observe should return nil") } } type mockCloseableObserver struct { closed bool } func (m *mockCloseableObserver) Observe(_ context.Context, _ []observer.Event, _ ...observer.Option) error { return nil } func (m *mockCloseableObserver) Close() error { m.closed = true return nil } func TestObserverRegistry_WrapperClose(t *testing.T) { r := new(observerRegistry) mock := &mockCloseableObserver{} r.Register("o", mock) w := r.Get("o") if err := w.(interface{ Close() error }).Close(); err != nil { t.Fatal(err) } if !mock.closed { t.Fatal("wrapper Close did not close underlying") } } func TestObserverRegistry_WrapperCloseNilUnderlying(t *testing.T) { r := new(observerRegistry) w := r.Get("ghost") if err := w.(interface{ Close() error }).Close(); err != nil { t.Fatalf("Close on nil underlying: %v", err) } } // --- Recorder wrapper tests --- type mockRecorder struct { err error } func (m *mockRecorder) Record(_ context.Context, _ []byte, _ ...recorder.RecordOption) error { return m.err } func TestRecorderRegistry_Delegate(t *testing.T) { r := new(recorderRegistry) r.Register("r", &mockRecorder{}) w := r.Get("r") if err := w.Record(context.Background(), []byte("data")); err != nil { t.Fatalf("Record = %v", err) } } func TestRecorderRegistry_NilUnderlying(t *testing.T) { r := new(recorderRegistry) w := r.Get("ghost") if err := w.Record(context.Background(), []byte("x")); err != nil { t.Fatal("nil underlying Record should return nil") } } // --- Resolver wrapper tests --- type mockResolver struct { ips []net.IP err error } func (m *mockResolver) Resolve(_ context.Context, _, _ string, _ ...resolver.Option) ([]net.IP, error) { return m.ips, m.err } func TestResolverRegistry_Delegate(t *testing.T) { r := new(resolverRegistry) ips := []net.IP{net.ParseIP("1.2.3.4")} r.Register("r", &mockResolver{ips: ips}) w := r.Get("r") result, err := w.Resolve(context.Background(), "tcp", "example.com") if err != nil { t.Fatal(err) } if len(result) != 1 { t.Fatalf("Resolve = %v", result) } } func TestResolverRegistry_NilUnderlying(t *testing.T) { r := new(resolverRegistry) w := r.Get("ghost") ips, err := w.Resolve(context.Background(), "tcp", "x") if !errors.Is(err, resolver.ErrInvalid) { t.Fatalf("expected resolver.ErrInvalid, got %v", err) } if ips != nil { t.Fatal("should return nil IPs") } } // --- Router wrapper tests --- type mockRouter struct { route *router.Route } func (m *mockRouter) GetRoute(_ context.Context, _ string, _ ...router.Option) *router.Route { return m.route } func TestRouterRegistry_Delegate(t *testing.T) { r := new(routerRegistry) r.Register("r", &mockRouter{}) w := r.Get("r") if w.GetRoute(context.Background(), "1.2.3.4") != nil { t.Fatal("mock returns nil route") } } func TestRouterRegistry_NilUnderlying(t *testing.T) { r := new(routerRegistry) w := r.Get("ghost") if w.GetRoute(context.Background(), "x") != nil { t.Fatal("nil underlying should return nil") } } // --- SD wrapper tests --- type mockSD struct { services []*sd.Service err error } func (m *mockSD) Register(_ context.Context, _ *sd.Service, _ ...sd.Option) error { return m.err } func (m *mockSD) Deregister(_ context.Context, _ *sd.Service) error { return m.err } func (m *mockSD) Renew(_ context.Context, _ *sd.Service) error { return m.err } func (m *mockSD) Get(_ context.Context, _ string) ([]*sd.Service, error) { return m.services, m.err } func TestSDRegistry_Delegate(t *testing.T) { r := new(sdRegistry) svcs := []*sd.Service{{Name: "test"}} r.Register("s", &mockSD{services: svcs}) w := r.Get("s") if err := w.Register(context.Background(), &sd.Service{Name: "x"}); err != nil { t.Fatal(err) } if err := w.Deregister(context.Background(), &sd.Service{}); err != nil { t.Fatal(err) } if err := w.Renew(context.Background(), &sd.Service{}); err != nil { t.Fatal(err) } result, err := w.Get(context.Background(), "test") if err != nil || len(result) != 1 { t.Fatalf("Get = (%v, %v)", result, err) } } func TestSDRegistry_NilUnderlying(t *testing.T) { r := new(sdRegistry) w := r.Get("ghost") if err := w.Register(context.Background(), nil); err != nil { t.Fatal("nil Register should return nil") } if err := w.Deregister(context.Background(), nil); err != nil { t.Fatal("nil Deregister should return nil") } if err := w.Renew(context.Background(), nil); err != nil { t.Fatal("nil Renew should return nil") } if svcs, err := w.Get(context.Background(), "x"); err != nil || svcs != nil { t.Fatalf("nil Get = (%v, %v)", svcs, err) } } // --- Logger registry tests --- type mockLogger struct{} func (m *mockLogger) Trace(_ ...any) {} func (m *mockLogger) Tracef(_ string, _ ...any) {} func (m *mockLogger) Debug(_ ...any) {} func (m *mockLogger) Debugf(_ string, _ ...any) {} func (m *mockLogger) Info(_ ...any) {} func (m *mockLogger) Infof(_ string, _ ...any) {} func (m *mockLogger) Warn(_ ...any) {} func (m *mockLogger) Warnf(_ string, _ ...any) {} func (m *mockLogger) Error(_ ...any) {} func (m *mockLogger) Errorf(_ string, _ ...any) {} func (m *mockLogger) Fatal(_ ...any) {} func (m *mockLogger) Fatalf(_ string, _ ...any) {} func (m *mockLogger) GetLevel() logger.LogLevel { return logger.DebugLevel } func (m *mockLogger) IsLevelEnabled(_ logger.LogLevel) bool { return true } func (m *mockLogger) WithFields(_ map[string]any) logger.Logger { return m } func TestLoggerRegistry_RegisterGet(t *testing.T) { r := new(loggerRegistry) l := &mockLogger{} r.Register("log", l) if v := r.Get("log"); v == nil { t.Fatal("Get returned nil") } } func TestLoggerRegistry_GetEmpty(t *testing.T) { r := new(loggerRegistry) if v := r.Get(""); v != nil { t.Fatal("empty name should return nil logger") } } // --- Service registry tests --- func TestServiceRegistry_RegisterGet(t *testing.T) { r := new(serviceRegistry) svc := &closeableService{} r.Register("svc", svc) if v := r.Get("svc"); v == nil { t.Fatal("Get returned nil") } } // --- Factory registry tests (listener, handler, dialer, connector) --- // These call logger.Fatal on duplicate, so we only test the non-duplicate path. func TestListenerRegistry_Register(t *testing.T) { r := new(listenerRegistry) factory := func(_ ...listener.Option) listener.Listener { return nil } if err := r.Register("tcp", factory); err != nil { t.Fatal(err) } if !r.IsRegistered("tcp") { t.Fatal("should be registered") } } func TestHandlerRegistry_Register(t *testing.T) { r := new(handlerRegistry) factory := func(_ ...handler.Option) handler.Handler { return nil } if err := r.Register("http", factory); err != nil { t.Fatal(err) } if !r.IsRegistered("http") { t.Fatal("should be registered") } } func TestDialerRegistry_Register(t *testing.T) { r := new(dialerRegistry) factory := func(_ ...dialer.Option) dialer.Dialer { return nil } if err := r.Register("tcp", factory); err != nil { t.Fatal(err) } if !r.IsRegistered("tcp") { t.Fatal("should be registered") } } func TestConnectorRegistry_Register(t *testing.T) { r := new(connectorRegistry) factory := func(_ ...connector.Option) connector.Connector { return nil } if err := r.Register("http", factory); err != nil { t.Fatal(err) } if !r.IsRegistered("http") { t.Fatal("should be registered") } } // --- Interface satisfaction checks --- var _ reg.Registry[NewListener] = (*listenerRegistry)(nil) var _ reg.Registry[NewHandler] = (*handlerRegistry)(nil) var _ reg.Registry[NewDialer] = (*dialerRegistry)(nil) var _ reg.Registry[NewConnector] = (*connectorRegistry)(nil) var _ reg.Registry[service.Service] = (*serviceRegistry)(nil) var _ reg.Registry[chain.Chainer] = (*chainRegistry)(nil) var _ reg.Registry[hop.Hop] = (*hopRegistry)(nil) var _ reg.Registry[auth.Authenticator] = (*autherRegistry)(nil) var _ reg.Registry[admission.Admission] = (*admissionRegistry)(nil) var _ reg.Registry[bypass.Bypass] = (*bypassRegistry)(nil) var _ reg.Registry[resolver.Resolver] = (*resolverRegistry)(nil) var _ reg.Registry[hosts.HostMapper] = (*hostsRegistry)(nil) var _ reg.Registry[recorder.Recorder] = (*recorderRegistry)(nil) var _ reg.Registry[traffic.TrafficLimiter] = (*trafficLimiterRegistry)(nil) var _ reg.Registry[conn.ConnLimiter] = (*connLimiterRegistry)(nil) var _ reg.Registry[rate.RateLimiter] = (*rateLimiterRegistry)(nil) var _ reg.Registry[ingress.Ingress] = (*ingressRegistry)(nil) var _ reg.Registry[router.Router] = (*routerRegistry)(nil) var _ reg.Registry[sd.SD] = (*sdRegistry)(nil) var _ reg.Registry[observer.Observer] = (*observerRegistry)(nil) var _ reg.Registry[logger.Logger] = (*loggerRegistry)(nil)