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:
nutcas3
2026-04-23 02:58:34 +03:00
parent 546c9cd69f
commit e2e39d8b37
6 changed files with 2621 additions and 0 deletions
@@ -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)
}
}