mattermost/server/cmd/mmctl/commands/websockets_test.go
Ben Schumacher 93ab9a4ccc
[MM-68351] Fix nil pointer panic in mmctl websocket command on connection failure (#36138)
* fix(mmctl): prevent nil pointer panic in websocket command on connection failure

When the WebSocket connection fails immediately, Listen() closes EventChannel
via defer. Reading from a closed channel with a plain receive returns nil,
causing a panic in ToJSON(). Switch to range so the loop exits cleanly,
add a nil guard, and surface ListenError to the caller.

Fixes MM-68351

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(mmctl): add unit tests for websocket nil event and ListenError handling

Extracts the event-processing loop into processWebSocketEvents to enable
unit testing, and adds tests covering the nil-event skip and error surfacing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(mmctl): add happy-path subtest for processWebSocketEvents

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 15:32:34 +02:00

48 lines
1.3 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package commands
import (
"net/http"
"testing"
"github.com/mattermost/mattermost/server/public/model"
"github.com/stretchr/testify/require"
)
func TestProcessWebSocketEvents(t *testing.T) {
t.Run("nil event in channel does not panic and returns nil", func(t *testing.T) {
ch := make(chan *model.WebSocketEvent, 1)
ch <- nil
close(ch)
c := &model.WebSocketClient{EventChannel: ch}
err := processWebSocketEvents(c)
require.NoError(t, err)
})
t.Run("non-nil event is processed and returns nil", func(t *testing.T) {
ch := make(chan *model.WebSocketEvent, 1)
ch <- model.NewWebSocketEvent(model.WebsocketEventPosted, "", "", "", nil, "")
close(ch)
c := &model.WebSocketClient{EventChannel: ch}
err := processWebSocketEvents(c)
require.NoError(t, err)
})
t.Run("ListenError is surfaced after channel closes", func(t *testing.T) {
ch := make(chan *model.WebSocketEvent)
close(ch)
appErr := model.NewAppError("Listen", "model.websocket_client.connect_fail.app_error", nil, "connection refused", http.StatusInternalServerError)
c := &model.WebSocketClient{
EventChannel: ch,
ListenError: appErr,
}
err := processWebSocketEvents(c)
require.Error(t, err)
require.Contains(t, err.Error(), appErr.Error())
})
}