forked from aegis/pyserveX
go implementation
This commit is contained in:
@@ -0,0 +1,460 @@
|
||||
package pathmatcher
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// ============== MountedPath Tests ==============
|
||||
|
||||
func TestMountedPath_RootMountMatchesEverything(t *testing.T) {
|
||||
mount := NewMountedPath("")
|
||||
|
||||
tests := []string{"/", "/api", "/api/users", "/anything/at/all"}
|
||||
|
||||
for _, path := range tests {
|
||||
if !mount.Matches(path) {
|
||||
t.Errorf("Root mount should match %s", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountedPath_SlashRootMountMatchesEverything(t *testing.T) {
|
||||
mount := NewMountedPath("/")
|
||||
|
||||
tests := []string{"/", "/api", "/api/users"}
|
||||
|
||||
for _, path := range tests {
|
||||
if !mount.Matches(path) {
|
||||
t.Errorf("'/' mount should match %s", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountedPath_ExactPathMatch(t *testing.T) {
|
||||
mount := NewMountedPath("/api")
|
||||
|
||||
tests := []struct {
|
||||
path string
|
||||
expected bool
|
||||
}{
|
||||
{"/api", true},
|
||||
{"/api/", true},
|
||||
{"/api/users", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if got := mount.Matches(tt.path); got != tt.expected {
|
||||
t.Errorf("Matches(%s) = %v, want %v", tt.path, got, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountedPath_NoFalsePrefixMatch(t *testing.T) {
|
||||
mount := NewMountedPath("/api")
|
||||
|
||||
tests := []string{"/api-v2", "/api2", "/apiv2"}
|
||||
|
||||
for _, path := range tests {
|
||||
if mount.Matches(path) {
|
||||
t.Errorf("/api should not match %s", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountedPath_ShorterPathNoMatch(t *testing.T) {
|
||||
mount := NewMountedPath("/api/v1")
|
||||
|
||||
tests := []string{"/api", "/ap", "/"}
|
||||
|
||||
for _, path := range tests {
|
||||
if mount.Matches(path) {
|
||||
t.Errorf("/api/v1 should not match shorter path %s", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountedPath_TrailingSlashNormalized(t *testing.T) {
|
||||
mount1 := NewMountedPath("/api/")
|
||||
mount2 := NewMountedPath("/api")
|
||||
|
||||
if mount1.Path() != "/api" {
|
||||
t.Errorf("Expected path /api, got %s", mount1.Path())
|
||||
}
|
||||
|
||||
if mount2.Path() != "/api" {
|
||||
t.Errorf("Expected path /api, got %s", mount2.Path())
|
||||
}
|
||||
|
||||
if !mount1.Matches("/api/users") {
|
||||
t.Error("mount1 should match /api/users")
|
||||
}
|
||||
|
||||
if !mount2.Matches("/api/users") {
|
||||
t.Error("mount2 should match /api/users")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountedPath_GetModifiedPathStripsPrefix(t *testing.T) {
|
||||
mount := NewMountedPath("/api")
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"/api", "/"},
|
||||
{"/api/", "/"},
|
||||
{"/api/users", "/users"},
|
||||
{"/api/users/123", "/users/123"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if got := mount.GetModifiedPath(tt.input); got != tt.expected {
|
||||
t.Errorf("GetModifiedPath(%s) = %s, want %s", tt.input, got, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountedPath_GetModifiedPathNoStrip(t *testing.T) {
|
||||
mount := NewMountedPath("/api", WithStripPath(false))
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"/api/users", "/api/users"},
|
||||
{"/api", "/api"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if got := mount.GetModifiedPath(tt.input); got != tt.expected {
|
||||
t.Errorf("GetModifiedPath(%s) = %s, want %s", tt.input, got, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountedPath_RootMountModifiedPath(t *testing.T) {
|
||||
mount := NewMountedPath("")
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"/api/users", "/api/users"},
|
||||
{"/", "/"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if got := mount.GetModifiedPath(tt.input); got != tt.expected {
|
||||
t.Errorf("GetModifiedPath(%s) = %s, want %s", tt.input, got, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountedPath_NameProperty(t *testing.T) {
|
||||
mount1 := NewMountedPath("/api")
|
||||
mount2 := NewMountedPath("/api", WithName("API Mount"))
|
||||
|
||||
if mount1.Name() != "/api" {
|
||||
t.Errorf("Expected name /api, got %s", mount1.Name())
|
||||
}
|
||||
|
||||
if mount2.Name() != "API Mount" {
|
||||
t.Errorf("Expected name 'API Mount', got %s", mount2.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// ============== MountManager Tests ==============
|
||||
|
||||
func TestMountManager_EmptyManager(t *testing.T) {
|
||||
manager := NewMountManager()
|
||||
|
||||
if got := manager.GetMount("/api"); got != nil {
|
||||
t.Error("Empty manager should return nil")
|
||||
}
|
||||
|
||||
if got := manager.MountCount(); got != 0 {
|
||||
t.Errorf("Expected mount count 0, got %d", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountManager_AddMount(t *testing.T) {
|
||||
manager := NewMountManager()
|
||||
mount := NewMountedPath("/api")
|
||||
|
||||
manager.AddMount(mount)
|
||||
|
||||
if manager.MountCount() != 1 {
|
||||
t.Errorf("Expected mount count 1, got %d", manager.MountCount())
|
||||
}
|
||||
|
||||
if got := manager.GetMount("/api/users"); got != mount {
|
||||
t.Error("GetMount should return the added mount")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountManager_LongestPrefixMatching(t *testing.T) {
|
||||
manager := NewMountManager()
|
||||
|
||||
apiMount := NewMountedPath("/api", WithName("api"))
|
||||
apiV1Mount := NewMountedPath("/api/v1", WithName("api_v1"))
|
||||
apiV2Mount := NewMountedPath("/api/v2", WithName("api_v2"))
|
||||
|
||||
manager.AddMount(apiMount)
|
||||
manager.AddMount(apiV2Mount)
|
||||
manager.AddMount(apiV1Mount)
|
||||
|
||||
tests := []struct {
|
||||
path string
|
||||
expectedName string
|
||||
}{
|
||||
{"/api/v1/users", "api_v1"},
|
||||
{"/api/v2/items", "api_v2"},
|
||||
{"/api/v3/other", "api"},
|
||||
{"/api", "api"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
got := manager.GetMount(tt.path)
|
||||
if got == nil {
|
||||
t.Errorf("GetMount(%s) returned nil, want mount with name %s", tt.path, tt.expectedName)
|
||||
continue
|
||||
}
|
||||
if got.Name() != tt.expectedName {
|
||||
t.Errorf("GetMount(%s).Name() = %s, want %s", tt.path, got.Name(), tt.expectedName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountManager_RemoveMount(t *testing.T) {
|
||||
manager := NewMountManager()
|
||||
|
||||
manager.AddMount(NewMountedPath("/api"))
|
||||
manager.AddMount(NewMountedPath("/admin"))
|
||||
|
||||
if manager.MountCount() != 2 {
|
||||
t.Errorf("Expected mount count 2, got %d", manager.MountCount())
|
||||
}
|
||||
|
||||
result := manager.RemoveMount("/api")
|
||||
|
||||
if !result {
|
||||
t.Error("RemoveMount should return true")
|
||||
}
|
||||
|
||||
if manager.MountCount() != 1 {
|
||||
t.Errorf("Expected mount count 1, got %d", manager.MountCount())
|
||||
}
|
||||
|
||||
if manager.GetMount("/api/users") != nil {
|
||||
t.Error("GetMount(/api/users) should return nil after removal")
|
||||
}
|
||||
|
||||
if manager.GetMount("/admin/users") == nil {
|
||||
t.Error("GetMount(/admin/users) should still work")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountManager_RemoveNonexistentMount(t *testing.T) {
|
||||
manager := NewMountManager()
|
||||
|
||||
result := manager.RemoveMount("/api")
|
||||
|
||||
if result {
|
||||
t.Error("RemoveMount should return false for nonexistent mount")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountManager_ListMounts(t *testing.T) {
|
||||
manager := NewMountManager()
|
||||
|
||||
manager.AddMount(NewMountedPath("/api", WithName("API")))
|
||||
manager.AddMount(NewMountedPath("/admin", WithName("Admin")))
|
||||
|
||||
mounts := manager.ListMounts()
|
||||
|
||||
if len(mounts) != 2 {
|
||||
t.Errorf("Expected 2 mounts, got %d", len(mounts))
|
||||
}
|
||||
|
||||
for _, m := range mounts {
|
||||
if _, ok := m["path"]; !ok {
|
||||
t.Error("Mount should have 'path' key")
|
||||
}
|
||||
if _, ok := m["name"]; !ok {
|
||||
t.Error("Mount should have 'name' key")
|
||||
}
|
||||
if _, ok := m["strip_path"]; !ok {
|
||||
t.Error("Mount should have 'strip_path' key")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountManager_MountsReturnsCopy(t *testing.T) {
|
||||
manager := NewMountManager()
|
||||
manager.AddMount(NewMountedPath("/api"))
|
||||
|
||||
mounts1 := manager.Mounts()
|
||||
mounts2 := manager.Mounts()
|
||||
|
||||
if &mounts1[0] == &mounts2[0] {
|
||||
t.Error("Mounts() should return different slices")
|
||||
}
|
||||
}
|
||||
|
||||
// ============== Utility Functions Tests ==============
|
||||
|
||||
func TestPathMatchesPrefix_Basic(t *testing.T) {
|
||||
tests := []struct {
|
||||
path string
|
||||
prefix string
|
||||
expected bool
|
||||
}{
|
||||
{"/api/users", "/api", true},
|
||||
{"/api", "/api", true},
|
||||
{"/api-v2", "/api", false},
|
||||
{"/ap", "/api", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if got := PathMatchesPrefix(tt.path, tt.prefix); got != tt.expected {
|
||||
t.Errorf("PathMatchesPrefix(%s, %s) = %v, want %v", tt.path, tt.prefix, got, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathMatchesPrefix_Root(t *testing.T) {
|
||||
tests := []struct {
|
||||
path string
|
||||
prefix string
|
||||
expected bool
|
||||
}{
|
||||
{"/anything", "", true},
|
||||
{"/anything", "/", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if got := PathMatchesPrefix(tt.path, tt.prefix); got != tt.expected {
|
||||
t.Errorf("PathMatchesPrefix(%s, %s) = %v, want %v", tt.path, tt.prefix, got, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripPathPrefix_Basic(t *testing.T) {
|
||||
tests := []struct {
|
||||
path string
|
||||
prefix string
|
||||
expected string
|
||||
}{
|
||||
{"/api/users", "/api", "/users"},
|
||||
{"/api", "/api", "/"},
|
||||
{"/api/", "/api", "/"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if got := StripPathPrefix(tt.path, tt.prefix); got != tt.expected {
|
||||
t.Errorf("StripPathPrefix(%s, %s) = %s, want %s", tt.path, tt.prefix, got, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripPathPrefix_Root(t *testing.T) {
|
||||
tests := []struct {
|
||||
path string
|
||||
prefix string
|
||||
expected string
|
||||
}{
|
||||
{"/api/users", "", "/api/users"},
|
||||
{"/api/users", "/", "/api/users"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if got := StripPathPrefix(tt.path, tt.prefix); got != tt.expected {
|
||||
t.Errorf("StripPathPrefix(%s, %s) = %s, want %s", tt.path, tt.prefix, got, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchAndModifyPath_Combined(t *testing.T) {
|
||||
tests := []struct {
|
||||
path string
|
||||
prefix string
|
||||
stripPath bool
|
||||
wantMatches bool
|
||||
wantModified string
|
||||
}{
|
||||
{"/api/users", "/api", true, true, "/users"},
|
||||
{"/api", "/api", true, true, "/"},
|
||||
{"/other", "/api", true, false, ""},
|
||||
{"/api/users", "/api", false, true, "/api/users"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
matches, modified := MatchAndModifyPath(tt.path, tt.prefix, tt.stripPath)
|
||||
if matches != tt.wantMatches {
|
||||
t.Errorf("MatchAndModifyPath(%s, %s, %v) matches = %v, want %v",
|
||||
tt.path, tt.prefix, tt.stripPath, matches, tt.wantMatches)
|
||||
}
|
||||
if modified != tt.wantModified {
|
||||
t.Errorf("MatchAndModifyPath(%s, %s, %v) modified = %s, want %s",
|
||||
tt.path, tt.prefix, tt.stripPath, modified, tt.wantModified)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============== Performance Tests ==============
|
||||
|
||||
func TestPerformance_ManyMatches(t *testing.T) {
|
||||
mount := NewMountedPath("/api/v1/users")
|
||||
|
||||
for i := 0; i < 10000; i++ {
|
||||
if !mount.Matches("/api/v1/users/123/posts") {
|
||||
t.Fatal("Should match")
|
||||
}
|
||||
if mount.Matches("/other/path") {
|
||||
t.Fatal("Should not match")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPerformance_ManyMounts(t *testing.T) {
|
||||
manager := NewMountManager()
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
manager.AddMount(NewMountedPath("/api/v" + string(rune('0'+i%10)) + string(rune('0'+i/10))))
|
||||
}
|
||||
|
||||
if manager.MountCount() != 100 {
|
||||
t.Errorf("Expected 100 mounts, got %d", manager.MountCount())
|
||||
}
|
||||
}
|
||||
|
||||
// ============== Benchmarks ==============
|
||||
|
||||
func BenchmarkMountedPath_Matches(b *testing.B) {
|
||||
mount := NewMountedPath("/api/v1")
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
mount.Matches("/api/v1/users/123")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMountManager_GetMount(b *testing.B) {
|
||||
manager := NewMountManager()
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
manager.AddMount(NewMountedPath("/api/v" + string(rune('0'+i%10))))
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
manager.GetMount("/api/v5/users/123")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPathMatchesPrefix(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
PathMatchesPrefix("/api/v1/users/123", "/api/v1")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user