package sux import ( "net/http" "net/http/httptest" "testing" ) func TestBasicRouting(t *testing.T) { router := New() router.GET("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("home")) }) router.GET("/about", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("about")) }) // Test home route req := httptest.NewRequest("GET", "/", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status 200, got %d", w.Code) } if w.Body.String() != "home" { t.Errorf("Expected body 'home', got '%s'", w.Body.String()) } // Test about route req = httptest.NewRequest("GET", "/about", nil) w = httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status 200, got %d", w.Code) } if w.Body.String() != "about" { t.Errorf("Expected body 'about', got '%s'", w.Body.String()) } } func TestParameterRouting(t *testing.T) { router := New() router.GET("/users/:id", func(w http.ResponseWriter, r *http.Request) { params := ParamsFromContext(r) if params.Len() == 0 { w.WriteHeader(http.StatusInternalServerError) return } id := params.Get("id") w.WriteHeader(http.StatusOK) w.Write([]byte("user " + id)) }) // Test parameter route req := httptest.NewRequest("GET", "/users/123", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status 200, got %d", w.Code) } if w.Body.String() != "user 123" { t.Errorf("Expected body 'user 123', got '%s'", w.Body.String()) } } func TestWildcardRouting(t *testing.T) { router := New() router.GET("/files/*path", func(w http.ResponseWriter, r *http.Request) { params := ParamsFromContext(r) if params.Len() == 0 { w.WriteHeader(http.StatusInternalServerError) return } path := params.Get("path") w.WriteHeader(http.StatusOK) w.Write([]byte("file: " + path)) }) // Test wildcard route req := httptest.NewRequest("GET", "/files/docs/readme.txt", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status 200, got %d", w.Code) } if w.Body.String() != "file: docs/readme.txt" { t.Errorf("Expected body 'file: docs/readme.txt', got '%s'", w.Body.String()) } } func TestMethodNotAllowed(t *testing.T) { router := New() router.GET("/resource", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("GET resource")) }) // Test POST to GET-only route req := httptest.NewRequest("POST", "/resource", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusMethodNotAllowed { t.Errorf("Expected status 405, got %d", w.Code) } } func TestNotFound(t *testing.T) { router := New() router.GET("/existing", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) // Test non-existent route req := httptest.NewRequest("GET", "/nonexistent", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusNotFound { t.Errorf("Expected status 404, got %d", w.Code) } } func TestMiddleware(t *testing.T) { router := New() // Add middleware that sets a header router.Use(func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Middleware", "applied") next.ServeHTTP(w, r) }) }) router.GET("/test", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("test")) }) req := httptest.NewRequest("GET", "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status 200, got %d", w.Code) } if w.Header().Get("X-Middleware") != "applied" { t.Errorf("Expected middleware header, got '%s'", w.Header().Get("X-Middleware")) } } func TestRouteGroups(t *testing.T) { router := New() // Create API group with middleware api := router.Group("/api", func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-API-Version", "v1") next.ServeHTTP(w, r) }) }) api.GET("/users", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("users list")) }) // Test grouped route req := httptest.NewRequest("GET", "/api/users", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status 200, got %d", w.Code) } if w.Body.String() != "users list" { t.Errorf("Expected body 'users list', got '%s'", w.Body.String()) } if w.Header().Get("X-API-Version") != "v1" { t.Errorf("Expected API version header, got '%s'", w.Header().Get("X-API-Version")) } } func TestCustomHandlers(t *testing.T) { router := New() // Custom 404 handler router.NotFound(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) w.Write([]byte("custom 404")) }) // Custom 405 handler router.MethodNotAllowed(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusMethodNotAllowed) w.Write([]byte("custom 405")) }) router.GET("/existing", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) // Test custom 404 req := httptest.NewRequest("GET", "/nonexistent", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusNotFound { t.Errorf("Expected status 404, got %d", w.Code) } if w.Body.String() != "custom 404" { t.Errorf("Expected body 'custom 404', got '%s'", w.Body.String()) } // Test custom 405 req = httptest.NewRequest("POST", "/existing", nil) w = httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusMethodNotAllowed { t.Errorf("Expected status 405, got %d", w.Code) } if w.Body.String() != "custom 405" { t.Errorf("Expected body 'custom 405', got '%s'", w.Body.String()) } } func TestMultipleParameters(t *testing.T) { router := New() router.GET("/users/:userId/posts/:postId", func(w http.ResponseWriter, r *http.Request) { params := ParamsFromContext(r) if params.Len() == 0 { w.WriteHeader(http.StatusInternalServerError) return } userId := params.Get("userId") postId := params.Get("postId") w.WriteHeader(http.StatusOK) w.Write([]byte("user " + userId + " post " + postId)) }) req := httptest.NewRequest("GET", "/users/123/posts/456", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status 200, got %d", w.Code) } if w.Body.String() != "user 123 post 456" { t.Errorf("Expected body 'user 123 post 456', got '%s'", w.Body.String()) } } func TestThreadSafety(t *testing.T) { router := New() router.GET("/test", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("test")) }) // Create multiple goroutines to test concurrent access done := make(chan bool, 10) for i := 0; i < 10; i++ { go func() { for j := 0; j < 100; j++ { req := httptest.NewRequest("GET", "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status 200, got %d", w.Code) } } done <- true }() } // Wait for all goroutines to complete for i := 0; i < 10; i++ { <-done } } func TestRouterInstanceIsolation(t *testing.T) { // Create two separate router instances router1 := New() router2 := New() router1.GET("/route", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("router1")) }) router2.GET("/route", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("router2")) }) // Test router1 req := httptest.NewRequest("GET", "/route", nil) w := httptest.NewRecorder() router1.ServeHTTP(w, req) if w.Body.String() != "router1" { t.Errorf("Expected body 'router1', got '%s'", w.Body.String()) } // Test router2 w = httptest.NewRecorder() router2.ServeHTTP(w, req) if w.Body.String() != "router2" { t.Errorf("Expected body 'router2', got '%s'", w.Body.String()) } }