- Added QA summary report for CrowdSec toggle fix validation, detailing test results, code quality audit, and recommendations for deployment. - Updated existing QA report to reflect the new toggle fix validation status and testing cycle. - Enhanced security documentation to explain the persistence of CrowdSec across container restarts and troubleshooting steps for common issues. - Expanded troubleshooting guide to address scenarios where CrowdSec does not start after a container restart, including diagnosis and solutions.
243 lines
7.3 KiB
Go
243 lines
7.3 KiB
Go
package handlers
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/gorilla/websocket"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/Wikid82/charon/backend/internal/logger"
|
|
)
|
|
|
|
func TestLogsWebSocketHandler_SuccessfulConnection(t *testing.T) {
|
|
server := newWebSocketTestServer(t)
|
|
|
|
conn := server.dial(t, "/logs/live")
|
|
|
|
waitForListenerCount(t, server.hook, 1)
|
|
require.NoError(t, conn.WriteMessage(websocket.TextMessage, []byte("hello")))
|
|
}
|
|
|
|
func TestLogsWebSocketHandler_ReceiveLogEntries(t *testing.T) {
|
|
server := newWebSocketTestServer(t)
|
|
conn := server.dial(t, "/logs/live")
|
|
|
|
// Wait for the WebSocket handler to fully subscribe before sending entries
|
|
waitForListenerCount(t, server.hook, 1)
|
|
|
|
server.sendEntry(t, logrus.InfoLevel, "hello", logrus.Fields{"source": "api", "user": "alice"})
|
|
|
|
received := readLogEntry(t, conn)
|
|
assert.Equal(t, "info", received.Level)
|
|
assert.Equal(t, "hello", received.Message)
|
|
assert.Equal(t, "api", received.Source)
|
|
assert.Equal(t, "alice", received.Fields["user"])
|
|
}
|
|
|
|
func TestLogsWebSocketHandler_LevelFilter(t *testing.T) {
|
|
server := newWebSocketTestServer(t)
|
|
conn := server.dial(t, "/logs/live?level=error")
|
|
|
|
// Wait for the WebSocket handler to fully subscribe before sending entries
|
|
waitForListenerCount(t, server.hook, 1)
|
|
|
|
server.sendEntry(t, logrus.InfoLevel, "info", logrus.Fields{"source": "api"})
|
|
server.sendEntry(t, logrus.ErrorLevel, "error", logrus.Fields{"source": "api"})
|
|
|
|
received := readLogEntry(t, conn)
|
|
assert.Equal(t, "error", received.Level)
|
|
|
|
// Ensure no additional messages arrive
|
|
require.NoError(t, conn.SetReadDeadline(time.Now().Add(150*time.Millisecond)))
|
|
_, _, err := conn.ReadMessage()
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestLogsWebSocketHandler_SourceFilter(t *testing.T) {
|
|
server := newWebSocketTestServer(t)
|
|
conn := server.dial(t, "/logs/live?source=api")
|
|
|
|
// Wait for the WebSocket handler to fully subscribe before sending entries
|
|
waitForListenerCount(t, server.hook, 1)
|
|
|
|
server.sendEntry(t, logrus.InfoLevel, "backend", logrus.Fields{"source": "backend"})
|
|
server.sendEntry(t, logrus.InfoLevel, "api", logrus.Fields{"source": "api"})
|
|
|
|
received := readLogEntry(t, conn)
|
|
assert.Equal(t, "api", received.Source)
|
|
}
|
|
|
|
func TestLogsWebSocketHandler_CombinedFilters(t *testing.T) {
|
|
server := newWebSocketTestServer(t)
|
|
conn := server.dial(t, "/logs/live?level=error&source=api")
|
|
|
|
// Wait for the WebSocket handler to fully subscribe before sending entries
|
|
waitForListenerCount(t, server.hook, 1)
|
|
|
|
server.sendEntry(t, logrus.WarnLevel, "warn api", logrus.Fields{"source": "api"})
|
|
server.sendEntry(t, logrus.ErrorLevel, "error api", logrus.Fields{"source": "api"})
|
|
server.sendEntry(t, logrus.ErrorLevel, "error ui", logrus.Fields{"source": "ui"})
|
|
|
|
received := readLogEntry(t, conn)
|
|
assert.Equal(t, "error api", received.Message)
|
|
assert.Equal(t, "api", received.Source)
|
|
}
|
|
|
|
func TestLogsWebSocketHandler_CaseInsensitiveFilters(t *testing.T) {
|
|
server := newWebSocketTestServer(t)
|
|
conn := server.dial(t, "/logs/live?level=ERROR&source=API")
|
|
|
|
// Wait for the WebSocket handler to fully subscribe before sending entries
|
|
waitForListenerCount(t, server.hook, 1)
|
|
|
|
server.sendEntry(t, logrus.ErrorLevel, "error api", logrus.Fields{"source": "api"})
|
|
received := readLogEntry(t, conn)
|
|
assert.Equal(t, "error api", received.Message)
|
|
assert.Equal(t, "error", received.Level)
|
|
}
|
|
|
|
func TestLogsWebSocketHandler_UpgradeFailure(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.GET("/logs/live", LogsWebSocketHandler)
|
|
|
|
w := httptest.NewRecorder()
|
|
req := httptest.NewRequest("GET", "/logs/live", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestLogsWebSocketHandler_ClientDisconnect(t *testing.T) {
|
|
server := newWebSocketTestServer(t)
|
|
conn := server.dial(t, "/logs/live")
|
|
|
|
waitForListenerCount(t, server.hook, 1)
|
|
require.NoError(t, conn.Close())
|
|
waitForListenerCount(t, server.hook, 0)
|
|
}
|
|
|
|
func TestLogsWebSocketHandler_ChannelClosed(t *testing.T) {
|
|
server := newWebSocketTestServer(t)
|
|
_ = server.dial(t, "/logs/live")
|
|
|
|
ids := server.subscriberIDs(t)
|
|
require.Len(t, ids, 1)
|
|
|
|
server.hook.Unsubscribe(ids[0])
|
|
waitForListenerCount(t, server.hook, 0)
|
|
}
|
|
|
|
func TestLogsWebSocketHandler_MultipleConnections(t *testing.T) {
|
|
server := newWebSocketTestServer(t)
|
|
const connCount = 5
|
|
|
|
conns := make([]*websocket.Conn, 0, connCount)
|
|
for i := 0; i < connCount; i++ {
|
|
conns = append(conns, server.dial(t, "/logs/live"))
|
|
}
|
|
|
|
waitForListenerCount(t, server.hook, connCount)
|
|
|
|
done := make(chan struct{})
|
|
for _, conn := range conns {
|
|
go func(c *websocket.Conn) {
|
|
defer func() { done <- struct{}{} }()
|
|
for {
|
|
entry := readLogEntry(t, c)
|
|
if entry.Message == "broadcast" {
|
|
assert.Equal(t, "broadcast", entry.Message)
|
|
return
|
|
}
|
|
}
|
|
}(conn)
|
|
}
|
|
|
|
server.sendEntry(t, logrus.InfoLevel, "broadcast", logrus.Fields{"source": "api"})
|
|
|
|
for i := 0; i < connCount; i++ {
|
|
<-done
|
|
}
|
|
}
|
|
|
|
func TestLogsWebSocketHandler_HighVolumeLogging(t *testing.T) {
|
|
server := newWebSocketTestServer(t)
|
|
conn := server.dial(t, "/logs/live")
|
|
|
|
// Wait for the WebSocket handler to fully subscribe before sending entries
|
|
waitForListenerCount(t, server.hook, 1)
|
|
|
|
for i := 0; i < 200; i++ {
|
|
server.sendEntry(t, logrus.InfoLevel, fmt.Sprintf("msg-%d", i), logrus.Fields{"source": "api"})
|
|
received := readLogEntry(t, conn)
|
|
assert.Equal(t, fmt.Sprintf("msg-%d", i), received.Message)
|
|
}
|
|
}
|
|
|
|
func TestLogsWebSocketHandler_EmptyLogFields(t *testing.T) {
|
|
server := newWebSocketTestServer(t)
|
|
conn := server.dial(t, "/logs/live")
|
|
|
|
// Wait for the WebSocket handler to fully subscribe before sending entries
|
|
waitForListenerCount(t, server.hook, 1)
|
|
|
|
server.sendEntry(t, logrus.InfoLevel, "no fields", nil)
|
|
first := readLogEntry(t, conn)
|
|
assert.Equal(t, "", first.Source)
|
|
|
|
server.sendEntry(t, logrus.InfoLevel, "empty map", logrus.Fields{})
|
|
second := readLogEntry(t, conn)
|
|
assert.Equal(t, "", second.Source)
|
|
}
|
|
|
|
func TestLogsWebSocketHandler_SubscriberIDUniqueness(t *testing.T) {
|
|
server := newWebSocketTestServer(t)
|
|
_ = server.dial(t, "/logs/live")
|
|
_ = server.dial(t, "/logs/live")
|
|
|
|
waitForListenerCount(t, server.hook, 2)
|
|
ids := server.subscriberIDs(t)
|
|
require.Len(t, ids, 2)
|
|
assert.NotEqual(t, ids[0], ids[1])
|
|
}
|
|
|
|
func TestLogsWebSocketHandler_WithRealLogger(t *testing.T) {
|
|
server := newWebSocketTestServer(t)
|
|
conn := server.dial(t, "/logs/live")
|
|
|
|
// Wait for the WebSocket handler to fully subscribe before sending entries
|
|
waitForListenerCount(t, server.hook, 1)
|
|
|
|
loggerEntry := logger.Log().WithField("source", "api")
|
|
loggerEntry.Info("from logger")
|
|
|
|
received := readLogEntry(t, conn)
|
|
assert.Equal(t, "from logger", received.Message)
|
|
assert.Equal(t, "api", received.Source)
|
|
}
|
|
|
|
func TestLogsWebSocketHandler_ConnectionLifecycle(t *testing.T) {
|
|
server := newWebSocketTestServer(t)
|
|
conn := server.dial(t, "/logs/live")
|
|
|
|
// Wait for the WebSocket handler to fully subscribe before sending entries
|
|
waitForListenerCount(t, server.hook, 1)
|
|
|
|
server.sendEntry(t, logrus.InfoLevel, "first", logrus.Fields{"source": "api"})
|
|
first := readLogEntry(t, conn)
|
|
assert.Equal(t, "first", first.Message)
|
|
|
|
require.NoError(t, conn.Close())
|
|
waitForListenerCount(t, server.hook, 0)
|
|
|
|
// Ensure no panic when sending after disconnect
|
|
server.sendEntry(t, logrus.InfoLevel, "after-close", logrus.Fields{"source": "api"})
|
|
}
|