Files
Charon/backend/internal/api/handlers/logs_ws_test_utils.go
GitHub Actions 8294d6ee49 Add QA test outputs, build scripts, and Dockerfile validation
- Created `qa-test-output-after-fix.txt` and `qa-test-output.txt` to log results of certificate page authentication tests.
- Added `build.sh` for deterministic backend builds in CI, utilizing `go list` for efficiency.
- Introduced `codeql_scan.sh` for CodeQL database creation and analysis for Go and JavaScript/TypeScript.
- Implemented `dockerfile_check.sh` to validate Dockerfiles for base image and package manager mismatches.
- Added `sourcery_precommit_wrapper.sh` to facilitate Sourcery CLI usage in pre-commit hooks.
2025-12-11 18:26:24 +00:00

101 lines
2.8 KiB
Go

package handlers
import (
"bytes"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
"github.com/Wikid82/charon/backend/internal/logger"
)
// webSocketTestServer wraps a test HTTP server and broadcast hook for WebSocket tests.
type webSocketTestServer struct {
server *httptest.Server
url string
hook *logger.BroadcastHook
}
// resetLogger reinitializes the global logger with an in-memory buffer to avoid cross-test leakage.
func resetLogger(t *testing.T) *logger.BroadcastHook {
t.Helper()
var buf bytes.Buffer
logger.Init(true, &buf)
return logger.GetBroadcastHook()
}
// newWebSocketTestServer builds a gin router exposing the WebSocket handler and starts an httptest server.
func newWebSocketTestServer(t *testing.T) *webSocketTestServer {
t.Helper()
gin.SetMode(gin.TestMode)
hook := resetLogger(t)
router := gin.New()
router.GET("/logs/live", LogsWebSocketHandler)
srv := httptest.NewServer(router)
t.Cleanup(srv.Close)
wsURL := "ws" + strings.TrimPrefix(srv.URL, "http")
return &webSocketTestServer{server: srv, url: wsURL, hook: hook}
}
// dial opens a WebSocket connection to the provided path and asserts upgrade success.
func (s *webSocketTestServer) dial(t *testing.T, path string) *websocket.Conn {
t.Helper()
conn, resp, err := websocket.DefaultDialer.Dial(s.url+path, nil)
require.NoError(t, err)
require.NotNil(t, resp)
require.Equal(t, http.StatusSwitchingProtocols, resp.StatusCode)
t.Cleanup(func() {
_ = resp.Body.Close()
})
conn.SetReadLimit(1 << 20)
t.Cleanup(func() {
_ = conn.Close()
})
return conn
}
// sendEntry broadcasts a log entry through the shared hook.
func (s *webSocketTestServer) sendEntry(t *testing.T, lvl logrus.Level, msg string, fields logrus.Fields) {
t.Helper()
entry := &logrus.Entry{
Level: lvl,
Message: msg,
Time: time.Now().UTC(),
Data: fields,
}
require.NoError(t, s.hook.Fire(entry))
}
// readLogEntry reads a LogEntry from the WebSocket with a short deadline to avoid flakiness.
func readLogEntry(t *testing.T, conn *websocket.Conn) LogEntry {
t.Helper()
require.NoError(t, conn.SetReadDeadline(time.Now().Add(5*time.Second)))
var entry LogEntry
require.NoError(t, conn.ReadJSON(&entry))
return entry
}
// waitForListenerCount waits until the broadcast hook reports the desired listener count.
func waitForListenerCount(t *testing.T, hook *logger.BroadcastHook, expected int) {
t.Helper()
require.Eventually(t, func() bool {
return hook.ActiveListeners() == expected
}, 2*time.Second, 20*time.Millisecond)
}
// subscriberIDs introspects the broadcast hook to return the active subscriber IDs.
func (s *webSocketTestServer) subscriberIDs(t *testing.T) []string {
t.Helper()
return s.hook.ListenerIDs()
}