package main import ( "net/http" "net/http/httptest" "testing" "github.com/gin-gonic/gin" "git.gobha.me/xcaliber/chat-switchboard/config" "git.gobha.me/xcaliber/chat-switchboard/handlers" "git.gobha.me/xcaliber/chat-switchboard/middleware" ) func init() { gin.SetMode(gin.TestMode) } func TestHealthEndpoint(t *testing.T) { r := gin.New() r.GET("/health", func(c *gin.Context) { c.JSON(200, gin.H{"status": "ok", "version": "test", "database": false}) }) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/health", nil) r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status %d, got %d", http.StatusOK, w.Code) } } func TestCORSHeaders(t *testing.T) { r := gin.New() r.Use(middleware.CORS()) r.GET("/test", func(c *gin.Context) { c.Status(200) }) w := httptest.NewRecorder() req, _ := http.NewRequest("OPTIONS", "/test", nil) r.ServeHTTP(w, req) if w.Code != http.StatusNoContent { t.Errorf("Expected %d for OPTIONS, got %d", http.StatusNoContent, w.Code) } if w.Header().Get("Access-Control-Allow-Origin") != "*" { t.Error("Missing CORS header") } } func TestAuthRoutesRegistered(t *testing.T) { cfg := &config.Config{JWTSecret: "test-secret"} auth := handlers.NewAuthHandler(cfg) r := gin.New() api := r.Group("/api/v1") authGroup := api.Group("/auth") { authGroup.POST("/register", auth.Register) authGroup.POST("/login", auth.Login) authGroup.POST("/refresh", auth.Refresh) authGroup.POST("/logout", auth.Logout) } routes := r.Routes() routePaths := make(map[string]bool) for _, route := range routes { routePaths[route.Method+" "+route.Path] = true } expected := []string{ "POST /api/v1/auth/register", "POST /api/v1/auth/login", "POST /api/v1/auth/refresh", "POST /api/v1/auth/logout", } for _, e := range expected { if !routePaths[e] { t.Errorf("Expected route %s to be registered", e) } } } func TestProtectedRoutesRegistered(t *testing.T) { r := gin.New() api := r.Group("/api/v1") protected := api.Group("") { protected.GET("/chats", handleListChats) protected.POST("/chats", handleCreateChat) protected.GET("/chats/:id", handleGetChat) protected.DELETE("/chats/:id", handleDeleteChat) protected.GET("/chats/:id/messages", handleGetMessages) protected.POST("/chats/:id/messages", handleCreateMessage) protected.GET("/settings", handleGetSettings) protected.GET("/api-configs", handleListAPIConfigs) protected.GET("/models", handleListModels) } routes := r.Routes() if len(routes) != 9 { t.Errorf("Expected 9 routes, got %d", len(routes)) } } func TestAuthMiddlewareNoDatabase(t *testing.T) { cfg := &config.Config{JWTSecret: "test-secret"} r := gin.New() r.Use(middleware.Auth(cfg)) r.GET("/protected", func(c *gin.Context) { c.JSON(200, gin.H{"ok": true}) }) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/protected", nil) r.ServeHTTP(w, req) // No database connected → middleware should pass through if w.Code != http.StatusOK { t.Errorf("Expected pass-through with no DB, got status %d", w.Code) } } func TestPlaceholderHandlersReturn501(t *testing.T) { r := gin.New() r.GET("/chats", handleListChats) r.POST("/chats", handleCreateChat) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/chats", nil) r.ServeHTTP(w, req) if w.Code != 501 { t.Errorf("Expected 501 for unimplemented handler, got %d", w.Code) } }