322 lines
11 KiB
Go
322 lines
11 KiB
Go
package tencentim
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// roundTripFunc 允许测试拦截腾讯云 REST 请求而不访问外网。
|
|
type roundTripFunc func(*http.Request) (*http.Response, error)
|
|
|
|
// RoundTrip 实现 http.RoundTripper。
|
|
func (f roundTripFunc) RoundTrip(request *http.Request) (*http.Response, error) {
|
|
return f(request)
|
|
}
|
|
|
|
// TestRESTClientEnsureRoomGroupBuildsTencentRequest 锁定建群请求的 REST 命令和核心字段。
|
|
func TestRESTClientEnsureRoomGroupBuildsTencentRequest(t *testing.T) {
|
|
var capturedPath string
|
|
var capturedBody map[string]any
|
|
client := newTestRESTClient(t, func(request *http.Request) (*http.Response, error) {
|
|
capturedPath = request.URL.Path
|
|
if err := json.NewDecoder(request.Body).Decode(&capturedBody); err != nil {
|
|
t.Fatalf("decode request body failed: %v", err)
|
|
}
|
|
|
|
return okRESTResponse(), nil
|
|
})
|
|
|
|
if err := client.EnsureRoomGroup(context.Background(), "room-1001", 10001); err != nil {
|
|
t.Fatalf("EnsureRoomGroup failed: %v", err)
|
|
}
|
|
|
|
if capturedPath != "/"+createGroupCommand {
|
|
t.Fatalf("path mismatch: got %q", capturedPath)
|
|
}
|
|
if capturedBody["GroupId"] != "room-1001" || capturedBody["Type"] != DefaultGroupType {
|
|
t.Fatalf("unexpected create group body: %+v", capturedBody)
|
|
}
|
|
}
|
|
|
|
// TestRESTClientPublishRoomEventBuildsCustomMessage 锁定房间系统事件的自定义消息载荷。
|
|
func TestRESTClientPublishRoomEventBuildsCustomMessage(t *testing.T) {
|
|
var capturedPath string
|
|
var capturedBody map[string]any
|
|
client := newTestRESTClient(t, func(request *http.Request) (*http.Response, error) {
|
|
capturedPath = request.URL.Path
|
|
if err := json.NewDecoder(request.Body).Decode(&capturedBody); err != nil {
|
|
t.Fatalf("decode request body failed: %v", err)
|
|
}
|
|
|
|
return okRESTResponse(), nil
|
|
})
|
|
|
|
err := client.PublishRoomEvent(context.Background(), RoomEvent{
|
|
GroupID: "room_1001",
|
|
EventID: "evt-1",
|
|
RoomID: "room-1001",
|
|
EventType: "room_gift_sent",
|
|
ActorUserID: 10001,
|
|
TargetUserID: 10002,
|
|
GiftValue: 20,
|
|
RoomVersion: 7,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("PublishRoomEvent failed: %v", err)
|
|
}
|
|
|
|
if capturedPath != "/"+sendGroupMsgCommand {
|
|
t.Fatalf("path mismatch: got %q", capturedPath)
|
|
}
|
|
if capturedBody["GroupId"] != "room_1001" {
|
|
t.Fatalf("group id mismatch: %+v", capturedBody)
|
|
}
|
|
body, ok := capturedBody["MsgBody"].([]any)
|
|
if !ok || len(body) != 1 {
|
|
t.Fatalf("unexpected msg body: %+v", capturedBody["MsgBody"])
|
|
}
|
|
element, ok := body[0].(map[string]any)
|
|
if !ok || element["MsgType"] != "TIMCustomElem" {
|
|
t.Fatalf("unexpected message element: %+v", body[0])
|
|
}
|
|
content, ok := element["MsgContent"].(map[string]any)
|
|
if !ok || content["Ext"] != "room_system_message" || content["Desc"] != "room_gift_sent" {
|
|
t.Fatalf("unexpected custom content: %+v", element["MsgContent"])
|
|
}
|
|
if !strings.Contains(content["Data"].(string), `"event_id":"evt-1"`) {
|
|
t.Fatalf("event payload missing event_id: %s", content["Data"])
|
|
}
|
|
if !strings.Contains(content["Data"].(string), `"room_id":"room-1001"`) {
|
|
t.Fatalf("event payload must keep internal room_id: %s", content["Data"])
|
|
}
|
|
}
|
|
|
|
func TestRESTClientPublishGroupCustomMessageBuildsBroadcastPayload(t *testing.T) {
|
|
var capturedBody map[string]any
|
|
client := newTestRESTClient(t, func(request *http.Request) (*http.Response, error) {
|
|
if err := json.NewDecoder(request.Body).Decode(&capturedBody); err != nil {
|
|
t.Fatalf("decode request body failed: %v", err)
|
|
}
|
|
return okRESTResponse(), nil
|
|
})
|
|
|
|
err := client.PublishGroupCustomMessage(context.Background(), CustomGroupMessage{
|
|
GroupID: "hy_lalu_bc_r_1001",
|
|
EventID: "gift_broadcast:room-1:cmd-1",
|
|
Desc: "super_gift",
|
|
Ext: "im_broadcast",
|
|
FromAccount: "administrator",
|
|
PayloadJSON: json.RawMessage(`{"event_id":"gift_broadcast:room-1:cmd-1","broadcast_type":"super_gift"}`),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("PublishGroupCustomMessage failed: %v", err)
|
|
}
|
|
|
|
if capturedBody["GroupId"] != "hy_lalu_bc_r_1001" || capturedBody["From_Account"] != "administrator" {
|
|
t.Fatalf("broadcast target mismatch: %+v", capturedBody)
|
|
}
|
|
if capturedBody["CloudCustomData"] != `{"event_id":"gift_broadcast:room-1:cmd-1","broadcast_type":"super_gift"}` {
|
|
t.Fatalf("cloud custom data mismatch: %+v", capturedBody)
|
|
}
|
|
}
|
|
|
|
func TestRESTClientPublishUserCustomMessageBuildsC2CPayload(t *testing.T) {
|
|
var capturedPath string
|
|
var capturedBody map[string]any
|
|
client := newTestRESTClient(t, func(request *http.Request) (*http.Response, error) {
|
|
capturedPath = request.URL.Path
|
|
if err := json.NewDecoder(request.Body).Decode(&capturedBody); err != nil {
|
|
t.Fatalf("decode request body: %v", err)
|
|
}
|
|
return okRESTResponse(), nil
|
|
})
|
|
|
|
err := client.PublishUserCustomMessage(context.Background(), CustomUserMessage{
|
|
ToAccount: "10001",
|
|
EventID: "evt-wallet-1",
|
|
Desc: "WalletBalanceChanged",
|
|
Ext: "wallet_notice",
|
|
PayloadJSON: json.RawMessage(`{"event_id":"evt-wallet-1"}`),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("PublishUserCustomMessage failed: %v", err)
|
|
}
|
|
if capturedPath != "/"+sendC2CMsgCommand {
|
|
t.Fatalf("unexpected command path: %s", capturedPath)
|
|
}
|
|
if capturedBody["To_Account"] != "10001" || capturedBody["SyncOtherMachine"].(float64) != 2 {
|
|
t.Fatalf("unexpected c2c target: %+v", capturedBody)
|
|
}
|
|
if capturedBody["From_Account"] != "administrator" {
|
|
t.Fatalf("unexpected c2c sender: %+v", capturedBody)
|
|
}
|
|
body, ok := capturedBody["MsgBody"].([]any)
|
|
if !ok || len(body) != 1 {
|
|
t.Fatalf("unexpected msg body: %+v", capturedBody["MsgBody"])
|
|
}
|
|
element := body[0].(map[string]any)
|
|
if element["MsgType"] != "TIMCustomElem" {
|
|
t.Fatalf("unexpected msg type: %+v", element)
|
|
}
|
|
content := element["MsgContent"].(map[string]any)
|
|
if content["Desc"] != "WalletBalanceChanged" || content["Ext"] != "wallet_notice" || content["Data"] != `{"event_id":"evt-wallet-1"}` {
|
|
t.Fatalf("unexpected custom content: %+v", content)
|
|
}
|
|
}
|
|
|
|
func TestRESTClientImportAccountBuildsTencentRequest(t *testing.T) {
|
|
var capturedPath string
|
|
var capturedBody map[string]any
|
|
client := newTestRESTClient(t, func(request *http.Request) (*http.Response, error) {
|
|
capturedPath = request.URL.Path
|
|
if err := json.NewDecoder(request.Body).Decode(&capturedBody); err != nil {
|
|
t.Fatalf("decode request body failed: %v", err)
|
|
}
|
|
return okRESTResponse(), nil
|
|
})
|
|
|
|
if err := client.ImportAccount(context.Background(), AccountProfile{
|
|
UserID: 10002,
|
|
Nick: "Lingxian",
|
|
FaceURL: "https://cdn.example/avatar.png",
|
|
}); err != nil {
|
|
t.Fatalf("ImportAccount failed: %v", err)
|
|
}
|
|
|
|
if capturedPath != "/"+accountImportCommand {
|
|
t.Fatalf("path mismatch: got %q", capturedPath)
|
|
}
|
|
if capturedBody["UserID"] != "10002" || capturedBody["Nick"] != "Lingxian" || capturedBody["FaceUrl"] != "https://cdn.example/avatar.png" {
|
|
t.Fatalf("unexpected account import body: %+v", capturedBody)
|
|
}
|
|
}
|
|
|
|
func TestRESTClientImportAccountTreatsExistingAccountAsSuccess(t *testing.T) {
|
|
client := newTestRESTClient(t, func(_ *http.Request) (*http.Response, error) {
|
|
payload := []byte(`{"ActionStatus":"FAIL","ErrorCode":70107,"ErrorInfo":"account already exists"}`)
|
|
return &http.Response{
|
|
StatusCode: http.StatusOK,
|
|
Body: io.NopCloser(bytes.NewReader(payload)),
|
|
Header: make(http.Header),
|
|
}, nil
|
|
})
|
|
|
|
if err := client.ImportAccount(context.Background(), AccountProfile{UserID: 10002}); err != nil {
|
|
t.Fatalf("existing account must be idempotent success: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestRESTClientDestroyGroupBuildsTencentRequest 锁定临时群清理的 REST 请求。
|
|
func TestRESTClientDestroyGroupBuildsTencentRequest(t *testing.T) {
|
|
var capturedPath string
|
|
var capturedBody map[string]any
|
|
client := newTestRESTClient(t, func(request *http.Request) (*http.Response, error) {
|
|
capturedPath = request.URL.Path
|
|
if err := json.NewDecoder(request.Body).Decode(&capturedBody); err != nil {
|
|
t.Fatalf("decode request body failed: %v", err)
|
|
}
|
|
|
|
return okRESTResponse(), nil
|
|
})
|
|
|
|
if err := client.DestroyGroup(context.Background(), "hy_codex_bc_r_210"); err != nil {
|
|
t.Fatalf("DestroyGroup failed: %v", err)
|
|
}
|
|
|
|
if capturedPath != "/"+destroyGroupCommand {
|
|
t.Fatalf("path mismatch: got %q", capturedPath)
|
|
}
|
|
if capturedBody["GroupId"] != "hy_codex_bc_r_210" {
|
|
t.Fatalf("unexpected destroy group body: %+v", capturedBody)
|
|
}
|
|
}
|
|
|
|
// TestRESTClientDeleteRoomGroupMemberBuildsTencentRequest 锁定踢出 IM 群成员的 REST 请求。
|
|
func TestRESTClientDeleteRoomGroupMemberBuildsTencentRequest(t *testing.T) {
|
|
var capturedPath string
|
|
var capturedBody map[string]any
|
|
client := newTestRESTClient(t, func(request *http.Request) (*http.Response, error) {
|
|
capturedPath = request.URL.Path
|
|
if err := json.NewDecoder(request.Body).Decode(&capturedBody); err != nil {
|
|
t.Fatalf("decode request body failed: %v", err)
|
|
}
|
|
|
|
return okRESTResponse(), nil
|
|
})
|
|
|
|
if err := client.DeleteRoomGroupMember(context.Background(), "room-1001", 10002); err != nil {
|
|
t.Fatalf("DeleteRoomGroupMember failed: %v", err)
|
|
}
|
|
|
|
if capturedPath != "/"+deleteGroupMemberCommand {
|
|
t.Fatalf("path mismatch: got %q", capturedPath)
|
|
}
|
|
members, ok := capturedBody["MemberToDel_Account"].([]any)
|
|
if !ok || len(members) != 1 || members[0] != "10002" {
|
|
t.Fatalf("unexpected member removal body: %+v", capturedBody)
|
|
}
|
|
if capturedBody["GroupId"] != "room-1001" || capturedBody["Silence"].(float64) != 1 {
|
|
t.Fatalf("unexpected group removal body: %+v", capturedBody)
|
|
}
|
|
}
|
|
|
|
// TestRESTClientKickUserBuildsTencentRequest 锁定封禁链路使用腾讯云 IM 失效账号登录态接口。
|
|
func TestRESTClientKickUserBuildsTencentRequest(t *testing.T) {
|
|
var capturedPath string
|
|
var capturedBody map[string]any
|
|
client := newTestRESTClient(t, func(request *http.Request) (*http.Response, error) {
|
|
capturedPath = request.URL.Path
|
|
if err := json.NewDecoder(request.Body).Decode(&capturedBody); err != nil {
|
|
t.Fatalf("decode request body failed: %v", err)
|
|
}
|
|
|
|
return okRESTResponse(), nil
|
|
})
|
|
|
|
if err := client.KickUser(context.Background(), 10002); err != nil {
|
|
t.Fatalf("KickUser failed: %v", err)
|
|
}
|
|
|
|
if capturedPath != "/"+kickUserCommand {
|
|
t.Fatalf("path mismatch: got %q", capturedPath)
|
|
}
|
|
if capturedBody["UserID"] != "10002" {
|
|
t.Fatalf("unexpected kick body: %+v", capturedBody)
|
|
}
|
|
}
|
|
|
|
func newTestRESTClient(t *testing.T, roundTrip roundTripFunc) *RESTClient {
|
|
t.Helper()
|
|
|
|
client, err := NewRESTClient(RESTConfig{
|
|
SDKAppID: 1400000000,
|
|
SecretKey: "secret",
|
|
AdminIdentifier: "administrator",
|
|
AdminUserSigTTL: time.Hour,
|
|
HTTPClient: &http.Client{
|
|
Transport: roundTrip,
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("NewRESTClient failed: %v", err)
|
|
}
|
|
|
|
return client
|
|
}
|
|
|
|
func okRESTResponse() *http.Response {
|
|
payload := []byte(`{"ActionStatus":"OK","ErrorCode":0,"ErrorInfo":""}`)
|
|
|
|
return &http.Response{
|
|
StatusCode: http.StatusOK,
|
|
Body: io.NopCloser(bytes.NewReader(payload)),
|
|
Header: make(http.Header),
|
|
}
|
|
}
|