Files
backend/internal/pkg/jpush/vendor/payload_compat_test.go
lafay a0e210feab
All checks were successful
Build Backend / build (push) Successful in 3m1s
Build Backend / build-docker (push) Successful in 2m10s
feat(server): implement chat file TTL cleanup and enhance JPush vendor channel support
Add FileCleanupWorker for automatic expiration of chat files with configurable retention period and batch processing. Files uploaded via `/api/v1/uploads/files` are tracked in `uploaded_files` table with expiration timestamps, then deleted from S3 and database upon expiry. Expired file URLs are injected as `"expired": true` in message responses.

Extend JPush push notification configuration with vendor-specific channel_id support (xiaomi, huawei, oppo, vivo, meizu, honor, fcm) for differentiating system vs chat notifications. Add iOS APNs thread-id grouping configuration for notification categorization.
2026-06-17 20:41:55 +08:00

136 lines
4.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package vendor
import (
"encoding/json"
"strings"
"testing"
"with_you/internal/config"
)
// TestPayloadEmptyConfigMatchesLegacy 验证:未配置任何 channel/模板时,
// Factory 产出的推送 payload 与改动前 BuildNotification 行为一致。
//
// 改动前 BuildNotification(title, body, "message", extras) 产出:
// notification: { alert, android:{alert,title,extras:{notification_type}+extras,priority:1}, ios:{alert:{title,body},sound:default,badge:+1,extras} }
// options: { time_to_live: 86400, apns_production } // 无 third_party_channel
func TestPayloadEmptyConfigMatchesLegacy(t *testing.T) {
factory := NewFactory()
cfg := config.ChannelConfig{} // 全空配置
params := MessageParams{
Scene: SceneChat,
Title: "张三",
Body: "你好",
MessageType: "message",
Extra: map[string]any{
"message_id": "m1",
"conversation_id": "c1",
"sender_id": "u1",
"category": "chat",
},
}
payload := factory.Build(params, cfg)
// 1. TPC 必须为 nil无 third_party_channel 下发,与改动前一致)
if payload.TPC != nil {
t.Fatalf("TPC should be nil when all channel config empty, got: %+v", payload.TPC)
}
notif := payload.Notification
if notif == nil || notif.Android == nil || notif.IOS == nil {
t.Fatalf("notification/android/ios must not be nil")
}
// 2. Android 字段对比
if notif.Android.Alert != "你好" || notif.Android.Title != "张三" || notif.Android.Priority != 1 {
t.Errorf("Android basic fields mismatch: %+v", notif.Android)
}
if notif.Android.ChannelID != "" {
t.Errorf("Android.ChannelID = %q, want empty", notif.Android.ChannelID)
}
if notif.Android.Extras["notification_type"] != "message" || notif.Android.Extras["message_id"] != "m1" {
t.Errorf("Android.Extras mismatch: %+v", notif.Android.Extras)
}
// 3. iOS 字段对比
if notif.IOS.Sound != "default" || notif.IOS.Badge != "+1" {
t.Errorf("IOS basic fields mismatch: %+v", notif.IOS)
}
if notif.IOS.ThreadID != "" {
t.Errorf("IOS.ThreadID = %q, want empty", notif.IOS.ThreadID)
}
if notif.IOS.Extras["notification_type"] != "message" {
t.Errorf("IOS.Extras[notification_type] mismatch: %+v", notif.IOS.Extras)
}
// 4. 序列化 JSON 确认厂商私有字段不出现
jsonBytes, err := json.Marshal(notif)
if err != nil {
t.Fatalf("marshal: %v", err)
}
jsonStr := string(jsonBytes)
for _, key := range []string{`"channel_id"`, `"thread-id"`, `"third_party_channel"`, `"mi_template"`, `"private_msg_template_id"`} {
if strings.Contains(jsonStr, key) {
t.Errorf("notification JSON should not contain %s when config empty, got: %s", key, jsonStr)
}
}
t.Logf("notification JSON: %s", jsonStr)
}
// TestPayloadWithChannelConfig 验证:配置 channel_id 后正确下发,且不影响通用通知字段
func TestPayloadWithChannelConfig(t *testing.T) {
factory := NewFactory()
cfg := config.ChannelConfig{
System: "sys_channel",
Chat: "chat_channel",
}
params := MessageParams{Scene: SceneChat, Title: "T", Body: "B", MessageType: "chat_message"}
payload := factory.Build(params, cfg)
// 通用通知仍正常
if payload.Notification.Android.Title != "T" {
t.Errorf("Android.Title = %q, want T", payload.Notification.Android.Title)
}
// Android channel_id 跟随场景chat
if payload.Notification.Android.ChannelID != "chat_channel" {
t.Errorf("Android.ChannelID = %q, want chat_channel", payload.Notification.Android.ChannelID)
}
// TPC 各厂商 channel_id 下发(华为/VIVO 等回退全局)
if payload.TPC == nil {
t.Fatal("TPC should not be nil when channel config present")
}
if payload.TPC.Huawei["channel_id"] != "chat_channel" {
t.Errorf("Huawei channel_id = %v, want chat_channel", payload.TPC.Huawei["channel_id"])
}
t.Logf("TPC JSON: %s", mustJSON(payload.TPC))
}
// TestPayloadXiaomiTemplateOptional 验证:仅配 channel_id 不配模板时,小米不下发模板字段
func TestPayloadXiaomiTemplateOptional(t *testing.T) {
factory := NewFactory()
cfg := config.ChannelConfig{
Vendor: config.VendorChannel{
Xiaomi: config.VendorChannelID{Chat: "mi_chat"},
},
}
params := MessageParams{Scene: SceneChat, Title: "T", Body: "B"}
payload := factory.Build(params, cfg)
if payload.TPC == nil || payload.TPC.Xiaomi == nil {
t.Fatal("Xiaomi TPC should exist")
}
if _, ok := payload.TPC.Xiaomi["mi_template_id"]; ok {
t.Error("Xiaomi should NOT contain mi_template_id when template not configured")
}
if payload.TPC.Xiaomi["channel_id"] != "mi_chat" {
t.Errorf("Xiaomi channel_id = %v, want mi_chat", payload.TPC.Xiaomi["channel_id"])
}
}
func mustJSON(v any) string {
b, _ := json.Marshal(v)
return string(b)
}