forked from aegis/pyserveX
376 lines
9.1 KiB
Go
376 lines
9.1 KiB
Go
package routing
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
// ============== Router Initialization Tests ==============
|
|
|
|
func TestRouter_Initialization(t *testing.T) {
|
|
router := NewRouter()
|
|
|
|
if router.StaticDir() != "./static" {
|
|
t.Errorf("Expected static dir ./static, got %s", router.StaticDir())
|
|
}
|
|
|
|
if len(router.Routes()) != 0 {
|
|
t.Error("Expected empty routes")
|
|
}
|
|
|
|
if len(router.ExactRoutes()) != 0 {
|
|
t.Error("Expected empty exact routes")
|
|
}
|
|
|
|
if router.DefaultRoute() != nil {
|
|
t.Error("Expected nil default route")
|
|
}
|
|
}
|
|
|
|
func TestRouter_CustomStaticDir(t *testing.T) {
|
|
router := NewRouter(WithStaticDir("/custom/path"))
|
|
|
|
if router.StaticDir() != "/custom/path" {
|
|
t.Errorf("Expected static dir /custom/path, got %s", router.StaticDir())
|
|
}
|
|
}
|
|
|
|
// ============== Route Adding Tests ==============
|
|
|
|
func TestRouter_AddExactRoute(t *testing.T) {
|
|
router := NewRouter()
|
|
config := map[string]interface{}{"return": "200 OK"}
|
|
|
|
router.AddRoute("=/health", config)
|
|
|
|
exactRoutes := router.ExactRoutes()
|
|
if _, ok := exactRoutes["/health"]; !ok {
|
|
t.Error("Expected /health in exact routes")
|
|
}
|
|
}
|
|
|
|
func TestRouter_AddDefaultRoute(t *testing.T) {
|
|
router := NewRouter()
|
|
config := map[string]interface{}{"spa_fallback": true, "root": "./static"}
|
|
|
|
router.AddRoute("__default__", config)
|
|
|
|
if router.DefaultRoute() == nil {
|
|
t.Error("Expected default route to be set")
|
|
}
|
|
}
|
|
|
|
func TestRouter_AddRegexRoute(t *testing.T) {
|
|
router := NewRouter()
|
|
config := map[string]interface{}{"root": "./static"}
|
|
|
|
router.AddRoute("~^/api/", config)
|
|
|
|
if len(router.Routes()) != 1 {
|
|
t.Errorf("Expected 1 regex route, got %d", len(router.Routes()))
|
|
}
|
|
}
|
|
|
|
func TestRouter_AddCaseInsensitiveRegexRoute(t *testing.T) {
|
|
router := NewRouter()
|
|
config := map[string]interface{}{"root": "./static", "cache_control": "public, max-age=3600"}
|
|
|
|
router.AddRoute("~*\\.(css|js)$", config)
|
|
|
|
if len(router.Routes()) != 1 {
|
|
t.Errorf("Expected 1 regex route, got %d", len(router.Routes()))
|
|
}
|
|
|
|
if router.Routes()[0].CaseSensitive {
|
|
t.Error("Expected case-insensitive route")
|
|
}
|
|
}
|
|
|
|
func TestRouter_InvalidRegexPattern(t *testing.T) {
|
|
router := NewRouter()
|
|
config := map[string]interface{}{"root": "./static"}
|
|
|
|
// Invalid regex - unmatched bracket
|
|
router.AddRoute("~^/api/[invalid", config)
|
|
|
|
// Should not add invalid pattern
|
|
if len(router.Routes()) != 0 {
|
|
t.Error("Should not add invalid regex pattern")
|
|
}
|
|
}
|
|
|
|
// ============== Route Matching Tests ==============
|
|
|
|
func TestRouter_MatchExactRoute(t *testing.T) {
|
|
router := NewRouter()
|
|
config := map[string]interface{}{"return": "200 OK"}
|
|
router.AddRoute("=/health", config)
|
|
|
|
match := router.Match("/health")
|
|
|
|
if match == nil {
|
|
t.Fatal("Expected match for /health")
|
|
}
|
|
|
|
if match.Config["return"] != "200 OK" {
|
|
t.Error("Expected return config")
|
|
}
|
|
|
|
if len(match.Params) != 0 {
|
|
t.Error("Expected empty params for exact match")
|
|
}
|
|
}
|
|
|
|
func TestRouter_MatchExactRouteNoMatch(t *testing.T) {
|
|
router := NewRouter()
|
|
config := map[string]interface{}{"return": "200 OK"}
|
|
router.AddRoute("=/health", config)
|
|
|
|
match := router.Match("/healthcheck")
|
|
|
|
if match != nil {
|
|
t.Error("Exact route should not match /healthcheck")
|
|
}
|
|
}
|
|
|
|
func TestRouter_MatchRegexRoute(t *testing.T) {
|
|
router := NewRouter()
|
|
config := map[string]interface{}{"proxy_pass": "http://localhost:9001"}
|
|
router.AddRoute("~^/api/v\\d+/", config)
|
|
|
|
match := router.Match("/api/v1/users")
|
|
|
|
if match == nil {
|
|
t.Fatal("Expected match for /api/v1/users")
|
|
}
|
|
|
|
if match.Config["proxy_pass"] != "http://localhost:9001" {
|
|
t.Error("Expected proxy_pass config")
|
|
}
|
|
}
|
|
|
|
func TestRouter_MatchRegexRouteWithGroups(t *testing.T) {
|
|
router := NewRouter()
|
|
config := map[string]interface{}{"proxy_pass": "http://localhost:9001"}
|
|
router.AddRoute("~^/api/v(?P<version>\\d+)/", config)
|
|
|
|
match := router.Match("/api/v2/data")
|
|
|
|
if match == nil {
|
|
t.Fatal("Expected match for /api/v2/data")
|
|
}
|
|
|
|
if match.Params["version"] != "2" {
|
|
t.Errorf("Expected version=2, got %s", match.Params["version"])
|
|
}
|
|
}
|
|
|
|
func TestRouter_MatchCaseInsensitiveRegex(t *testing.T) {
|
|
router := NewRouter()
|
|
config := map[string]interface{}{"root": "./static", "cache_control": "public, max-age=3600"}
|
|
router.AddRoute("~*\\.(CSS|JS)$", config)
|
|
|
|
// Should match lowercase
|
|
match1 := router.Match("/styles/main.css")
|
|
if match1 == nil {
|
|
t.Error("Should match lowercase .css")
|
|
}
|
|
|
|
// Should match uppercase
|
|
match2 := router.Match("/scripts/app.JS")
|
|
if match2 == nil {
|
|
t.Error("Should match uppercase .JS")
|
|
}
|
|
}
|
|
|
|
func TestRouter_MatchCaseSensitiveRegex(t *testing.T) {
|
|
router := NewRouter()
|
|
config := map[string]interface{}{"root": "./static"}
|
|
router.AddRoute("~\\.(css)$", config)
|
|
|
|
// Should match lowercase
|
|
match1 := router.Match("/styles/main.css")
|
|
if match1 == nil {
|
|
t.Error("Should match lowercase .css")
|
|
}
|
|
|
|
// Should NOT match uppercase
|
|
match2 := router.Match("/styles/main.CSS")
|
|
if match2 != nil {
|
|
t.Error("Should not match uppercase .CSS for case-sensitive regex")
|
|
}
|
|
}
|
|
|
|
func TestRouter_MatchDefaultRoute(t *testing.T) {
|
|
router := NewRouter()
|
|
router.AddRoute("=/health", map[string]interface{}{"return": "200 OK"})
|
|
router.AddRoute("__default__", map[string]interface{}{"spa_fallback": true})
|
|
|
|
match := router.Match("/unknown/path")
|
|
|
|
if match == nil {
|
|
t.Fatal("Expected default route match")
|
|
}
|
|
|
|
if match.Config["spa_fallback"] != true {
|
|
t.Error("Expected spa_fallback config from default route")
|
|
}
|
|
}
|
|
|
|
// ============== Priority Tests ==============
|
|
|
|
func TestRouter_PriorityExactOverRegex(t *testing.T) {
|
|
router := NewRouter()
|
|
router.AddRoute("=/api/status", map[string]interface{}{"return": "200 Exact"})
|
|
router.AddRoute("~^/api/", map[string]interface{}{"proxy_pass": "http://localhost:9001"})
|
|
|
|
match := router.Match("/api/status")
|
|
|
|
if match == nil {
|
|
t.Fatal("Expected match")
|
|
}
|
|
|
|
if match.Config["return"] != "200 Exact" {
|
|
t.Error("Exact match should have priority over regex")
|
|
}
|
|
}
|
|
|
|
func TestRouter_PriorityRegexOverDefault(t *testing.T) {
|
|
router := NewRouter()
|
|
router.AddRoute("~^/api/", map[string]interface{}{"proxy_pass": "http://localhost:9001"})
|
|
router.AddRoute("__default__", map[string]interface{}{"spa_fallback": true})
|
|
|
|
match := router.Match("/api/v1/users")
|
|
|
|
if match == nil {
|
|
t.Fatal("Expected match")
|
|
}
|
|
|
|
if match.Config["proxy_pass"] != "http://localhost:9001" {
|
|
t.Error("Regex match should have priority over default")
|
|
}
|
|
}
|
|
|
|
// ============== CreateRouterFromConfig Tests ==============
|
|
|
|
func TestCreateRouterFromConfig(t *testing.T) {
|
|
config := map[string]interface{}{
|
|
"regex_locations": map[string]interface{}{
|
|
"=/health": map[string]interface{}{
|
|
"return": "200 OK",
|
|
"content_type": "text/plain",
|
|
},
|
|
"~^/api/": map[string]interface{}{
|
|
"proxy_pass": "http://localhost:9001",
|
|
},
|
|
"__default__": map[string]interface{}{
|
|
"spa_fallback": true,
|
|
"root": "./static",
|
|
},
|
|
},
|
|
}
|
|
|
|
router := CreateRouterFromConfig(config)
|
|
|
|
// Check exact route
|
|
if _, ok := router.ExactRoutes()["/health"]; !ok {
|
|
t.Error("Expected /health exact route")
|
|
}
|
|
|
|
// Check regex route
|
|
if len(router.Routes()) != 1 {
|
|
t.Errorf("Expected 1 regex route, got %d", len(router.Routes()))
|
|
}
|
|
|
|
// Check default route
|
|
if router.DefaultRoute() == nil {
|
|
t.Error("Expected default route")
|
|
}
|
|
}
|
|
|
|
// ============== Static Dir Path Tests ==============
|
|
|
|
func TestRouter_StaticDirPath(t *testing.T) {
|
|
router := NewRouter(WithStaticDir("/var/www/html"))
|
|
|
|
expected, _ := filepath.Abs("/var/www/html")
|
|
actual, _ := filepath.Abs(router.StaticDir())
|
|
|
|
if actual != expected {
|
|
t.Errorf("Expected static dir %s, got %s", expected, actual)
|
|
}
|
|
}
|
|
|
|
// ============== Concurrent Access Tests ==============
|
|
|
|
func TestRouter_ConcurrentAccess(t *testing.T) {
|
|
router := NewRouter()
|
|
|
|
// Add routes concurrently
|
|
done := make(chan bool, 10)
|
|
|
|
for i := 0; i < 10; i++ {
|
|
go func(n int) {
|
|
router.AddRoute("~^/api/v"+string(rune('0'+n))+"/", map[string]interface{}{
|
|
"proxy_pass": "http://localhost:900" + string(rune('0'+n)),
|
|
})
|
|
done <- true
|
|
}(i)
|
|
}
|
|
|
|
// Wait for all goroutines
|
|
for i := 0; i < 10; i++ {
|
|
<-done
|
|
}
|
|
|
|
// Match routes concurrently
|
|
for i := 0; i < 10; i++ {
|
|
go func(n int) {
|
|
router.Match("/api/v" + string(rune('0'+n)) + "/users")
|
|
done <- true
|
|
}(i)
|
|
}
|
|
|
|
for i := 0; i < 10; i++ {
|
|
<-done
|
|
}
|
|
}
|
|
|
|
// ============== Benchmarks ==============
|
|
|
|
func BenchmarkRouter_MatchExact(b *testing.B) {
|
|
router := NewRouter()
|
|
router.AddRoute("=/health", map[string]interface{}{"return": "200 OK"})
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
router.Match("/health")
|
|
}
|
|
}
|
|
|
|
func BenchmarkRouter_MatchRegex(b *testing.B) {
|
|
router := NewRouter()
|
|
router.AddRoute("~^/api/v(?P<version>\\d+)/", map[string]interface{}{"proxy_pass": "http://localhost:9001"})
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
router.Match("/api/v1/users/123")
|
|
}
|
|
}
|
|
|
|
func BenchmarkRouter_MatchWithManyRoutes(b *testing.B) {
|
|
router := NewRouter()
|
|
|
|
// Add many routes
|
|
for i := 0; i < 50; i++ {
|
|
router.AddRoute("~^/api/v"+string(rune('0'+i%10))+"/service"+string(rune('0'+i/10))+"/",
|
|
map[string]interface{}{"proxy_pass": "http://localhost:9001"})
|
|
}
|
|
router.AddRoute("__default__", map[string]interface{}{"spa_fallback": true})
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
router.Match("/api/v5/service3/users/123")
|
|
}
|
|
}
|