package matcher import ( "errors" "testing" "github.com/valyala/fasthttp" ) // nginx priority: exact(=) > prefix_priority(^~) > regex(~) > prefix func TestLocationEngine_NginxPriority(t *testing.T) { engine := NewLocationEngine() handler := func(ctx *fasthttp.RequestCtx) {} // 注册不同类型 engine.AddExact("/api", handler, false) // priority 1 engine.AddPrefixPriority("/api/", handler, false) // priority 2 (^~) engine.AddRegex(`\.php$`, handler, false, false) // priority 3 engine.AddPrefix("/", handler, false) // priority 4 engine.MarkInitialized() // 测试精确匹配优先 result := engine.Match("/api") if result.LocationType != "exact" { t.Errorf("expected exact, got %s", result.LocationType) } // 测试 ^~ 阻止正则 result = engine.Match("/api/test.php") if result.LocationType != "prefix_priority" { t.Errorf("^~ should block regex, got %s", result.LocationType) } } func TestLocationEngine_RegexMatch(t *testing.T) { engine := NewLocationEngine() handler := func(ctx *fasthttp.RequestCtx) {} engine.AddPrefixPriority("/api/", handler, false) engine.AddRegex(`\.php$`, handler, false, false) engine.AddPrefix("/", handler, false) engine.MarkInitialized() // 正则匹配(^~ 不匹配 /index.php) result := engine.Match("/index.php") if result.LocationType != "regex" { t.Errorf("expected regex for /index.php, got %s", result.LocationType) } } func TestLocationEngine_PrefixFallback(t *testing.T) { engine := NewLocationEngine() handler := func(ctx *fasthttp.RequestCtx) {} engine.AddPrefix("/", handler, false) engine.MarkInitialized() result := engine.Match("/any/path") if result == nil || result.LocationType != "prefix" { t.Errorf("expected prefix match, got %v", result) } } func TestLocationEngine_NoMatch(t *testing.T) { engine := NewLocationEngine() engine.MarkInitialized() result := engine.Match("/nonexistent") if result != nil { t.Errorf("expected nil for no match, got %+v", result) } } func TestLocationEngine_RegexCaptures(t *testing.T) { engine := NewLocationEngine() handler := func(ctx *fasthttp.RequestCtx) {} engine.AddRegex(`^/user/(?P[0-9]+)$`, handler, false, false) engine.MarkInitialized() result := engine.Match("/user/42") if result.LocationType != "regex" { t.Errorf("expected regex, got %s", result.LocationType) } if result.Captures == nil || result.Captures["id"] != "42" { t.Errorf("expected captures id=42, got %v", result.Captures) } } func TestLocationEngine_Initialized_Twice(t *testing.T) { engine := NewLocationEngine() handler := func(ctx *fasthttp.RequestCtx) {} engine.MarkInitialized() err := engine.AddExact("/api", handler, false) if err == nil { t.Error("should fail when adding after initialized") } } func TestLocationEngine_PathConflict(t *testing.T) { engine := NewLocationEngine() handler := func(ctx *fasthttp.RequestCtx) {} engine.AddExact("/api", handler, false) err := engine.AddExact("/api", handler, false) if err == nil { t.Error("should fail on path conflict") } var ce *ConflictError if !errors.As(err, &ce) { t.Errorf("expected *ConflictError, got %T: %v", err, err) } }