166 lines
5.1 KiB
Go
166 lines
5.1 KiB
Go
package tencentim
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"os"
|
||
"strconv"
|
||
"strings"
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
func TestRealTencentIMCreateSendAndDestroyGroup(t *testing.T) {
|
||
if os.Getenv("TENCENT_IM_REAL_TEST") != "1" {
|
||
t.Skip("set TENCENT_IM_REAL_TEST=1 and Tencent IM env vars to run the real REST smoke test")
|
||
}
|
||
|
||
cfg := realRESTConfigFromEnv(t)
|
||
client, err := NewRESTClient(cfg)
|
||
if err != nil {
|
||
t.Fatalf("NewRESTClient failed: %v", err)
|
||
}
|
||
|
||
nowMS := time.Now().UTC().UnixMilli()
|
||
groupID := fmt.Sprintf("hy_codex_bc_r_%d", nowMS)
|
||
eventID := fmt.Sprintf("codex_real_im_%d", nowMS)
|
||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||
defer cancel()
|
||
|
||
// 真实 IM 测试必须使用临时 GroupID,避免污染业务全局/区域播报群。
|
||
if err := client.EnsureGroup(ctx, GroupSpec{
|
||
GroupID: groupID,
|
||
Name: "codex real im smoke",
|
||
Type: DefaultGroupType,
|
||
ApplyJoinOption: "FreeAccess",
|
||
}); err != nil {
|
||
t.Fatalf("real Tencent IM create group failed: %v", err)
|
||
}
|
||
t.Cleanup(func() {
|
||
cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||
defer cleanupCancel()
|
||
if err := client.DestroyGroup(cleanupCtx, groupID); err != nil {
|
||
t.Errorf("real Tencent IM destroy group cleanup failed: %v", err)
|
||
}
|
||
})
|
||
memberUserID := nowMS % 9_000_000_000
|
||
if err := realImportAccount(ctx, client, memberUserID); err != nil {
|
||
t.Fatalf("real Tencent IM account import failed: %v", err)
|
||
}
|
||
c2cEventID := fmt.Sprintf("codex_real_im_c2c_%d", nowMS)
|
||
c2cPayload, err := json.Marshal(map[string]any{
|
||
"event_id": c2cEventID,
|
||
"event_type": "codex_real_im_c2c_probe",
|
||
"sent_at_ms": nowMS,
|
||
})
|
||
if err != nil {
|
||
t.Fatalf("marshal c2c smoke payload failed: %v", err)
|
||
}
|
||
if err := client.PublishUserCustomMessage(ctx, CustomUserMessage{
|
||
ToAccount: strconv.FormatInt(memberUserID, 10),
|
||
EventID: c2cEventID,
|
||
Desc: "codex_real_im_c2c_probe",
|
||
Ext: "im_c2c_smoke",
|
||
PayloadJSON: c2cPayload,
|
||
}); err != nil {
|
||
t.Fatalf("real Tencent IM send C2C custom message failed: %v", err)
|
||
}
|
||
if err := realAddGroupMember(ctx, client, groupID, memberUserID); err != nil {
|
||
t.Fatalf("real Tencent IM add group member failed: %v", err)
|
||
}
|
||
|
||
payload, err := json.Marshal(map[string]any{
|
||
"event_id": eventID,
|
||
"broadcast_type": "codex_real_im_smoke",
|
||
"scope": "region",
|
||
"app_code": "codex",
|
||
"region_id": 210,
|
||
"sent_at_ms": nowMS,
|
||
"action": map[string]any{
|
||
"type": "enter_room",
|
||
"room_id": "codex-room-smoke",
|
||
},
|
||
})
|
||
if err != nil {
|
||
t.Fatalf("marshal smoke payload failed: %v", err)
|
||
}
|
||
|
||
// 真正向腾讯云 IM REST 发送 TIMCustomElem;成功只表示腾讯服务端接受消息,不代表有客户端在线展示。
|
||
if err := client.PublishGroupCustomMessage(ctx, CustomGroupMessage{
|
||
GroupID: groupID,
|
||
EventID: eventID,
|
||
Desc: "codex_real_im_smoke",
|
||
Ext: "im_broadcast",
|
||
PayloadJSON: payload,
|
||
}); err != nil {
|
||
t.Fatalf("real Tencent IM send group custom message failed: %v", err)
|
||
}
|
||
if err := client.DeleteGroupMember(ctx, groupID, memberUserID); err != nil {
|
||
t.Fatalf("real Tencent IM delete group member failed: %v", err)
|
||
}
|
||
|
||
t.Logf("real Tencent IM smoke passed: sdk_app_id=%d group_id=%s group_event_id=%s c2c_event_id=%s removed_user_id=%d", cfg.SDKAppID, groupID, eventID, c2cEventID, memberUserID)
|
||
}
|
||
|
||
func realImportAccount(ctx context.Context, client *RESTClient, userID int64) error {
|
||
return client.ImportAccount(ctx, AccountProfile{
|
||
UserID: userID,
|
||
Nick: "codex-im-smoke",
|
||
})
|
||
}
|
||
|
||
func realAddGroupMember(ctx context.Context, client *RESTClient, groupID string, userID int64) error {
|
||
var response restResponse
|
||
if err := client.post(ctx, "v4/group_open_http_svc/add_group_member", map[string]any{
|
||
"GroupId": groupID,
|
||
"Silence": 1,
|
||
"MemberList": []map[string]any{
|
||
{"Member_Account": strconv.FormatInt(userID, 10)},
|
||
},
|
||
}, &response); err != nil {
|
||
return err
|
||
}
|
||
return response.err()
|
||
}
|
||
|
||
func realRESTConfigFromEnv(t *testing.T) RESTConfig {
|
||
t.Helper()
|
||
|
||
sdkAppID, err := strconv.ParseInt(requiredEnv(t, "TENCENT_IM_SDK_APP_ID"), 10, 64)
|
||
if err != nil || sdkAppID <= 0 {
|
||
t.Fatalf("TENCENT_IM_SDK_APP_ID is invalid")
|
||
}
|
||
endpoint := strings.TrimSpace(os.Getenv("TENCENT_IM_ENDPOINT"))
|
||
if endpoint == "" {
|
||
endpoint = DefaultEndpoint
|
||
}
|
||
adminUserSigTTL := time.Hour
|
||
if rawTTL := strings.TrimSpace(os.Getenv("TENCENT_IM_ADMIN_USERSIG_TTL")); rawTTL != "" {
|
||
parsed, err := time.ParseDuration(rawTTL)
|
||
if err != nil || parsed <= 0 {
|
||
t.Fatalf("TENCENT_IM_ADMIN_USERSIG_TTL is invalid")
|
||
}
|
||
adminUserSigTTL = parsed
|
||
}
|
||
|
||
return RESTConfig{
|
||
SDKAppID: sdkAppID,
|
||
SecretKey: requiredEnv(t, "TENCENT_IM_SECRET_KEY"),
|
||
AdminIdentifier: requiredEnv(t, "TENCENT_IM_ADMIN_IDENTIFIER"),
|
||
AdminUserSigTTL: adminUserSigTTL,
|
||
Endpoint: endpoint,
|
||
GroupType: DefaultGroupType,
|
||
}
|
||
}
|
||
|
||
func requiredEnv(t *testing.T, key string) string {
|
||
t.Helper()
|
||
|
||
value := strings.TrimSpace(os.Getenv(key))
|
||
if value == "" {
|
||
t.Fatalf("%s is required for real Tencent IM smoke test", key)
|
||
}
|
||
return value
|
||
}
|