Files
gochat/backend/internal/ws/ws_test.go
T
rogee aeddedf2a3 Reorganize repo: backend/, deploy/, docs/ layout + AGENTS.md
Restructure the monorepo into clear top-level directories:
- backend/: Go module root (cmd, internal, pkg, configs, migrations,
  docs/swagger, scripts, tests, go.mod, Makefile, .air.toml)
- deploy/: Docker (Dockerfile, docker-compose*), quickstart, fluentd
- docs/: project documentation + reports/ (moved from repo root)
- AGENTS.md: new AI coding-agent guide at repo root

Update all references to the new layout:
- Dockerfile: COPY backend/go.mod, COPY backend/ (context = repo root)
- docker-compose files: context ../.., dockerfile deploy/docker/Dockerfile,
  env_file ../../.env, volume mounts ../../backend:/app
- deploy/quickstart/compose.yaml: dockerfile deploy/docker/Dockerfile
- CI: working-directory: backend for go commands, file deploy/docker/Dockerfile,
  coverage path backend/coverage.out, health_check backend/scripts/
- backend/Makefile: docker target uses -f ../deploy/docker/Dockerfile ../
- README: architecture tree, quickstart, config paths updated

Move root stray scripts (rename_models.*, run_m11_tests.sh, verify_build.sh,
gorm_bool_main.go) to backend/scripts/legacy/. All moves via git mv to
preserve history. Build, vet, SQLite tests, and docker compose config verified.
2026-07-07 14:44:12 +08:00

244 lines
7.3 KiB
Go

package ws
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
)
// === Event type constants tests ===
// Verify all Chatwoot-style dot-separated event naming conventions.
func TestMessageEventConstants(t *testing.T) {
assert.Equal(t, "message.created", EventMessageCreated)
assert.Equal(t, "message.updated", EventMessageUpdated)
assert.Equal(t, "message.deleted", EventMessageDeleted)
}
func TestConversationEventConstants(t *testing.T) {
assert.Equal(t, "conversation.created", EventConversationCreated)
assert.Equal(t, "conversation.updated", EventConversationUpdated)
assert.Equal(t, "conversation.resolved", EventConversationResolved)
assert.Equal(t, "conversation.opened", EventConversationOpened)
assert.Equal(t, "conversation.assigned", EventConversationAssigned)
assert.Equal(t, "conversation.unassigned", EventConversationUnassigned)
assert.Equal(t, "conversation.status_changed", EventConversationStatusChanged)
assert.Equal(t, "conversation.typing_on", EventConversationTypingOn)
assert.Equal(t, "conversation.typing_off", EventConversationTypingOff)
assert.Equal(t, "conversation.mentioned", EventConversationMentioned)
assert.Equal(t, "conversation.read", EventConversationRead)
}
func TestContactEventConstants(t *testing.T) {
assert.Equal(t, "contact.created", EventContactCreated)
assert.Equal(t, "contact.updated", EventContactUpdated)
assert.Equal(t, "contact.deleted", EventContactDeleted)
assert.Equal(t, "contact.merged", EventContactMerged)
}
func TestInboxEventConstants(t *testing.T) {
assert.Equal(t, "inbox.created", EventInboxCreated)
assert.Equal(t, "inbox.updated", EventInboxUpdated)
assert.Equal(t, "inbox.deleted", EventInboxDeleted)
}
func TestNotificationEventConstants(t *testing.T) {
assert.Equal(t, "notification.created", EventNotificationCreated)
assert.Equal(t, "notification.updated", EventNotificationUpdated)
assert.Equal(t, "notification.deleted", EventNotificationDeleted)
}
func TestPresenceEventConstants(t *testing.T) {
assert.Equal(t, "presence.update", EventPresenceUpdate)
assert.Equal(t, "account.cache_invalidated", EventAccountCacheInvalidated)
}
func TestAgentTypingEventConstants(t *testing.T) {
assert.Equal(t, "agent.typing_on", EventAgentTypingOn)
assert.Equal(t, "agent.typing_off", EventAgentTypingOff)
assert.Equal(t, "agent.online", EventAgentOnline)
assert.Equal(t, "agent.offline", EventAgentOffline)
}
func TestProtocolControlConstants(t *testing.T) {
assert.Equal(t, "subscribe.confirm", EventSubscribeConfirm)
assert.Equal(t, "unsubscribe.confirm", EventUnsubscribeConfirm)
assert.Equal(t, "subscribe.reject", EventSubscribeReject)
assert.Equal(t, "ping.response", EventPingResponse)
assert.Equal(t, "welcome", EventWelcome)
assert.Equal(t, "disconnect", EventDisconnect)
}
func TestEventNamingConvention_DotSeparated(t *testing.T) {
// All business events should follow Chatwoot's resource.action naming
events := []string{
EventMessageCreated, EventMessageUpdated, EventMessageDeleted,
EventConversationCreated, EventConversationUpdated, EventConversationResolved,
EventContactCreated, EventContactUpdated, EventContactDeleted, EventContactMerged,
EventInboxCreated, EventInboxUpdated, EventInboxDeleted,
EventNotificationCreated, EventNotificationUpdated, EventNotificationDeleted,
EventPresenceUpdate, EventAccountCacheInvalidated,
EventAgentTypingOn, EventAgentTypingOff, EventAgentOnline, EventAgentOffline,
}
for _, event := range events {
assert.Contains(t, event, ".", "event %s should follow resource.action naming", event)
}
}
// === WSMessage struct tests ===
func TestWSMessage_MarshalUnmarshal(t *testing.T) {
msg := WSMessage{
Event: EventMessageCreated,
Data: map[string]interface{}{"id": 1, "content": "hello"},
AccountID: 100,
}
data, err := json.Marshal(msg)
assert.NoError(t, err)
assert.Contains(t, string(data), "message.created")
assert.Contains(t, string(data), "account_id")
var decoded WSMessage
err = json.Unmarshal(data, &decoded)
assert.NoError(t, err)
assert.Equal(t, EventMessageCreated, decoded.Event)
assert.Equal(t, uint(100), decoded.AccountID)
}
func TestWSMessage_WithPerformer(t *testing.T) {
msg := WSMessage{
Event: EventConversationUpdated,
Data: map[string]interface{}{"status": "resolved"},
Performer: &Performer{
ID: 1,
Name: "Agent Smith",
Type: "user",
AvatarURL: "https://example.com/avatar.png",
},
}
data, err := json.Marshal(msg)
assert.NoError(t, err)
assert.Contains(t, string(data), "performer")
assert.Contains(t, string(data), "Agent Smith")
}
// === WSCommand struct tests ===
func TestWSCommand_MarshalUnmarshal(t *testing.T) {
cmd := WSCommand{
Command: "subscribe",
Data: `{"channel":"AccountChannel","account_id":100}`,
}
data, err := json.Marshal(cmd)
assert.NoError(t, err)
var decoded WSCommand
err = json.Unmarshal(data, &decoded)
assert.NoError(t, err)
assert.Equal(t, "subscribe", decoded.Command)
}
func TestWSCommand_CommandTypes(t *testing.T) {
commands := []string{"subscribe", "unsubscribe", "ping", "typing_on", "typing_off", "update_presence"}
for _, cmd := range commands {
wsCmd := WSCommand{Command: cmd}
assert.Equal(t, cmd, wsCmd.Command)
}
}
// === WSClaims struct tests ===
func TestWSClaims_UserAuth(t *testing.T) {
claims := WSClaims{
UserID: 1,
AccountID: 100,
Role: "agent",
Provider: "email",
IsContact: false,
}
assert.Equal(t, uint(1), claims.UserID)
assert.Equal(t, uint(100), claims.AccountID)
assert.Equal(t, "agent", claims.Role)
assert.False(t, claims.IsContact)
}
func TestWSClaims_ContactAuth(t *testing.T) {
claims := WSClaims{
AccountID: 100,
Role: "contact",
Provider: "pubsub_token",
PubsubToken: "abc123",
IsContact: true,
ContactID: 50,
InboxID: 10,
}
assert.True(t, claims.IsContact)
assert.Equal(t, uint(50), claims.ContactID)
assert.Equal(t, uint(10), claims.InboxID)
assert.NotEmpty(t, claims.PubsubToken)
}
// === SubscribeData struct tests ===
func TestSubscribeData_AccountChannel(t *testing.T) {
sd := SubscribeData{
Channel: "AccountChannel",
AccountID: 100,
}
assert.Equal(t, "AccountChannel", sd.Channel)
assert.Equal(t, uint(100), sd.AccountID)
}
func TestSubscribeData_ConversationChannel(t *testing.T) {
sd := SubscribeData{
Channel: "ConversationChannel",
AccountID: 100,
ConversationID: 42,
}
assert.Equal(t, "ConversationChannel", sd.Channel)
assert.Equal(t, uint(42), sd.ConversationID)
}
func TestSubscribeData_ContactAuth(t *testing.T) {
sd := SubscribeData{
Channel: "ConversationChannel",
AccountID: 100,
PubsubToken: "token_abc",
ConversationID: 42,
}
assert.NotEmpty(t, sd.PubsubToken)
}
// === TypingData struct tests ===
func TestTypingData(t *testing.T) {
td := TypingData{
ConversationID: 42,
AccountID: 100,
}
assert.Equal(t, uint(42), td.ConversationID)
assert.Equal(t, uint(100), td.AccountID)
}
// === Performer struct tests ===
func TestPerformer_UserType(t *testing.T) {
p := Performer{
ID: 1,
Name: "Agent One",
Type: "user",
AvatarURL: "https://example.com/avatar1.png",
}
assert.Equal(t, "user", p.Type)
}
func TestPerformer_ContactType(t *testing.T) {
p := Performer{
ID: 50,
Name: "Contact Five",
Type: "contact",
AvatarURL: "",
}
assert.Equal(t, "contact", p.Type)
}