Files
go-web-gin/app_test.go

357 lines
8.8 KiB
Go

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))
}