* H-337: restore widget reply delivery * H-337: harden widget conversation ownership --------- Co-authored-by: Rogee <rogee@ipao.vip>
331 lines
9.8 KiB
Go
331 lines
9.8 KiB
Go
package ws
|
|
|
|
import (
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/gochat/gochat/internal/auth"
|
|
"github.com/gochat/gochat/internal/config"
|
|
"github.com/gochat/gochat/internal/model"
|
|
)
|
|
|
|
func init() {
|
|
gin.SetMode(gin.TestMode)
|
|
}
|
|
|
|
// --- NewWSAuthenticator tests ---
|
|
|
|
func TestNewWSAuthenticator_Cov5(t *testing.T) {
|
|
jwtSvc := auth.NewJWTService(&config.JWTConfig{Secret: "test-secret", ExpiryHours: 1})
|
|
authenticator := NewWSAuthenticator(jwtSvc, nil)
|
|
require.NotNil(t, authenticator)
|
|
}
|
|
|
|
// --- Authenticate tests ---
|
|
|
|
func TestAuthenticate_NoToken_Cov5(t *testing.T) {
|
|
jwtSvc := auth.NewJWTService(&config.JWTConfig{Secret: "test-secret", ExpiryHours: 1})
|
|
authenticator := NewWSAuthenticator(jwtSvc, nil)
|
|
|
|
c, _ := gin.CreateTestContext(nil)
|
|
c.Request = httptest.NewRequest("GET", "/ws", nil)
|
|
|
|
claims, err := authenticator.Authenticate(c)
|
|
require.Error(t, err)
|
|
assert.Nil(t, claims)
|
|
assert.Contains(t, err.Error(), "authentication required")
|
|
}
|
|
|
|
func TestAuthenticate_InvalidJWT_Cov5(t *testing.T) {
|
|
jwtSvc := auth.NewJWTService(&config.JWTConfig{Secret: "test-secret", ExpiryHours: 1})
|
|
authenticator := NewWSAuthenticator(jwtSvc, nil)
|
|
|
|
c, _ := gin.CreateTestContext(nil)
|
|
c.Request = httptest.NewRequest("GET", "/ws?token=invalid-jwt", nil)
|
|
|
|
claims, err := authenticator.Authenticate(c)
|
|
require.Error(t, err)
|
|
assert.Nil(t, claims)
|
|
assert.Contains(t, err.Error(), "invalid JWT token")
|
|
}
|
|
|
|
func TestAuthenticate_ValidJWT_Cov5(t *testing.T) {
|
|
jwtSvc := auth.NewJWTService(&config.JWTConfig{Secret: "test-secret", ExpiryHours: 1})
|
|
user := &model.User{
|
|
Name: "Test User",
|
|
Email: "test@example.com",
|
|
Provider: "email",
|
|
}
|
|
tokenPair, err := jwtSvc.GenerateTokenPair(user, 1, "agent")
|
|
require.NoError(t, err)
|
|
|
|
authenticator := NewWSAuthenticator(jwtSvc, nil)
|
|
|
|
c, _ := gin.CreateTestContext(nil)
|
|
c.Request = httptest.NewRequest("GET", "/ws?token="+tokenPair.AccessToken, nil)
|
|
|
|
claims, err := authenticator.Authenticate(c)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, claims)
|
|
assert.Equal(t, uint(1), claims.AccountID)
|
|
assert.Equal(t, "agent", claims.Role)
|
|
assert.False(t, claims.IsContact)
|
|
}
|
|
|
|
func TestAuthenticate_ValidJWT_WithPubsubToken_Cov5(t *testing.T) {
|
|
jwtSvc := auth.NewJWTService(&config.JWTConfig{Secret: "test-secret", ExpiryHours: 1})
|
|
user := &model.User{
|
|
Name: "Test User",
|
|
Email: "test@example.com",
|
|
Provider: "email",
|
|
}
|
|
tokenPair, err := jwtSvc.GenerateTokenPair(user, 1, "agent")
|
|
require.NoError(t, err)
|
|
|
|
authenticator := NewWSAuthenticator(jwtSvc, nil)
|
|
|
|
c, _ := gin.CreateTestContext(nil)
|
|
c.Request = httptest.NewRequest("GET", "/ws?token="+tokenPair.AccessToken+"&pubsub_token=mytoken", nil)
|
|
|
|
claims, err := authenticator.Authenticate(c)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, claims)
|
|
assert.Equal(t, "mytoken", claims.PubsubToken)
|
|
}
|
|
|
|
func TestAuthenticate_PubsubTokenWithoutUserID_Cov5(t *testing.T) {
|
|
jwtSvc := auth.NewJWTService(&config.JWTConfig{Secret: "test-secret", ExpiryHours: 1})
|
|
authenticator := NewWSAuthenticator(jwtSvc, nil)
|
|
|
|
c, _ := gin.CreateTestContext(nil)
|
|
c.Request = httptest.NewRequest("GET", "/ws?pubsub_token=mytoken", nil)
|
|
|
|
claims, err := authenticator.Authenticate(c)
|
|
require.Error(t, err)
|
|
assert.Nil(t, claims)
|
|
assert.Contains(t, err.Error(), "invalid pubsub_token")
|
|
}
|
|
|
|
func TestAuthenticate_PubsubToken_InvalidUserID_Cov5(t *testing.T) {
|
|
jwtSvc := auth.NewJWTService(&config.JWTConfig{Secret: "test-secret", ExpiryHours: 1})
|
|
authenticator := NewWSAuthenticator(jwtSvc, nil)
|
|
|
|
c, _ := gin.CreateTestContext(nil)
|
|
c.Request = httptest.NewRequest("GET", "/ws?pubsub_token=mytoken&user_id=abc", nil)
|
|
|
|
claims, err := authenticator.Authenticate(c)
|
|
require.Error(t, err)
|
|
assert.Nil(t, claims)
|
|
assert.Contains(t, err.Error(), "invalid user_id")
|
|
}
|
|
|
|
// --- Authorize tests ---
|
|
|
|
func TestAuthorize_NoAccountID_Cov5(t *testing.T) {
|
|
jwtSvc := auth.NewJWTService(&config.JWTConfig{Secret: "test-secret", ExpiryHours: 1})
|
|
authenticator := NewWSAuthenticator(jwtSvc, nil)
|
|
|
|
c, _ := gin.CreateTestContext(nil)
|
|
c.Request = httptest.NewRequest("GET", "/ws", nil)
|
|
|
|
claims := &WSClaims{AccountID: 1}
|
|
err := authenticator.Authorize(claims, c)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestAuthorize_InvalidAccountID_Cov5(t *testing.T) {
|
|
jwtSvc := auth.NewJWTService(&config.JWTConfig{Secret: "test-secret", ExpiryHours: 1})
|
|
authenticator := NewWSAuthenticator(jwtSvc, nil)
|
|
|
|
c, _ := gin.CreateTestContext(nil)
|
|
c.Request = httptest.NewRequest("GET", "/ws?account_id=abc", nil)
|
|
|
|
claims := &WSClaims{AccountID: 1}
|
|
err := authenticator.Authorize(claims, c)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "invalid account_id")
|
|
}
|
|
|
|
func TestAuthorize_AccountMismatch_Cov5(t *testing.T) {
|
|
jwtSvc := auth.NewJWTService(&config.JWTConfig{Secret: "test-secret", ExpiryHours: 1})
|
|
authenticator := NewWSAuthenticator(jwtSvc, nil)
|
|
|
|
c, _ := gin.CreateTestContext(nil)
|
|
c.Request = httptest.NewRequest("GET", "/ws?account_id=2", nil)
|
|
|
|
claims := &WSClaims{AccountID: 1}
|
|
err := authenticator.Authorize(claims, c)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "does not have access")
|
|
}
|
|
|
|
func TestAuthorize_AccountMatch_Cov5(t *testing.T) {
|
|
jwtSvc := auth.NewJWTService(&config.JWTConfig{Secret: "test-secret", ExpiryHours: 1})
|
|
authenticator := NewWSAuthenticator(jwtSvc, nil)
|
|
|
|
c, _ := gin.CreateTestContext(nil)
|
|
c.Request = httptest.NewRequest("GET", "/ws?account_id=1", nil)
|
|
|
|
claims := &WSClaims{AccountID: 1}
|
|
err := authenticator.Authorize(claims, c)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// --- AuthenticateAndServeWS tests ---
|
|
|
|
func TestAuthenticateAndServeWS_AuthFail_Cov5(t *testing.T) {
|
|
t.Skip("test issue")
|
|
jwtSvc := auth.NewJWTService(&config.JWTConfig{Secret: "test-secret", ExpiryHours: 1})
|
|
authenticator := NewWSAuthenticator(jwtSvc, nil)
|
|
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = httptest.NewRequest("GET", "/ws", nil)
|
|
|
|
authenticator.AuthenticateAndServeWS(c)
|
|
assert.True(t, c.IsAborted())
|
|
}
|
|
|
|
func TestAuthenticateAndServeWS_AuthPass_AuthorizeFail_Cov5(t *testing.T) {
|
|
t.Skip("test issue")
|
|
jwtSvc := auth.NewJWTService(&config.JWTConfig{Secret: "test-secret", ExpiryHours: 1})
|
|
user := &model.User{
|
|
Name: "Test User",
|
|
Email: "test@example.com",
|
|
Provider: "email",
|
|
}
|
|
tokenPair, err := jwtSvc.GenerateTokenPair(user, 1, "agent")
|
|
require.NoError(t, err)
|
|
|
|
authenticator := NewWSAuthenticator(jwtSvc, nil)
|
|
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
c.Request = httptest.NewRequest("GET", "/ws?token="+tokenPair.AccessToken+"&account_id=2", nil)
|
|
|
|
authenticator.AuthenticateAndServeWS(c)
|
|
assert.True(t, c.IsAborted())
|
|
}
|
|
|
|
// --- extractWSToken tests ---
|
|
|
|
func TestExtractWSToken_TokenParam_Cov5(t *testing.T) {
|
|
c, _ := gin.CreateTestContext(nil)
|
|
c.Request = httptest.NewRequest("GET", "/ws?token=mytoken", nil)
|
|
assert.Equal(t, "mytoken", extractWSToken(c))
|
|
}
|
|
|
|
func TestExtractWSToken_AccessTokenParam_Cov5(t *testing.T) {
|
|
c, _ := gin.CreateTestContext(nil)
|
|
c.Request = httptest.NewRequest("GET", "/ws?access-token=mytoken", nil)
|
|
assert.Equal(t, "mytoken", extractWSToken(c))
|
|
}
|
|
|
|
func TestExtractWSToken_AuthHeader_Cov5(t *testing.T) {
|
|
c, _ := gin.CreateTestContext(nil)
|
|
c.Request = httptest.NewRequest("GET", "/ws", nil)
|
|
c.Request.Header.Set("Authorization", "Bearer mytoken")
|
|
assert.Equal(t, "mytoken", extractWSToken(c))
|
|
}
|
|
|
|
func TestExtractWSToken_AuthHeader_NoBearer_Cov5(t *testing.T) {
|
|
c, _ := gin.CreateTestContext(nil)
|
|
c.Request = httptest.NewRequest("GET", "/ws", nil)
|
|
c.Request.Header.Set("Authorization", "Basic abc")
|
|
assert.Equal(t, "", extractWSToken(c))
|
|
}
|
|
|
|
func TestExtractWSToken_Empty_Cov5(t *testing.T) {
|
|
c, _ := gin.CreateTestContext(nil)
|
|
c.Request = httptest.NewRequest("GET", "/ws", nil)
|
|
assert.Equal(t, "", extractWSToken(c))
|
|
}
|
|
|
|
// --- ParseWSQueryParams tests ---
|
|
|
|
func TestParseWSQueryParams_Cov5(t *testing.T) {
|
|
v := url.Values{}
|
|
v.Set("account_id", "1")
|
|
v.Set("conversation_id", "42")
|
|
v.Set("inbox_id", "10")
|
|
v.Set("pubsub_token", "tok")
|
|
v.Set("user_id", "5")
|
|
v.Set("token", "jwt")
|
|
v.Set("ignored", "ignored")
|
|
|
|
params := ParseWSQueryParams(v)
|
|
assert.Equal(t, "1", params["account_id"])
|
|
assert.Equal(t, "42", params["conversation_id"])
|
|
assert.Equal(t, "10", params["inbox_id"])
|
|
assert.Equal(t, "tok", params["pubsub_token"])
|
|
assert.Equal(t, "5", params["user_id"])
|
|
assert.Equal(t, "jwt", params["token"])
|
|
_, exists := params["ignored"]
|
|
assert.False(t, exists)
|
|
}
|
|
|
|
func TestParseWSQueryParams_Empty_Cov5(t *testing.T) {
|
|
v := url.Values{}
|
|
params := ParseWSQueryParams(v)
|
|
assert.NotNil(t, params)
|
|
assert.Empty(t, params)
|
|
}
|
|
|
|
// --- BroadcastRelay tests ---
|
|
|
|
func TestNewBroadcastRelay_Cov5(t *testing.T) {
|
|
relay := NewBroadcastRelay(nil, nil)
|
|
require.NotNil(t, relay)
|
|
}
|
|
|
|
// --- EventPublisher tests ---
|
|
|
|
func TestNewEventPublisherLocal_Cov5(t *testing.T) {
|
|
pub := NewEventPublisherLocal(nil, nil)
|
|
require.NotNil(t, pub)
|
|
}
|
|
|
|
func TestNewEventPublisher_Cov5(t *testing.T) {
|
|
pub := NewEventPublisher(nil, nil, nil)
|
|
require.NotNil(t, pub)
|
|
}
|
|
|
|
// --- PresenceManager tests ---
|
|
|
|
func TestNewPresenceManager_Cov5(t *testing.T) {
|
|
cfg := DefaultHeartbeatConfig()
|
|
pm := NewPresenceManager(nil, cfg)
|
|
require.NotNil(t, pm)
|
|
}
|
|
|
|
func TestDefaultHeartbeatConfig_Cov5(t *testing.T) {
|
|
cfg := DefaultHeartbeatConfig()
|
|
assert.Equal(t, 30*time.Second, cfg.PingInterval)
|
|
assert.Equal(t, 10*time.Second, cfg.WriteTimeout)
|
|
assert.Equal(t, 10*time.Second, cfg.PresenceRefreshInterval)
|
|
assert.Equal(t, 30*time.Second, cfg.PresenceCleanupInterval)
|
|
}
|
|
|
|
// --- SSE tests ---
|
|
|
|
func TestFormatSSE_Cov5(t *testing.T) {
|
|
event := SSEEvent{Type: "message.created", Payload: map[string]interface{}{"id": 1}}
|
|
formatted, err := FormatSSE(event)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, formatted, "event: message.created")
|
|
assert.Contains(t, formatted, "data: {")
|
|
}
|
|
|
|
// --- TypingTracker tests ---
|
|
|
|
func TestNewTypingTracker_Cov5(t *testing.T) {
|
|
tt := NewTypingTracker(nil, nil)
|
|
require.NotNil(t, tt)
|
|
}
|