553 lines
20 KiB
Go
553 lines
20 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
package smart
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/helpers"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut/targetresolver"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
|
|
)
|
|
|
|
type smartCoverageCaller struct {
|
|
responses map[string][]string
|
|
failAt map[string]int
|
|
counts map[string]int
|
|
arguments map[string][]map[string]any
|
|
}
|
|
|
|
func (c *smartCoverageCaller) CallTool(
|
|
_ context.Context,
|
|
product, tool string,
|
|
args map[string]any,
|
|
) (*edition.ToolResult, error) {
|
|
if c.counts == nil {
|
|
c.counts = map[string]int{}
|
|
}
|
|
if c.arguments == nil {
|
|
c.arguments = map[string][]map[string]any{}
|
|
}
|
|
key := product + "/" + tool
|
|
c.counts[key]++
|
|
c.arguments[key] = append(c.arguments[key], args)
|
|
if c.failAt[key] == -1 || c.failAt[key] == c.counts[key] {
|
|
return nil, errors.New("fixture failure")
|
|
}
|
|
responses := c.responses[key]
|
|
text := `{"result":[]}`
|
|
if len(responses) > 0 {
|
|
index := c.counts[key] - 1
|
|
if index >= len(responses) {
|
|
index = len(responses) - 1
|
|
}
|
|
text = responses[index]
|
|
}
|
|
return &edition.ToolResult{Content: []edition.ContentBlock{{Type: "text", Text: text}}}, nil
|
|
}
|
|
|
|
func (c *smartCoverageCaller) CallReadTool(ctx context.Context, product, tool string, args map[string]any) (*edition.ToolResult, error) {
|
|
return c.CallTool(ctx, product, tool, args)
|
|
}
|
|
|
|
func (*smartCoverageCaller) Format() string { return "json" }
|
|
func (*smartCoverageCaller) DryRun() bool { return false }
|
|
func (*smartCoverageCaller) Fields() string { return "" }
|
|
func (*smartCoverageCaller) JQ() string { return "" }
|
|
|
|
func TestCrossPlatformCoverageRichMessageProjections(t *testing.T) {
|
|
message := map[string]any{
|
|
"openMessageId": "msg",
|
|
"openConversationId": "cid",
|
|
"threadId": "thread",
|
|
"msgType": "text",
|
|
"createTime": "1",
|
|
"updateTime": "2",
|
|
"content": `{"mediaId":"@image"}`,
|
|
"emotionReplyList": []any{map[string]any{"emojiName": "赞", "replyCount": 1}},
|
|
"quotedMessage": map[string]any{
|
|
"openMessageId": "quoted",
|
|
"content": `{"mediaId":"@quoted-image"}`,
|
|
},
|
|
}
|
|
for name, row := range map[string]map[string]any{
|
|
"at-me": atMeProject(message),
|
|
"chat": projectChatMessage(message),
|
|
"search": searchMsgProject(message),
|
|
} {
|
|
for _, key := range []string{"threadId", "messageType", "updateTime", "quotedMessage", "resourceRefs"} {
|
|
if _, ok := row[key]; !ok {
|
|
t.Errorf("%s projection missing %s: %#v", name, key, row)
|
|
}
|
|
}
|
|
resources := row["resourceRefs"].([]map[string]any)
|
|
if len(resources) != 2 {
|
|
t.Fatalf("%s projected resources = %#v", name, resources)
|
|
}
|
|
quotedArgs := resources[1]["download"].(map[string]any)["arguments"].(map[string]any)
|
|
if resources[1]["resourceId"] != "@quoted-image" ||
|
|
quotedArgs["message-id"] != "quoted" ||
|
|
quotedArgs["open-conversation-id"] != "cid" {
|
|
t.Fatalf("%s quoted resource context = %#v", name, resources[1])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageChatMessagesOpenIDRoute(t *testing.T) {
|
|
caller := &smartCoverageCaller{responses: map[string][]string{
|
|
"chat/list_individual_chat_message": {`{"result":{"messages":[]}}`},
|
|
}}
|
|
helpers.InitDeps(caller)
|
|
root := newPlatformCoverageRoot()
|
|
root.SetArgs([]string{"chat", "+chat-messages", "--open-dingtalk-id", testCurrentDOpenID, "--limit", "1", "--yes"})
|
|
if err := root.Execute(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if caller.counts["chat/list_individual_chat_message"] != 1 {
|
|
t.Fatalf("calls = %#v", caller.counts)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageChatMembersListOutcomes(t *testing.T) {
|
|
successResponses := map[string][]string{
|
|
"chat/get_group_members": {`{"result":{"hasMore":false,"list":[{"memberEmpName":"成员","openDingtalkId":"D-user"}]}}`},
|
|
"bot/list_group_bots": {`{"result":{"bots":[{"robotName":"机器人","robotCode":"robot"}]}}`},
|
|
}
|
|
cases := []struct {
|
|
name string
|
|
memberArg string
|
|
failAt map[string]int
|
|
wantError bool
|
|
}{
|
|
{name: "both success"},
|
|
{name: "user only failure", memberArg: "user", failAt: map[string]int{"chat/get_group_members": 1}, wantError: true},
|
|
{name: "bot only failure", memberArg: "bot", failAt: map[string]int{"bot/list_group_bots": 1}, wantError: true},
|
|
{name: "both failure", failAt: map[string]int{"chat/get_group_members": 1, "bot/list_group_bots": 1}, wantError: true},
|
|
{name: "partial user failure", failAt: map[string]int{"chat/get_group_members": 1}},
|
|
{name: "partial bot failure", failAt: map[string]int{"bot/list_group_bots": 1}},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
caller := &smartCoverageCaller{responses: successResponses, failAt: tc.failAt}
|
|
helpers.InitDeps(caller)
|
|
root := newPlatformCoverageRoot()
|
|
args := []string{"chat", "+chat-members-list", "--conversation-id", "cid"}
|
|
if tc.memberArg != "" {
|
|
args = append(args, "--member-types", tc.memberArg)
|
|
}
|
|
root.SetArgs(args)
|
|
err := root.Execute()
|
|
if (err != nil) != tc.wantError {
|
|
t.Fatalf("error = %v, wantError=%v", err, tc.wantError)
|
|
}
|
|
})
|
|
}
|
|
|
|
for _, raw := range [][]string{{""}, {"unknown"}} {
|
|
if _, _, err := resolveMemberTypes(raw); err == nil {
|
|
t.Fatalf("resolveMemberTypes(%q) succeeded", raw)
|
|
}
|
|
}
|
|
if users, bots, err := resolveMemberTypes(nil); err != nil || !users || !bots {
|
|
t.Fatalf("default member types = %v %v %v", users, bots, err)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageChatMembersListPaginatesUserBucketAndDeduplicates(t *testing.T) {
|
|
caller := &smartCoverageCaller{responses: map[string][]string{
|
|
"chat/get_group_members": {
|
|
`{"result":{"hasMore":true,"nextCursor":"2","list":[{"memberEmpName":"A","openDingtalkId":"D1"}]}}`,
|
|
`{"result":{"hasMore":false,"list":[{"memberEmpName":"A","openDingtalkId":"D1"},{"memberEmpName":"B","openDingtalkId":"D2"}]}}`,
|
|
},
|
|
"bot/list_group_bots": {`{"result":{"bots":[{"robotName":"R","robotCode":"robot"}]}}`},
|
|
}}
|
|
helpers.InitDeps(caller)
|
|
root := newPlatformCoverageRoot()
|
|
var output bytes.Buffer
|
|
root.SetOut(&output)
|
|
root.SetArgs([]string{"chat", "+chat-members-list", "--conversation-id", "cid"})
|
|
if err := root.Execute(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if caller.counts["chat/get_group_members"] != 2 {
|
|
t.Fatalf("member page calls = %#v", caller.counts)
|
|
}
|
|
var payload map[string]any
|
|
if err := json.Unmarshal(output.Bytes(), &payload); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
counts, _ := payload["counts"].(map[string]any)
|
|
buckets, _ := payload["buckets"].(map[string]any)
|
|
users, _ := buckets["users"].(map[string]any)
|
|
if payload["contractVersion"] != groupMembersContractVersion || payload["complete"] != true ||
|
|
counts["users"] != float64(2) || counts["bots"] != float64(1) ||
|
|
users["pagesFetched"] != float64(2) || users["complete"] != true {
|
|
t.Fatalf("member buckets = %#v", payload)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageChatMembersListPageLimitPublishesContinuation(t *testing.T) {
|
|
caller := &smartCoverageCaller{responses: map[string][]string{
|
|
"chat/get_group_members": {
|
|
`{"result":{"hasMore":true,"nextCursor":"2","list":[{"memberEmpName":"A","openDingtalkId":"D1"}]}}`,
|
|
},
|
|
}}
|
|
helpers.InitDeps(caller)
|
|
root := newPlatformCoverageRoot()
|
|
var output bytes.Buffer
|
|
root.SetOut(&output)
|
|
root.SetArgs([]string{
|
|
"chat", "+chat-members-list", "--conversation-id", "cid",
|
|
"--member-types", "user", "--page-limit", "1",
|
|
})
|
|
if err := root.Execute(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var payload map[string]any
|
|
if err := json.Unmarshal(output.Bytes(), &payload); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if payload["complete"] != false || payload["hasMore"] != true || payload["nextCursor"] != "2" {
|
|
t.Fatalf("bounded member payload = %#v", payload)
|
|
}
|
|
buckets := payload["buckets"].(map[string]any)
|
|
users := buckets["users"].(map[string]any)
|
|
if users["stopReason"] != "page_limit" || users["failedCount"] != float64(0) {
|
|
t.Fatalf("bounded user bucket = %#v", users)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageChatMembersListKeepsEarlierPagesWhenLaterReadFails(t *testing.T) {
|
|
caller := &smartCoverageCaller{
|
|
responses: map[string][]string{
|
|
"chat/get_group_members": {
|
|
`{"result":{"hasMore":true,"nextCursor":"2","list":[{"memberEmpName":"A","openDingtalkId":"D1"}]}}`,
|
|
},
|
|
},
|
|
failAt: map[string]int{"chat/get_group_members": 2},
|
|
}
|
|
helpers.InitDeps(caller)
|
|
root := newPlatformCoverageRoot()
|
|
var output bytes.Buffer
|
|
root.SetOut(&output)
|
|
root.SetArgs([]string{
|
|
"chat", "+chat-members-list", "--conversation-id", "cid", "--member-types", "user",
|
|
})
|
|
if err := root.Execute(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var payload map[string]any
|
|
if err := json.Unmarshal(output.Bytes(), &payload); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if payload["complete"] != false || payload["partial"] != true ||
|
|
payload["hasMore"] != true || payload["nextCursor"] != "2" ||
|
|
payload["failedCount"] != float64(1) {
|
|
t.Fatalf("partial member payload = %#v", payload)
|
|
}
|
|
buckets := payload["buckets"].(map[string]any)
|
|
users := buckets["users"].(map[string]any)
|
|
if users["count"] != float64(1) || users["pagesFetched"] != float64(1) ||
|
|
users["stopReason"] != "read_failure" || users["partial"] != true {
|
|
t.Fatalf("partial user bucket = %#v", users)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageChatMembersGroupResolutionAndProjection(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
search string
|
|
fail bool
|
|
wantError bool
|
|
}{
|
|
{name: "search error", fail: true, wantError: true},
|
|
{name: "none", search: `{"result":[]}`, wantError: true},
|
|
{name: "ambiguous", search: `{"result":[{"openConversationId":"c1","title":"群"},{"openConversationId":"c2","title":"群"}]}`, wantError: true},
|
|
{name: "one", search: `{"result":[{"openConversationId":"c1","title":"群"}]}`},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
failAt := map[string]int{}
|
|
if tc.fail {
|
|
failAt["im/search_groups"] = 1
|
|
}
|
|
caller := &smartCoverageCaller{
|
|
responses: map[string][]string{
|
|
"im/search_groups": {tc.search},
|
|
"chat/get_group_members": {`{"result":{"list":[]}}`},
|
|
"bot/list_group_bots": {`{"result":{"bots":[]}}`},
|
|
},
|
|
failAt: failAt,
|
|
}
|
|
helpers.InitDeps(caller)
|
|
root := newPlatformCoverageRoot()
|
|
root.SetArgs([]string{"chat", "+chat-members-list", "--group", "群"})
|
|
err := root.Execute()
|
|
if (err != nil) != tc.wantError {
|
|
t.Fatalf("error = %v, wantError=%v", err, tc.wantError)
|
|
}
|
|
})
|
|
}
|
|
|
|
bots := groupBotProject(map[string]any{"bots": []any{"invalid", map[string]any{"name": "bot"}}})
|
|
if len(bots) != 1 || bots[0]["name"] != "bot" {
|
|
t.Fatalf("bots = %#v", bots)
|
|
}
|
|
if groupBotProject(map[string]any{"result": "invalid"}) == nil {
|
|
t.Fatal("empty bot projection should be a non-nil empty slice")
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageSearchValidationAndTimeErrors(t *testing.T) {
|
|
cases := [][]string{
|
|
{},
|
|
{"--query", "x", "--start", "2026-07-01T00:00:00Z"},
|
|
{"--query", "x", "--days", "0"},
|
|
{"--query", "x", "--limit", "101"},
|
|
{"--query", "x", "--page-limit", "0"},
|
|
{"--query", "x", "--start", "bad", "--end", "2026-07-02T00:00:00Z"},
|
|
{"--query", "x", "--start", "2026-07-01T00:00:00Z", "--end", "bad"},
|
|
{"--query", "x", "--start", "2026-07-02T00:00:00Z", "--end", "2026-07-01T00:00:00Z"},
|
|
}
|
|
for _, tail := range cases {
|
|
helpers.InitDeps(&smartCoverageCaller{})
|
|
root := newPlatformCoverageRoot()
|
|
root.SetArgs(append([]string{"chat", "+search-msg", "--yes"}, tail...))
|
|
if err := root.Execute(); err == nil {
|
|
t.Errorf("invalid search args succeeded: %v", tail)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageSearchPaginationFailureModes(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
responses []string
|
|
failAt int
|
|
args []string
|
|
wantError bool
|
|
}{
|
|
{name: "initial error", failAt: 1, args: []string{"--query", "x"}, wantError: true},
|
|
{
|
|
name: "duplicate and inferred cursor",
|
|
responses: []string{
|
|
`{"result":{"messages":[{"openMessageId":"m1"},{"openMessageId":"m1"}],"nextCursor":"c2"}}`,
|
|
`{"result":{"messages":[],"hasMore":false}}`,
|
|
},
|
|
args: []string{"--query", "x", "--page-all", "--no-enrich"},
|
|
},
|
|
{
|
|
name: "stalled cursor",
|
|
responses: []string{`{"result":{"messages":[],"hasMore":true,"nextCursor":"same"}}`},
|
|
args: []string{"--query", "x", "--cursor", "same", "--page-all", "--no-enrich"},
|
|
},
|
|
{
|
|
name: "page limit",
|
|
responses: []string{`{"result":{"messages":[],"hasMore":true,"nextCursor":"next"}}`},
|
|
args: []string{"--query", "x", "--page-all", "--page-limit", "1", "--no-enrich"},
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
caller := &smartCoverageCaller{
|
|
responses: map[string][]string{"im/search_messages": tc.responses},
|
|
failAt: map[string]int{"im/search_messages": tc.failAt},
|
|
}
|
|
helpers.InitDeps(caller)
|
|
root := newPlatformCoverageRoot()
|
|
var output bytes.Buffer
|
|
root.SetOut(&output)
|
|
root.SetArgs(append([]string{"chat", "+search-msg", "--yes"}, tc.args...))
|
|
err := root.Execute()
|
|
if (err != nil) != tc.wantError {
|
|
t.Fatalf("error = %v, wantError=%v", err, tc.wantError)
|
|
}
|
|
if err == nil {
|
|
var payload map[string]any
|
|
if decodeErr := json.Unmarshal(output.Bytes(), &payload); decodeErr != nil {
|
|
t.Fatalf("decode %q: %v", strings.TrimSpace(output.String()), decodeErr)
|
|
}
|
|
if payload["complete"] != false && tc.name != "duplicate and inferred cursor" {
|
|
t.Fatalf("payload = %#v", payload)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageSmartResolutionAdapters(t *testing.T) {
|
|
if defaultChatPageLimit(0, 50) != 50 || defaultChatPageLimit(7, 50) != 7 {
|
|
t.Fatal("default page limit normalization failed")
|
|
}
|
|
if err := localChatOptionError("fixture", "fixture"); err == nil {
|
|
t.Fatal("local validation error unexpectedly nil")
|
|
}
|
|
users := []contactUser{
|
|
{userID: "u1", openDingTalkID: "d1", name: "甲"},
|
|
{openDingTalkID: "d2", name: "乙"},
|
|
}
|
|
if got := usersWithUserID(users); len(got) != 1 || got[0].userID != "u1" {
|
|
t.Fatalf("usersWithUserID = %#v", got)
|
|
}
|
|
extracted := extractUsers(map[string]any{"result": []any{
|
|
map[string]any{"userId": "u1", "openDingTalkId": "d1", "name": "甲"},
|
|
}})
|
|
if len(extracted) != 1 || extracted[0].openDingTalkID != "d1" {
|
|
t.Fatalf("extractUsers = %#v", extracted)
|
|
}
|
|
if extractUsers(map[string]any{}) != nil {
|
|
t.Fatal("empty extraction should return nil")
|
|
}
|
|
labels := userLabels(users)
|
|
if len(labels) != 2 || !strings.Contains(labels[0], "甲") {
|
|
t.Fatalf("user labels = %#v", labels)
|
|
}
|
|
resolved := targetresolver.User{UserID: "u", OpenDingTalkID: "d", Name: "姓名"}
|
|
if got := toResolvedUser(fromResolvedUser(resolved)); got != resolved {
|
|
t.Fatalf("user adapter round trip = %#v", got)
|
|
}
|
|
|
|
groups := extractGroupsForSend(map[string]any{"result": []any{
|
|
map[string]any{"openConversationId": "cid-1", "title": "项目群"},
|
|
map[string]any{"openConversationId": "cid-2", "title": "项目群备份"},
|
|
}})
|
|
if len(groups) != 2 || groups[0].id != "cid-1" {
|
|
t.Fatalf("extractGroupsForSend = %#v", groups)
|
|
}
|
|
preferred := preferExactGroupMatches(groups, "项目群")
|
|
if len(preferred) != 1 || preferred[0].id != "cid-1" {
|
|
t.Fatalf("preferExactGroupMatches = %#v", preferred)
|
|
}
|
|
if labels := sendGroupLabels(groups); len(labels) != 2 || !strings.Contains(labels[0], "项目群") {
|
|
t.Fatalf("group labels = %#v", labels)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageChatMessagesValidationAndFailureBoundaries(t *testing.T) {
|
|
invalid := [][]string{
|
|
{"--group", "cid", "--page-limit", "2"},
|
|
{"--group", "cid", "--page-all", "--page-limit", "0"},
|
|
{"--group", "cid", "--page-all", "--page-limit", "501"},
|
|
{"--group", "cid", "--page-all", "--max-results", "-1"},
|
|
{"--group", "cid", "--overwrite"},
|
|
{"--group", "cid", "--output", "/absolute.json"},
|
|
{"--group", "missing"},
|
|
{"--user-query", "missing"},
|
|
}
|
|
for _, tail := range invalid {
|
|
helpers.InitDeps(&smartCoverageCaller{})
|
|
root := newPlatformCoverageRoot()
|
|
root.SetArgs(append([]string{"chat", "+chat-messages"}, tail...))
|
|
if err := root.Execute(); err == nil {
|
|
t.Errorf("invalid chat messages args succeeded: %v", tail)
|
|
}
|
|
}
|
|
|
|
for _, tc := range []struct {
|
|
name string
|
|
responses []string
|
|
failAt int
|
|
args []string
|
|
wantError bool
|
|
}{
|
|
{name: "single read failure", failAt: 1, args: []string{"--group", "cid123456789"}, wantError: true},
|
|
{name: "all first read failure", failAt: 1, args: []string{"--group", "cid123456789", "--page-all"}, wantError: true},
|
|
{name: "all later read failure", responses: []string{`{"result":{"messages":[{"openMessageId":"m1","createTime":"2"}],"hasMore":true,"nextCursor":1767225600123}}`}, failAt: 2, args: []string{"--group", "cid123456789", "--page-all"}, wantError: true},
|
|
{name: "missing pagination", responses: []string{`{"result":{"messages":[]}}`}, args: []string{"--group", "cid123456789", "--page-all"}, wantError: true},
|
|
{name: "empty page with continuation", responses: []string{`{"result":{"messages":[],"hasMore":true}}`}, args: []string{"--group", "cid123456789", "--page-all"}, wantError: true},
|
|
{name: "result limit without boundary", responses: []string{`{"result":{"messages":[{"openMessageId":"m1"},{"openMessageId":"m2"}],"hasMore":true}}`}, args: []string{"--group", "cid123456789", "--page-all", "--max-results", "1"}, wantError: true},
|
|
{name: "result limit with boundary", responses: []string{`{"result":{"messages":[{"openMessageId":"m1","createTime":"2"}],"hasMore":true,"nextCursor":1767225600123}}`}, args: []string{"--group", "cid123456789", "--page-all", "--max-results", "1"}},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
caller := &smartCoverageCaller{
|
|
responses: map[string][]string{"chat/list_conversation_message_v2": tc.responses},
|
|
failAt: map[string]int{"chat/list_conversation_message_v2": tc.failAt},
|
|
}
|
|
helpers.InitDeps(caller)
|
|
root := newPlatformCoverageRoot()
|
|
root.SetArgs(append([]string{"chat", "+chat-messages"}, tc.args...))
|
|
err := root.Execute()
|
|
if (err != nil) != tc.wantError {
|
|
t.Fatalf("error = %v, wantError=%v", err, tc.wantError)
|
|
}
|
|
})
|
|
}
|
|
|
|
caller := &smartCoverageCaller{responses: map[string][]string{
|
|
"chat/list_conversation_message_v2": {`{"result":{"messages":[],"hasMore":false}}`},
|
|
}}
|
|
helpers.InitDeps(caller)
|
|
root := newPlatformCoverageRoot()
|
|
root.SetArgs([]string{"chat", "+chat-messages", "--group", "cid123456789", "--output", "messages.json", "--dry-run"})
|
|
if err := root.Execute(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageGroupMemberAndAtMeFailureBoundaries(t *testing.T) {
|
|
for _, args := range [][]string{
|
|
{"chat", "+group-members", "--group", "missing"},
|
|
{"chat", "+group-members", "--group", "cid", "--page-limit", "0"},
|
|
{"chat", "+chat-members-list", "--conversation-id", "cid", "--page-limit", "501"},
|
|
{"chat", "+at-me", "--group", "missing"},
|
|
} {
|
|
helpers.InitDeps(&smartCoverageCaller{})
|
|
root := newPlatformCoverageRoot()
|
|
root.SetArgs(args)
|
|
if err := root.Execute(); err == nil {
|
|
t.Errorf("invalid args succeeded: %v", args)
|
|
}
|
|
}
|
|
|
|
caller := &smartCoverageCaller{
|
|
responses: map[string][]string{
|
|
"im/search_groups": {`{"result":[{"openConversationId":"cid","title":"群"}]}`},
|
|
"chat/get_group_members": {`{"result":{"hasMore":true,"list":[]}}`},
|
|
},
|
|
}
|
|
helpers.InitDeps(caller)
|
|
root := newPlatformCoverageRoot()
|
|
root.SetArgs([]string{"chat", "+group-members", "--group", "群"})
|
|
if err := root.Execute(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
for _, args := range [][]string{
|
|
{"chat", "+chat-messages", "--user", "u1"},
|
|
{"chat", "+unread-chats", "--count", "1"},
|
|
} {
|
|
helpers.InitDeps(&smartCoverageCaller{responses: map[string][]string{
|
|
"chat/list_individual_chat_message": {`{"result":{"messages":[],"hasMore":false}}`},
|
|
"chat/list_unread_conversation_messages": {`{"result":[]}`},
|
|
}})
|
|
root := newPlatformCoverageRoot()
|
|
root.SetArgs(args)
|
|
if err := root.Execute(); err != nil {
|
|
t.Fatalf("args %v: %v", args, err)
|
|
}
|
|
}
|
|
|
|
helpers.InitDeps(&smartCoverageCaller{})
|
|
root = newPlatformCoverageRoot()
|
|
root.SetArgs([]string{"chat", "+search-msg", "--query", "x", "--chat-query", "missing", "--yes"})
|
|
if err := root.Execute(); err == nil {
|
|
t.Fatal("missing search chat query unexpectedly resolved")
|
|
}
|
|
|
|
helpers.InitDeps(&smartCoverageCaller{responses: map[string][]string{
|
|
"contact/search_contact_by_key_word": {`{"result":[{"userId":"u1","name":"甲"}]}`},
|
|
"im/search_messages": {`{"result":{"messages":[],"hasMore":false}}`},
|
|
}})
|
|
root = newPlatformCoverageRoot()
|
|
root.SetArgs([]string{"chat", "+search-msg", "--query", "x", "--sender-query", "甲", "--yes"})
|
|
if err := root.Execute(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|