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) } 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 event_id=%s removed_user_id=%d", cfg.SDKAppID, groupID, eventID, 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 }