package middleware import ( "testing" ) func TestNormalizeEndpoint(t *testing.T) { testCases := []struct { name string input string expected string }{ { name: "Simple path", input: "/api/users", expected: "/api/users", }, { name: "Path with UUID", input: "/api/users/abcdef12345", expected: "/api/users/{id}", }, { name: "Path with UUID and trailing slash", input: "/api/users/abcdef12345/", expected: "/api/users/{id}/", }, { name: "Path with UUID in middle", input: "/api/users/abcdef12345/profile", expected: "/api/users/{id}/profile", }, { name: "Path with query params", input: "/api/users?page=1&limit=10", expected: "/api/users", }, { name: "Path with UUID and query params", input: "/api/users/abcdef12345?detail=full", expected: "/api/users/abcdef12345", // Query params removed first, then UUID not matched }, { name: "Multiple UUIDs", input: "/api/users/abc12345678/posts/def87654321", expected: "/api/users/{id}/posts/{id}", }, { name: "Root path", input: "/", expected: "/", }, { name: "Empty path", input: "", expected: "", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result := normalizeEndpoint(tc.input) if result != tc.expected { t.Errorf("Expected '%s', got '%s'", tc.expected, result) } }) } } func TestNormalizeEndpointUUIDFormats(t *testing.T) { uuidFormats := []string{ "abcdef12345", "ABCDEF12345", "abc_def1234", "abc-def1234", "mixedCase12", } for _, uuid := range uuidFormats { t.Run(uuid, func(t *testing.T) { input := "/api/users/" + uuid result := normalizeEndpoint(input) expected := "/api/users/{id}" if result != expected { t.Errorf("Expected '%s', got '%s' for UUID format '%s'", expected, result, uuid) } }) } } func TestNormalizeEndpointComplexQueryStrings(t *testing.T) { testCases := []struct { input string expected string }{ {"/api/users?a=1&b=2&c=3", "/api/users"}, {"/api/users?filter=active&sort=name&order=asc", "/api/users"}, {"/api/users?search=john+doe", "/api/users"}, {"/api/users?tags[]=tag1&tags[]=tag2", "/api/users"}, } for _, tc := range testCases { result := normalizeEndpoint(tc.input) if result != tc.expected { t.Errorf("Input '%s': expected '%s', got '%s'", tc.input, tc.expected, result) } } } func TestNormalizeEndpointEdgeCases(t *testing.T) { testCases := []struct { name string input string expected string }{ { name: "Just query string", input: "?param=value", expected: "", }, { name: "Double slashes", input: "/api//users", expected: "/api//users", }, { name: "Trailing query without params", input: "/api/users?", expected: "/api/users", }, { name: "UUID at end without slash", input: "/users/abc12345678", expected: "/users/{id}", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result := normalizeEndpoint(tc.input) if result != tc.expected { t.Errorf("Expected '%s', got '%s'", tc.expected, result) } }) } } func TestNormalizeEndpointPreservesNonUUID(t *testing.T) { testCases := []string{ "/api/users/all", "/api/users/active", "/api/sessions/current", "/health", "/metrics", } for _, input := range testCases { t.Run(input, func(t *testing.T) { result := normalizeEndpoint(input) if result != input { t.Errorf("Non-UUID path should be preserved. Input: '%s', got '%s'", input, result) } }) } } func TestNormalizeEndpointUUIDLength(t *testing.T) { // UUIDs must be exactly 11 characters testCases := []struct { name string uuid string shouldNormalize bool }{ {"10 chars", "abcdefghij", false}, {"11 chars", "abcdefghijk", true}, {"12 chars", "abcdefghijkl", false}, {"5 chars", "abcde", false}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { input := "/api/users/" + tc.uuid result := normalizeEndpoint(input) if tc.shouldNormalize { expected := "/api/users/{id}" if result != expected { t.Errorf("Expected '%s', got '%s'", expected, result) } } else { if result != input { t.Errorf("Should not normalize, expected '%s', got '%s'", input, result) } } }) } } func BenchmarkNormalizeEndpoint(b *testing.B) { testPaths := []string{ "/api/users", "/api/users/abc12345678", "/api/users/abc12345678/profile", "/api/users?page=1&limit=10", } b.ResetTimer() for i := 0; i < b.N; i++ { for _, path := range testPaths { normalizeEndpoint(path) } } }