From 556d40ceb04b939556ea6b977ebaecc45ed8e843 Mon Sep 17 00:00:00 2001 From: xfy Date: Wed, 3 Jun 2026 11:47:06 +0800 Subject: [PATCH] fix(matcher,server): use ConflictError in AddNamed and add tests Make AddNamed return *ConflictError for consistency with other Add* methods so handleRegistrationError treats named location conflicts as warnings instead of fatal errors. Add tests for handleRegistrationError covering both conflict and fatal error paths. --- internal/matcher/location.go | 8 ++++++-- internal/server/server_test.go | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/internal/matcher/location.go b/internal/matcher/location.go index c42a210..1df4521 100644 --- a/internal/matcher/location.go +++ b/internal/matcher/location.go @@ -173,8 +173,12 @@ func (e *LocationEngine) AddNamed(name string, handler fasthttp.RequestHandler) return errors.New("LocationEngine already initialized") } - if existing, ok := e.namedMatchers[name]; ok { - return fmt.Errorf("named location '@%s' already registered", existing.name) + if _, ok := e.namedMatchers[name]; ok { + return &ConflictError{ + Path: "@" + name, + ExistingType: "named", + NewType: "named", + } } matcher := NewNamedMatcher(name, handler) diff --git a/internal/server/server_test.go b/internal/server/server_test.go index 2a4b76c..a68c82d 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -18,6 +18,7 @@ import ( "fmt" "net" "os" + "strings" "sync" "testing" "time" @@ -26,6 +27,7 @@ import ( "rua.plus/lolly/internal/config" "rua.plus/lolly/internal/loadbalance" "rua.plus/lolly/internal/lua" + "rua.plus/lolly/internal/matcher" "rua.plus/lolly/internal/middleware/accesslog" "rua.plus/lolly/internal/middleware/security" "rua.plus/lolly/internal/proxy" @@ -989,6 +991,27 @@ func TestCreateListener_UnixSocketCleanup(t *testing.T) { defer ln.Close() } +func TestHandleRegistrationError_ConflictWarning(t *testing.T) { + s := &Server{} + err := s.handleRegistrationError("proxy", "/api", + &matcher.ConflictError{Path: "/api", ExistingType: "exact", NewType: "prefix"}) + if err != nil { + t.Errorf("conflict should return nil, got: %v", err) + } +} + +func TestHandleRegistrationError_FatalError(t *testing.T) { + s := &Server{} + err := s.handleRegistrationError("proxy", "/api", + fmt.Errorf("invalid regex pattern: missing closing parenthesis")) + if err == nil { + t.Error("fatal error should return non-nil") + } + if !strings.Contains(err.Error(), "proxy route /api") { + t.Errorf("error should wrap context, got: %v", err) + } +} + func TestDupListener_TCP(t *testing.T) { ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil {