2026-05-09 18:01:08 +08:00

243 lines
7.4 KiB
Go

package regionimgroup
import (
"context"
"strings"
"testing"
"time"
"chatapp3-golang/internal/config"
"chatapp3-golang/internal/model"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func TestCreateRegionIMGroupSavesActiveGroup(t *testing.T) {
service, db := newRegionIMGroupTestService(t)
fakeIM := &fakeIMGateway{}
service.im = fakeIM
resp, err := service.CreateRegionIMGroup(context.Background(), RegionIMGroupRequest{
SysOrigin: "LIKEI",
RegionCode: "AR",
RegionName: "Arab",
})
if err != nil {
t.Fatalf("CreateRegionIMGroup() error = %v", err)
}
if resp.Status != imGroupStatusActive || resp.GroupID != defaultRegionIMGroupID("LIKEI", "AR") {
t.Fatalf("CreateRegionIMGroup() response = %+v", resp)
}
if len(fakeIM.created) != 1 || fakeIM.created[0].groupID != resp.GroupID {
t.Fatalf("created groups = %+v, want one %s", fakeIM.created, resp.GroupID)
}
var row model.VoiceRoomRegionIMGroup
if err := db.Where("sys_origin = ? AND region_code = ?", "LIKEI", "AR").First(&row).Error; err != nil {
t.Fatalf("load im group: %v", err)
}
if row.Status != imGroupStatusActive || row.GroupID != resp.GroupID {
t.Fatalf("stored im group = %+v", row)
}
}
func TestDefaultRegionIMGroupIDKeepsASCIIRegionReadable(t *testing.T) {
got := defaultRegionIMGroupID("LIKEI", "IR")
if got != "VRRP_LIKEI_IR" {
t.Fatalf("defaultRegionIMGroupID() = %s, want VRRP_LIKEI_IR", got)
}
}
func TestDefaultRegionIMGroupIDKeepsNonASCIIRegionsDistinct(t *testing.T) {
middleEast := defaultRegionIMGroupID("LIKEI", "\u4e2d\u4e1c")
southAsia := defaultRegionIMGroupID("LIKEI", "\u5357\u4e9a\u533a\u57df")
if middleEast == southAsia {
t.Fatalf("non-ascii regions share group id %s", middleEast)
}
if middleEast == "VRRP_LIKEI" || southAsia == "VRRP_LIKEI" {
t.Fatalf("non-ascii region group ids must not collapse: %s %s", middleEast, southAsia)
}
if !strings.HasPrefix(middleEast, "VRRP_LIKEI_REGION_") ||
!strings.HasPrefix(southAsia, "VRRP_LIKEI_REGION_") {
t.Fatalf("group ids = %s %s, want hashed region suffix", middleEast, southAsia)
}
}
func TestDefaultRegionIMGroupNameIsGeneric(t *testing.T) {
got := defaultRegionIMGroupName("LIKEI", "IR", "Iran")
if got != "region-im-group-LIKEI-Iran" {
t.Fatalf("defaultRegionIMGroupName() = %s, want generic region name", got)
}
if strings.Contains(got, "red-packet") {
t.Fatalf("defaultRegionIMGroupName() contains red-packet: %s", got)
}
}
func TestSendRegionBroadcastResolvesRegionAndEnrichesGiftPayload(t *testing.T) {
service, db := newRegionIMGroupTestService(t)
fakeIM := &fakeIMGateway{}
service.im = fakeIM
ctx := context.Background()
now := time.Now()
if err := db.Create(&model.VoiceRoomRegionIMGroup{
ID: 3002,
SysOrigin: "LIKEI",
RegionCode: "AR",
RegionName: "Arab",
GroupID: "@region-ar",
GroupName: "region-ar",
Status: imGroupStatusActive,
CreateTime: now,
UpdateTime: now,
}).Error; err != nil {
t.Fatalf("seed im group: %v", err)
}
resp, err := service.SendRegionBroadcast(ctx, RegionBroadcastRequest{
SysOrigin: "LIKEI",
SenderUserID: flexibleInt64(1001),
Type: "SEND_GIFT",
Data: map[string]any{
"roomId": "9001",
"giftId": "5001",
"giftQuantity": 2,
"acceptUserIds": []any{"1002"},
},
})
if err != nil {
t.Fatalf("SendRegionBroadcast() error = %v", err)
}
if resp.RegionCode != "AR" || resp.GroupID != "@region-ar" || resp.Type != "SEND_GIFT" {
t.Fatalf("SendRegionBroadcast() response = %+v", resp)
}
if len(fakeIM.sent) != 1 {
t.Fatalf("sent messages = %+v, want one region message", fakeIM.sent)
}
if fakeIM.sent[0].groupID != "@region-ar" {
t.Fatalf("sent group = %s, want @region-ar", fakeIM.sent[0].groupID)
}
body, ok := fakeIM.sent[0].body.(map[string]any)
if !ok {
t.Fatalf("sent body type = %T", fakeIM.sent[0].body)
}
if body["type"] != "SEND_GIFT" {
t.Fatalf("sent type = %v, want SEND_GIFT", body["type"])
}
data, ok := body["data"].(map[string]any)
if !ok {
t.Fatalf("sent data type = %T", body["data"])
}
if data["regionCode"] != "AR" || data["actualAccount"] != "1001" ||
data["userNickname"] != "sender" || data["acceptNickname"] != "claimer" {
t.Fatalf("sent data = %+v", data)
}
}
func TestSendRegionBroadcastPrefersJavaRegionResolver(t *testing.T) {
service, db := newRegionIMGroupTestService(t)
service.regionResolver = fakeRegionResolver{
regions: map[string]string{"LIKEI|SA": "中东区"},
}
fakeIM := &fakeIMGateway{}
service.im = fakeIM
now := time.Now()
if err := db.Create(&model.VoiceRoomRegionIMGroup{
ID: 3003,
SysOrigin: "LIKEI",
RegionCode: "中东区",
RegionName: "Middle East",
GroupID: "@region-middle-east",
GroupName: "region-middle-east",
Status: imGroupStatusActive,
CreateTime: now,
UpdateTime: now,
}).Error; err != nil {
t.Fatalf("seed im group: %v", err)
}
resp, err := service.SendRegionBroadcast(context.Background(), RegionBroadcastRequest{
SysOrigin: "LIKEI",
SenderUserID: flexibleInt64(1001),
Type: "SEND_GIFT",
Data: map[string]any{"roomId": "9001"},
})
if err != nil {
t.Fatalf("SendRegionBroadcast() error = %v", err)
}
if resp.RegionCode != "中东区" || resp.GroupID != "@region-middle-east" {
t.Fatalf("SendRegionBroadcast() response = %+v, want Java region group", resp)
}
}
func newRegionIMGroupTestService(t *testing.T) (*Service, *gorm.DB) {
t.Helper()
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
if err != nil {
t.Fatalf("open sqlite: %v", err)
}
if err := db.AutoMigrate(
&model.UserBaseInfo{},
&model.VoiceRoomRegionCountry{},
&model.VoiceRoomRegionIMGroup{},
); err != nil {
t.Fatalf("auto migrate: %v", err)
}
now := time.Now()
if err := db.Create(&[]model.UserBaseInfo{
{ID: 1001, Account: "1001", UserNickname: "sender", UserAvatar: "sender.png", OriginSys: "LIKEI", CountryCode: "SA", CountryName: "Saudi Arabia", CreateTime: now},
{ID: 1002, Account: "1002", UserNickname: "claimer", UserAvatar: "claimer.png", OriginSys: "LIKEI", CountryCode: "SA", CountryName: "Saudi Arabia", CreateTime: now},
}).Error; err != nil {
t.Fatalf("seed users: %v", err)
}
if err := db.Create(&model.VoiceRoomRegionCountry{
ID: 1,
SysOrigin: "LIKEI",
RegionCode: "AR",
CountryCode: "SA",
Enabled: true,
CreateTime: now,
UpdateTime: now,
}).Error; err != nil {
t.Fatalf("seed region: %v", err)
}
return NewService(config.Config{}, db), db
}
type fakeIMCreateRecord struct {
groupID string
groupName string
}
type fakeIMSendRecord struct {
groupID string
body any
}
type fakeIMGateway struct {
created []fakeIMCreateRecord
sent []fakeIMSendRecord
}
func (g *fakeIMGateway) CreateAVChatRoom(_ context.Context, groupID string, groupName string) (string, error) {
g.created = append(g.created, fakeIMCreateRecord{groupID: groupID, groupName: groupName})
return groupID, nil
}
func (g *fakeIMGateway) SendCustomGroupMessage(_ context.Context, groupID string, body any) error {
g.sent = append(g.sent, fakeIMSendRecord{groupID: groupID, body: body})
return nil
}
type fakeRegionResolver struct {
regions map[string]string
err error
}
func (g fakeRegionResolver) ResolveRegionCodeByCountryCode(_ context.Context, sysOrigin string, countryCode string) (string, error) {
if g.err != nil {
return "", g.err
}
key := strings.ToUpper(strings.TrimSpace(sysOrigin)) + "|" + strings.ToUpper(strings.TrimSpace(countryCode))
return strings.TrimSpace(g.regions[key]), nil
}