mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-18 11:02:54 +00:00
feat: Add comprehensive API integration tests with authentication, RBAC, and endpoint coverage for all platform handlers
- Add TestSetup struct with DB/Router/AuthService/test users/JWT tokens for test environment initialization - Add setupTest function with SQLite in-memory database, user/plugin/automation/config/auth session migrations, and test user creation with admin/operator/viewer/user roles - Add makeRequest helper for authenticated HTTP requests with Bearer token and JSON body support
This commit is contained in:
@@ -0,0 +1,287 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSimpleRateLimiter_Allow(t *testing.T) {
|
||||
limiter := NewSimpleRateLimiter(5, time.Second)
|
||||
|
||||
// Should allow first 5 requests
|
||||
for i := range 5 {
|
||||
assert.True(t, limiter.Allow("test-key"), "Request %d should be allowed", i+1)
|
||||
}
|
||||
|
||||
// 6th request should be denied
|
||||
assert.False(t, limiter.Allow("test-key"), "6th request should be denied")
|
||||
}
|
||||
|
||||
func TestSimpleRateLimiter_WindowReset(t *testing.T) {
|
||||
limiter := NewSimpleRateLimiter(2, 100*time.Millisecond)
|
||||
|
||||
// Use up the limit
|
||||
assert.True(t, limiter.Allow("test-key"))
|
||||
assert.True(t, limiter.Allow("test-key"))
|
||||
assert.False(t, limiter.Allow("test-key"))
|
||||
|
||||
// Wait for window to reset
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
|
||||
// Should be allowed again
|
||||
assert.True(t, limiter.Allow("test-key"))
|
||||
}
|
||||
|
||||
func TestSimpleRateLimiter_DifferentKeys(t *testing.T) {
|
||||
limiter := NewSimpleRateLimiter(2, time.Second)
|
||||
|
||||
// Different keys should have independent limits
|
||||
assert.True(t, limiter.Allow("key1"))
|
||||
assert.True(t, limiter.Allow("key1"))
|
||||
assert.False(t, limiter.Allow("key1"))
|
||||
|
||||
// key2 should still have its full limit
|
||||
assert.True(t, limiter.Allow("key2"))
|
||||
assert.True(t, limiter.Allow("key2"))
|
||||
}
|
||||
|
||||
func TestRateLimit_Middleware(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Create a test handler
|
||||
handler := func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
||||
}
|
||||
|
||||
// Apply rate limiting middleware (10 requests per minute)
|
||||
router := gin.New()
|
||||
router.Use(RateLimit(10))
|
||||
router.GET("/test", handler)
|
||||
|
||||
// Make 11 requests
|
||||
for i := range 10 {
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
req.RemoteAddr = "127.0.0.1:12345"
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code, "Request %d should succeed", i+1)
|
||||
}
|
||||
|
||||
// 11th request should be rate limited
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
req.RemoteAddr = "127.0.0.1:12345"
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusTooManyRequests, w.Code)
|
||||
|
||||
var response map[string]any
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Rate limit exceeded", response["error"])
|
||||
}
|
||||
|
||||
func TestRateLimitByEndpoint_Middleware(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
handler := func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
||||
}
|
||||
|
||||
router := gin.New()
|
||||
router.Use(RateLimitByEndpoint())
|
||||
router.GET("/v1/auth/login", handler)
|
||||
router.GET("/v1/services", handler)
|
||||
router.GET("/v1/users", handler)
|
||||
router.GET("/v1/other", handler)
|
||||
|
||||
// Test auth endpoint (strict limit)
|
||||
for i := range 11 {
|
||||
req, _ := http.NewRequest("GET", "/v1/auth/login", nil)
|
||||
req.RemoteAddr = "127.0.0.1:12345"
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
if i < 10 {
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
} else {
|
||||
assert.Equal(t, http.StatusTooManyRequests, w.Code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRateLimitByUser_Middleware(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
handler := func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
||||
}
|
||||
|
||||
router := gin.New()
|
||||
router.Use(RateLimitByUser(5))
|
||||
router.GET("/test", handler)
|
||||
|
||||
// Test without user ID (should use IP-based limiting)
|
||||
for i := range 6 {
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
req.RemoteAddr = "127.0.0.1:12345"
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
if i < 5 {
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
} else {
|
||||
assert.Equal(t, http.StatusTooManyRequests, w.Code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRateLimitByIP_Middleware(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
handler := func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
||||
}
|
||||
|
||||
router := gin.New()
|
||||
router.Use(RateLimitByIP(3))
|
||||
router.GET("/test", handler)
|
||||
|
||||
// Test IP-based limiting
|
||||
for i := range 4 {
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
req.RemoteAddr = "192.168.1.100:12345"
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
if i < 3 {
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
} else {
|
||||
assert.Equal(t, http.StatusTooManyRequests, w.Code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRateLimitWithHeaders_Middleware(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
handler := func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
||||
}
|
||||
|
||||
router := gin.New()
|
||||
router.Use(RateLimitWithHeaders(10))
|
||||
router.GET("/test", handler)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
req.RemoteAddr = "127.0.0.1:12345"
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
// Check for rate limit headers
|
||||
assert.Equal(t, "10", w.Header().Get("X-RateLimit-Limit"))
|
||||
assert.Equal(t, "60", w.Header().Get("X-RateLimit-Window"))
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
func TestRateLimit_ErrorHandling(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Test with invalid limiter (should still work, just not limit)
|
||||
router := gin.New()
|
||||
router.Use(RateLimit(-1)) // Invalid limit
|
||||
router.GET("/test", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
||||
})
|
||||
|
||||
// Should still work without rate limiting
|
||||
for range 20 {
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
req.RemoteAddr = "127.0.0.1:12345"
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRateLimit_ConcurrentAccess(t *testing.T) {
|
||||
limiter := NewSimpleRateLimiter(100, time.Second)
|
||||
|
||||
// Test concurrent access
|
||||
done := make(chan bool, 200)
|
||||
|
||||
// Launch 200 goroutines, each making 1 request
|
||||
for range 200 {
|
||||
go func() {
|
||||
allowed := limiter.Allow("concurrent-test")
|
||||
done <- allowed
|
||||
}()
|
||||
}
|
||||
|
||||
// Count allowed requests
|
||||
allowedCount := 0
|
||||
for range 200 {
|
||||
if <-done {
|
||||
allowedCount++
|
||||
}
|
||||
}
|
||||
|
||||
// Should allow exactly 100 requests
|
||||
assert.Equal(t, 100, allowedCount)
|
||||
}
|
||||
|
||||
func TestRateLimit_CleanupOldRequests(t *testing.T) {
|
||||
limiter := NewSimpleRateLimiter(5, 100*time.Millisecond)
|
||||
|
||||
// Use up the limit
|
||||
for range 5 {
|
||||
assert.True(t, limiter.Allow("cleanup-test"))
|
||||
}
|
||||
assert.False(t, limiter.Allow("cleanup-test"))
|
||||
|
||||
// Wait for old requests to be cleaned up
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Should be able to make requests again
|
||||
assert.True(t, limiter.Allow("cleanup-test"))
|
||||
|
||||
// Check that old requests were cleaned up (internal state should be smaller)
|
||||
// This is more of an implementation detail test, but ensures cleanup works
|
||||
}
|
||||
|
||||
// Benchmark tests
|
||||
func BenchmarkSimpleRateLimiter_Allow(b *testing.B) {
|
||||
limiter := NewSimpleRateLimiter(1000, time.Second)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
limiter.Allow("benchmark-key")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRateLimit_Middleware(b *testing.B) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
router := gin.New()
|
||||
router.Use(RateLimit(1000))
|
||||
router.GET("/test", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
||||
})
|
||||
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
req.RemoteAddr = "127.0.0.1:12345"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user