lolly/internal/matcher/integration_test.go
xfy 53eaec57ad feat(matcher): 添加 nginx 风格 location 匹配引擎
实现 nginx 兼容的 location 匹配系统,支持:
- 精确匹配 (=) - Hash Map O(1)
- 前缀优先匹配 (^~) - Radix Tree
- 正则匹配 (~, ~*) - 按配置顺序
- 普通前缀匹配 - Radix Tree 最长匹配
- 命名 location (@name) - 内部重定向

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 09:26:22 +08:00

111 lines
2.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package matcher
import (
"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) // priority 1
engine.AddPrefixPriority("/api/", handler) // priority 2 (^~)
engine.AddRegex(`\.php$`, handler, false) // priority 3
engine.AddPrefix("/", handler) // 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" {
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)
engine.AddRegex(`\.php$`, handler, false)
engine.AddPrefix("/", handler)
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)
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<id>[0-9]+)$`, handler, 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)
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)
err := engine.AddExact("/api", handler)
if err == nil {
t.Error("should fail on path conflict")
}
}