Files
dingtalk-workspace-cli/internal/helpers/doc_group_mentions_test.go

300 lines
8.9 KiB
Go

package helpers
import (
"context"
"errors"
"io"
"os"
"reflect"
"strings"
"testing"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
"github.com/spf13/cobra"
)
type docGroupMentionCall struct {
server string
tool string
args map[string]any
}
type docGroupMentionCaller struct {
calls []docGroupMentionCall
err error
}
func (c *docGroupMentionCaller) CallTool(_ context.Context, server, tool string, args map[string]any) (*edition.ToolResult, error) {
c.calls = append(c.calls, docGroupMentionCall{server: server, tool: tool, args: args})
if c.err != nil {
return nil, c.err
}
return &edition.ToolResult{Content: []edition.ContentBlock{{Type: "text", Text: `{}`}}}, nil
}
func (*docGroupMentionCaller) Format() string { return "json" }
func (*docGroupMentionCaller) DryRun() bool { return false }
func (*docGroupMentionCaller) Fields() string { return "" }
func (*docGroupMentionCaller) JQ() string { return "" }
func executeDocGroupMentionCommand(t *testing.T, caller *docGroupMentionCaller, args ...string) error {
t.Helper()
previousDeps := deps
previousArgs := os.Args
InitDeps(caller)
deps.Out.w = io.Discard
deps.Out.errW = io.Discard
os.Args = []string{"dws", "doc"}
t.Cleanup(func() {
deps = previousDeps
os.Args = previousArgs
})
root := newDocCommand()
root.SilenceErrors = true
root.SilenceUsage = true
root.SetArgs(args)
return root.Execute()
}
func TestCrossPlatformCoverageDocCommentGroupMentionFlagsAndMappings(t *testing.T) {
root := newDocCommand()
for _, name := range []string{"create", "reply", "update"} {
cmd, remaining, err := root.Find([]string{"comment", name})
if err != nil || len(remaining) != 0 {
t.Fatalf("find comment %s: remaining=%v err=%v", name, remaining, err)
}
flag := cmd.Flags().Lookup("mentioned-open-conversation-id")
if flag == nil || flag.Value.Type() != "stringSlice" {
t.Fatalf("comment %s group mention flag = %#v, want stringSlice", name, flag)
}
}
tests := []struct {
name string
args []string
tool string
key string
}{
{
name: "create",
args: []string{
"comment", "create", "--node", "doc-1", "--content", "body",
"--mentioned-open-conversation-id", "oc-1",
},
tool: "create_comment",
},
{
name: "reply",
args: []string{
"comment", "reply", "--node", "doc-1", "--comment-key", "comment-1", "--content", "body",
"--mentioned-open-conversation-id", "oc-1",
},
tool: "reply_comment",
key: "replyCommentKey",
},
{
name: "update",
args: []string{
"comment", "update", "--node", "doc-1", "--comment-key", "comment-1", "--content", "body",
"--mentioned-open-conversation-id", "oc-1",
},
tool: "update_comment",
key: "commentKey",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
caller := &docGroupMentionCaller{}
if err := executeDocGroupMentionCommand(t, caller, tt.args...); err != nil {
t.Fatalf("execute: %v", err)
}
if len(caller.calls) != 1 {
t.Fatalf("calls = %d, want 1", len(caller.calls))
}
call := caller.calls[0]
if call.server != "doc-comment" || call.tool != tt.tool {
t.Fatalf("target = %s/%s, want doc-comment/%s", call.server, call.tool, tt.tool)
}
if got := call.args["mentionedOpenConversationIds"]; !reflect.DeepEqual(got, []string{"oc-1"}) {
t.Fatalf("mentionedOpenConversationIds = %#v", got)
}
if tt.key != "" && call.args[tt.key] != "comment-1" {
t.Fatalf("%s = %#v", tt.key, call.args[tt.key])
}
})
}
}
func TestCrossPlatformCoverageDocCommentGroupMentionsTrimDeduplicateAndPreserveUserMentions(t *testing.T) {
caller := &docGroupMentionCaller{}
err := executeDocGroupMentionCommand(
t,
caller,
"comment", "update", "--node", "doc-1", "--comment-key", "comment-1", "--content", "body",
"--mention", "user-1, user-2",
"--mentioned-open-conversation-id", "oc-1, oc-2",
"--mentioned-open-conversation-id", "oc-1",
)
if err != nil {
t.Fatalf("execute: %v", err)
}
if len(caller.calls) != 1 {
t.Fatalf("calls = %d, want 1", len(caller.calls))
}
args := caller.calls[0].args
if got := args["mentionedUserIds"]; !reflect.DeepEqual(got, []string{"user-1", "user-2"}) {
t.Fatalf("mentionedUserIds = %#v", got)
}
if got := args["mentionedOpenConversationIds"]; !reflect.DeepEqual(got, []string{"oc-1", "oc-2"}) {
t.Fatalf("mentionedOpenConversationIds = %#v", got)
}
}
func TestCrossPlatformCoverageDocCommentGroupMentionValidationAndCompatibility(t *testing.T) {
t.Run("omitted", func(t *testing.T) {
caller := &docGroupMentionCaller{}
err := executeDocGroupMentionCommand(
t,
caller,
"comment", "create", "--node", "doc-1", "--content", "plain",
)
if err != nil {
t.Fatalf("execute: %v", err)
}
if _, exists := caller.calls[0].args["mentionedOpenConversationIds"]; exists {
t.Fatal("group mention field should be omitted")
}
})
t.Run("blank", func(t *testing.T) {
caller := &docGroupMentionCaller{}
err := executeDocGroupMentionCommand(
t,
caller,
"comment", "create", "--node", "doc-1", "--content", "body",
"--mentioned-open-conversation-id", " ",
)
if err == nil || !strings.Contains(err.Error(), "must not be empty") {
t.Fatalf("error = %v", err)
}
if len(caller.calls) != 0 {
t.Fatalf("calls = %d, want 0", len(caller.calls))
}
})
for _, command := range []struct {
name string
args []string
}{
{
name: "reply rejects blank",
args: []string{
"comment", "reply", "--node", "doc-1", "--comment-key", "comment-1", "--content", "body",
"--mentioned-open-conversation-id", " ",
},
},
{
name: "update rejects blank",
args: []string{
"comment", "update", "--node", "doc-1", "--comment-key", "comment-1", "--content", "body",
"--mentioned-open-conversation-id", " ",
},
},
} {
t.Run(command.name, func(t *testing.T) {
caller := &docGroupMentionCaller{}
err := executeDocGroupMentionCommand(t, caller, command.args...)
if err == nil || !strings.Contains(err.Error(), "must not be empty") {
t.Fatalf("error = %v", err)
}
if len(caller.calls) != 0 {
t.Fatalf("calls = %d, want 0", len(caller.calls))
}
})
}
t.Run("emoji conflict", func(t *testing.T) {
caller := &docGroupMentionCaller{}
err := executeDocGroupMentionCommand(
t,
caller,
"comment", "reply", "--node", "doc-1", "--comment-key", "comment-1", "--content", "like",
"--emoji", "--mentioned-open-conversation-id", "oc-1",
)
if err == nil || !strings.Contains(err.Error(), "emoji replies do not support group mentions") {
t.Fatalf("error = %v", err)
}
if len(caller.calls) != 0 {
t.Fatalf("calls = %d, want 0", len(caller.calls))
}
})
t.Run("emoji validates blank group mention", func(t *testing.T) {
caller := &docGroupMentionCaller{}
err := executeDocGroupMentionCommand(
t,
caller,
"comment", "reply", "--node", "doc-1", "--comment-key", "comment-1", "--content", "like",
"--emoji", "--mentioned-open-conversation-id", " ",
)
if err == nil || !strings.Contains(err.Error(), "must not be empty") {
t.Fatalf("error = %v", err)
}
if len(caller.calls) != 0 {
t.Fatalf("calls = %d, want 0", len(caller.calls))
}
})
t.Run("emoji without group mention remains supported", func(t *testing.T) {
caller := &docGroupMentionCaller{}
err := executeDocGroupMentionCommand(
t,
caller,
"comment", "reply", "--node", "doc-1", "--comment-key", "comment-1", "--content", "like",
"--emoji",
)
if err != nil {
t.Fatalf("execute: %v", err)
}
if len(caller.calls) != 1 || caller.calls[0].args["emoji"] != true {
t.Fatalf("calls = %#v", caller.calls)
}
})
t.Run("server error is not downgraded", func(t *testing.T) {
sentinel := errors.New("group mention rejected")
caller := &docGroupMentionCaller{err: sentinel}
err := executeDocGroupMentionCommand(
t,
caller,
"comment", "create", "--node", "doc-1", "--content", "body",
"--mentioned-open-conversation-id", "oc-1",
)
if err == nil || !strings.Contains(err.Error(), sentinel.Error()) {
t.Fatalf("error = %v", err)
}
if len(caller.calls) != 1 {
t.Fatalf("calls = %d, want exactly 1", len(caller.calls))
}
if got := caller.calls[0].args["mentionedOpenConversationIds"]; !reflect.DeepEqual(got, []string{"oc-1"}) {
t.Fatalf("first call lost group mentions: %#v", got)
}
})
}
func TestCrossPlatformCoverageCommentGroupMentionHelperErrorBranches(t *testing.T) {
wrongType := &cobra.Command{Use: "wrong-type"}
wrongType.Flags().String("mentioned-open-conversation-id", "", "")
if _, err := commentGroupMentionIDs(wrongType); err == nil {
t.Fatal("wrong flag type should fail")
}
emptySlice := &cobra.Command{Use: "empty-slice"}
addCommentGroupMentionFlag(emptySlice)
emptySlice.Flags().Lookup("mentioned-open-conversation-id").Changed = true
if _, err := commentGroupMentionIDs(emptySlice); err == nil || !strings.Contains(err.Error(), "at least one") {
t.Fatalf("empty explicitly changed slice error = %v", err)
}
}