This commit is contained in:
2026-06-10 07:24:13 +08:00
parent ac7f351308
commit 2bb54b21a5
47 changed files with 2912 additions and 305 deletions
@@ -27,7 +27,18 @@ func setupNotificationDB(t *testing.T) *gorm.DB {
t.Helper()
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
assert.NoError(t, err)
err = db.AutoMigrate(&model.Notification{}, &model.User{}, &model.Account{}, &model.NotificationPreference{})
err = db.AutoMigrate(
&model.Notification{},
&model.User{},
&model.Account{},
&model.NotificationPreference{},
&model.Contact{},
&model.Inbox{},
&model.ContactInbox{},
&model.Conversation{},
&model.Message{},
&model.AgentBot{},
)
assert.NoError(t, err)
return db
}
@@ -148,6 +159,112 @@ func TestNotificationHandler_List_ChatwootEnvelopeAndIncludes(t *testing.T) {
sqlDB.Close()
}
func TestNotificationHandler_List_SerializesPushEventActors(t *testing.T) {
db := setupNotificationDB(t)
handler := setupNotificationHandler(t, db)
router := setupNotificationRouter(handler)
accountID := uint(1)
user := &model.User{Name: "Agent Smith", DisplayName: "Smith", Email: "smith@example.com", Password: "pass", AccountID: accountID, AvatarURL: "https://example.com/agent.png", Available: true}
contact := &model.Contact{AccountID: accountID, Name: "Alice", Email: "alice@example.com", PhoneNumber: "+15551234567", AvatarURL: "https://example.com/alice.png", Identifier: "alice-1", AdditionalAttributes: []byte(`{"city":"Paris"}`), CustomAttributes: []byte(`{"tier":"gold"}`), Blocked: true}
inbox := &model.Inbox{AccountID: accountID, Name: "Support", ChannelType: "Channel::Api", ChannelID: 1}
require.NoError(t, db.Create(user).Error)
require.NoError(t, db.Create(contact).Error)
require.NoError(t, db.Create(inbox).Error)
contactInbox := &model.ContactInbox{ContactID: contact.ID, InboxID: inbox.ID, SourceID: "source-1", HMACVerified: true}
require.NoError(t, db.Create(contactInbox).Error)
now := time.Now().Unix()
displayID := uint(42)
conversation := &model.Conversation{AccountID: accountID, InboxID: inbox.ID, ContactID: contact.ID, ContactInboxID: &contactInbox.ID, DisplayID: &displayID, ChannelType: "Channel::Api", Channel: "api", Status: "open", Priority: "urgent", LastActivityAt: &now}
require.NoError(t, db.Create(conversation).Error)
message := &model.Message{ConversationID: conversation.ID, AccountID: accountID, InboxID: inbox.ID, SenderID: &contact.ID, SenderType: string(model.SenderTypeContact), Content: "Hello", ContentType: "text", MessageType: "incoming", Status: "sent"}
require.NoError(t, db.Create(message).Error)
notification := &model.Notification{UserID: user.ID, AccountID: &accountID, NotificationType: "assigned_conversation_new_message", PrimaryActorType: "Conversation", PrimaryActorID: conversation.ID, SecondaryActorType: "Contact", SecondaryActorID: contact.ID}
require.NoError(t, db.Create(notification).Error)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/api/v1/accounts/1/notifications", nil)
req.Header.Set("X-User-ID", strconv.FormatUint(uint64(user.ID), 10))
router.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
var body map[string]any
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &body))
payload := body["data"].(map[string]any)["payload"].([]any)
require.Len(t, payload, 1)
item := payload[0].(map[string]any)
primary := item["primary_actor"].(map[string]any)
assert.Equal(t, float64(displayID), primary["id"])
assert.Equal(t, "Channel::Api", primary["channel"])
assert.Equal(t, float64(inbox.ID), primary["inbox_id"])
assert.Equal(t, "open", primary["status"])
assert.Equal(t, float64(now), primary["last_activity_at"])
contactInboxPayload := primary["contact_inbox"].(map[string]any)
assert.Equal(t, "source-1", contactInboxPayload["source_id"])
contactInboxInbox := contactInboxPayload["inbox"].(map[string]any)
assert.Equal(t, float64(inbox.ID), contactInboxInbox["id"])
assert.Equal(t, "Support", contactInboxInbox["name"])
assert.Equal(t, "Channel::Api", contactInboxInbox["channel_type"])
assert.NotContains(t, contactInboxPayload, "contact_id")
assert.NotContains(t, contactInboxPayload, "hmac_token")
messages := primary["messages"].([]any)
require.Len(t, messages, 1)
assert.Equal(t, "Hello", messages[0].(map[string]any)["content"])
meta := primary["meta"].(map[string]any)
sender := meta["sender"].(map[string]any)
assert.Equal(t, float64(contact.ID), sender["id"])
assert.Equal(t, "alice@example.com", sender["email"])
assert.Equal(t, "+15551234567", sender["phone_number"])
secondary := item["secondary_actor"].(map[string]any)
assert.Equal(t, "contact", secondary["type"])
assert.Equal(t, true, secondary["blocked"])
assert.Equal(t, "https://example.com/alice.png", secondary["thumbnail"])
serializedUser := item["user"].(map[string]any)
assert.Equal(t, float64(user.ID), serializedUser["id"])
assert.Equal(t, "Agent Smith", serializedUser["name"])
assert.Equal(t, "Smith", serializedUser["available_name"])
assert.Equal(t, "https://example.com/agent.png", serializedUser["avatar_url"])
assert.Equal(t, "user", serializedUser["type"])
assert.Equal(t, "online", serializedUser["availability_status"])
assert.Equal(t, "https://example.com/agent.png", serializedUser["thumbnail"])
sqlDB, _ := db.DB()
sqlDB.Close()
}
func TestNotificationHandler_List_ActorSerializationIsAccountScoped(t *testing.T) {
db := setupNotificationDB(t)
handler := setupNotificationHandler(t, db)
router := setupNotificationRouter(handler)
accountID := uint(1)
otherAccountID := uint(2)
user := &model.User{Name: "Scoped Agent", Email: "scoped-agent@example.com", Password: "pass", AccountID: accountID}
otherContact := &model.Contact{AccountID: otherAccountID, Name: "Other Contact", Email: "other@example.com"}
require.NoError(t, db.Create(user).Error)
require.NoError(t, db.Create(otherContact).Error)
notification := &model.Notification{UserID: user.ID, AccountID: &accountID, NotificationType: "contact_created", PrimaryActorType: "Contact", PrimaryActorID: otherContact.ID}
require.NoError(t, db.Create(notification).Error)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/api/v1/accounts/1/notifications", nil)
req.Header.Set("X-User-ID", strconv.FormatUint(uint64(user.ID), 10))
router.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
var body map[string]any
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &body))
payload := body["data"].(map[string]any)["payload"].([]any)
require.Len(t, payload, 1)
primary := payload[0].(map[string]any)["primary_actor"].(map[string]any)
assert.Equal(t, float64(otherContact.ID), primary["id"])
assert.Equal(t, "Contact", primary["type"])
assert.Equal(t, map[string]any{}, primary["meta"])
sqlDB, _ := db.DB()
sqlDB.Close()
}
func TestNotificationGet(t *testing.T) {
db := setupNotificationDB(t)
handler := setupNotificationHandler(t, db)