package go_web_gin import ( "context" "net/http" "net/http/httptest" "testing" "time" "github.com/gin-gonic/gin" "github.com/stretchr/testify/suite" ) // AppTestSuite is the test suite for App type AppTestSuite struct { suite.Suite app *App } // SetupSuite runs once before all tests func (s *AppTestSuite) SetupSuite() { gin.SetMode(gin.TestMode) s.app = New() } // TearDownSuite runs once after all tests func (s *AppTestSuite) TearDownSuite() { // Cleanup if needed } // SetupTest runs before each test func (s *AppTestSuite) SetupTest() { // Reset state if needed } // TearDownTest runs after each test func (s *AppTestSuite) TearDownTest() { // Cleanup after each test if needed } // TestNew tests the New function func (s *AppTestSuite) TestNew() { app := New() s.NotNil(app, "New should return a non-nil App") s.NotNil(app.engine, "engine should be initialized") s.NotNil(app.logger, "logger should be initialized") } // TestNewSingleton tests that svr.GetEngine returns the same instance func (s *AppTestSuite) TestNewSingleton() { app1 := New() app2 := New() s.Equal(app1.engine, app2.engine, "engine should be singleton") } // TestUseMiddleware tests the UseMiddleware method func (s *AppTestSuite) TestUseMiddleware() { middlewareCalled := false middleware := func(c *gin.Context) { middlewareCalled = true c.Next() } s.app.UseMiddleware(middleware) // Create a test route to verify middleware s.app.engine.GET("/test-middleware", func(c *gin.Context) { c.Status(http.StatusOK) }) req := httptest.NewRequest("GET", "/test-middleware", nil) w := httptest.NewRecorder() s.app.engine.ServeHTTP(w, req) s.True(middlewareCalled, "middleware should be called") } // TestRegisterRoutes tests the RegisterRoutes method func (s *AppTestSuite) TestRegisterRoutes() { app := New() app.RegisterRoutes(func(e *gin.Engine) { e.GET("/test-route", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "ok"}) }) }) req := httptest.NewRequest("GET", "/test-route", nil) w := httptest.NewRecorder() app.engine.ServeHTTP(w, req) s.Equal(http.StatusOK, w.Code) s.Contains(w.Body.String(), "ok") } // TestRegisterRoutesMultiple tests registering multiple routes func (s *AppTestSuite) TestRegisterRoutesMultiple() { app := New() app.RegisterRoutes(func(e *gin.Engine) { e.GET("/route1", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"route": 1}) }) e.POST("/route2", func(c *gin.Context) { c.JSON(http.StatusCreated, gin.H{"route": 2}) }) e.PUT("/route3", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"route": 3}) }) }) // Test GET req1 := httptest.NewRequest("GET", "/route1", nil) w1 := httptest.NewRecorder() app.engine.ServeHTTP(w1, req1) s.Equal(http.StatusOK, w1.Code) // Test POST req2 := httptest.NewRequest("POST", "/route2", nil) w2 := httptest.NewRecorder() app.engine.ServeHTTP(w2, req2) s.Equal(http.StatusCreated, w2.Code) // Test PUT req3 := httptest.NewRequest("PUT", "/route3", nil) w3 := httptest.NewRecorder() app.engine.ServeHTTP(w3, req3) s.Equal(http.StatusOK, w3.Code) } // TestDB tests the DB method returns nil when not initialized func (s *AppTestSuite) TestDB() { app := New() s.Nil(app.DB(), "DB should be nil when not initialized") } // TestRedis tests the Redis method returns nil when not initialized func (s *AppTestSuite) TestRedis() { app := New() s.Nil(app.Redis(), "Redis should be nil when not initialized") } // TestLogger tests the Logger method func (s *AppTestSuite) TestLogger() { app := New() logger := app.Logger() s.NotNil(logger, "Logger should not be nil") } // TestLoggerNotNil tests that logger is always initialized func (s *AppTestSuite) TestLoggerNotNil() { s.NotNil(s.app.Logger(), "Logger should always be initialized in New()") } // TestLoggerMethods tests that logger methods work func (s *AppTestSuite) TestLoggerMethods() { s.NotPanics(func() { s.app.Logger().Info(nil, "Test info message") s.app.Logger().Debug(nil, "Test debug message") s.app.Logger().Warn(nil, "Test warn message") }, "Logger methods should not panic") } // TestLoggerWithContext tests logger with context func (s *AppTestSuite) TestLoggerWithContext() { ctx := context.Background() s.NotPanics(func() { s.app.Logger().Info(ctx, "Test with context") }, "Logger should work with context") } // TestRunInvalidAddress tests Run with invalid address func (s *AppTestSuite) TestRunInvalidAddress() { app := New() app.RegisterRoutes(func(e *gin.Engine) { e.GET("/ping", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"pong": true}) }) }) // Use a channel to handle the async nature of Run errCh := make(chan error, 1) go func() { errCh <- app.Run("invalid:address:format") }() select { case err := <-errCh: s.Error(err, "Run should return error for invalid address") case <-time.After(2 * time.Second): // Server might start, which is fine for this test } } // TestHTTPMethod tests various HTTP methods func (s *AppTestSuite) TestHTTPMethod() { app := New() app.RegisterRoutes(func(e *gin.Engine) { e.DELETE("/delete", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"deleted": true}) }) e.PATCH("/patch", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"patched": true}) }) e.OPTIONS("/options", func(c *gin.Context) { c.Status(http.StatusNoContent) }) e.HEAD("/head", func(c *gin.Context) { c.Status(http.StatusOK) }) }) // Test DELETE req := httptest.NewRequest("DELETE", "/delete", nil) w := httptest.NewRecorder() app.engine.ServeHTTP(w, req) s.Equal(http.StatusOK, w.Code) // Test PATCH req2 := httptest.NewRequest("PATCH", "/patch", nil) w2 := httptest.NewRecorder() app.engine.ServeHTTP(w2, req2) s.Equal(http.StatusOK, w2.Code) // Test OPTIONS req3 := httptest.NewRequest("OPTIONS", "/options", nil) w3 := httptest.NewRecorder() app.engine.ServeHTTP(w3, req3) s.Equal(http.StatusNoContent, w3.Code) // Test HEAD req4 := httptest.NewRequest("HEAD", "/head", nil) w4 := httptest.NewRecorder() app.engine.ServeHTTP(w4, req4) s.Equal(http.StatusOK, w4.Code) } // TestRouteGroup tests route groups func (s *AppTestSuite) TestRouteGroup() { app := New() app.RegisterRoutes(func(e *gin.Engine) { api := e.Group("/api") { api.GET("/users", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"users": []string{}}) }) api.GET("/posts", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"posts": []string{}}) }) } }) req1 := httptest.NewRequest("GET", "/api/users", nil) w1 := httptest.NewRecorder() app.engine.ServeHTTP(w1, req1) s.Equal(http.StatusOK, w1.Code) req2 := httptest.NewRequest("GET", "/api/posts", nil) w2 := httptest.NewRecorder() app.engine.ServeHTTP(w2, req2) s.Equal(http.StatusOK, w2.Code) } // TestMiddlewareChain tests middleware chain func (s *AppTestSuite) TestMiddlewareChain() { app := New() order := []string{} app.UseMiddleware(func(c *gin.Context) { order = append(order, "middleware1-before") c.Next() order = append(order, "middleware1-after") }) app.UseMiddleware(func(c *gin.Context) { order = append(order, "middleware2-before") c.Next() order = append(order, "middleware2-after") }) app.RegisterRoutes(func(e *gin.Engine) { e.GET("/chain", func(c *gin.Context) { order = append(order, "handler") c.Status(http.StatusOK) }) }) req := httptest.NewRequest("GET", "/chain", nil) w := httptest.NewRecorder() app.engine.ServeHTTP(w, req) expected := []string{ "middleware1-before", "middleware2-before", "handler", "middleware2-after", "middleware1-after", } s.Equal(expected, order, "middleware should execute in correct order") } // TestQueryParam tests query parameters func (s *AppTestSuite) TestQueryParam() { app := New() app.RegisterRoutes(func(e *gin.Engine) { e.GET("/search", func(c *gin.Context) { q := c.Query("q") c.JSON(http.StatusOK, gin.H{"query": q}) }) }) req := httptest.NewRequest("GET", "/search?q=test", nil) w := httptest.NewRecorder() app.engine.ServeHTTP(w, req) s.Equal(http.StatusOK, w.Code) s.Contains(w.Body.String(), "test") } // TestPathParam tests path parameters func (s *AppTestSuite) TestPathParam() { app := New() app.RegisterRoutes(func(e *gin.Engine) { e.GET("/users/:id", func(c *gin.Context) { id := c.Param("id") c.JSON(http.StatusOK, gin.H{"user_id": id}) }) }) req := httptest.NewRequest("GET", "/users/123", nil) w := httptest.NewRecorder() app.engine.ServeHTTP(w, req) s.Equal(http.StatusOK, w.Code) s.Contains(w.Body.String(), "123") } // TestNotFound tests 404 handling func (s *AppTestSuite) TestNotFound() { app := New() req := httptest.NewRequest("GET", "/nonexistent", nil) w := httptest.NewRecorder() app.engine.ServeHTTP(w, req) s.Equal(http.StatusNotFound, w.Code) } // TestAppRunSuite runs the test suite func TestAppRunSuite(t *testing.T) { suite.Run(t, new(AppTestSuite)) }