Files
dingtalk-workspace-cli/internal/shortcut/chat/chat_message_coverage_test.go

223 lines
7.0 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
package chat
import (
"bytes"
"context"
"encoding/json"
"errors"
"net/http"
"testing"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/helpers"
)
func TestCrossPlatformCoverageListMessageRichProjection(t *testing.T) {
rows := listMessagesProject(map[string]any{"result": map[string]any{"messages": []any{
map[string]any{
"openMessageId": "msg",
"openConversationId": "cid",
"threadId": "thread",
"msgType": "text",
"createTime": "1",
"updateTime": "2",
"content": `{"mediaId":"@image"}`,
"quotedMessage": map[string]any{
"openMessageId": "quoted",
"content": `{"mediaId":"@quoted-image"}`,
},
},
}}})
if len(rows) != 1 {
t.Fatalf("rows = %#v", rows)
}
for _, key := range []string{"threadId", "updateTime", "quotedMessage", "resourceRefs"} {
if _, ok := rows[0][key]; !ok {
t.Errorf("projection missing %s: %#v", key, rows[0])
}
}
resources := rows[0]["resourceRefs"].([]map[string]any)
if len(resources) != 2 {
t.Fatalf("projected resources = %#v", 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("quoted resource context = %#v", resources[1])
}
}
func TestCrossPlatformCoverageMgetResourceDownloadOutcomes(t *testing.T) {
baseArgs := []string{"chat", "+messages-mget", "--msg-ids", "msg", "--download-resources", "--yes"}
readyMget := `{"result":[{"openMessageId":"msg","openConversationId":"cid","content":"{\"mediaId\":\"@file\"}"}]}`
missingContextMget := `{"result":[{"content":"{\"mediaId\":\"@file\"}"}]}`
validInfo := `{"result":{"resourceUrl":"https://download.dingtalk.com/resource.bin"}}`
t.Run("dry run", func(t *testing.T) {
helpers.InitDeps(&larkAlignmentCaller{responses: map[string]string{
"im/list_messages_by_ids": readyMget,
}})
root := newPlatformCoverageRoot()
root.SetArgs(append(append([]string{}, baseArgs...), "--dry-run"))
if err := root.Execute(); err != nil {
t.Fatal(err)
}
})
t.Run("getwd", func(t *testing.T) {
resetResourceDownloadHooks(t)
resourceGetwd = func() (string, error) { return "", errors.New("getwd") }
helpers.InitDeps(&larkAlignmentCaller{responses: map[string]string{
"im/list_messages_by_ids": readyMget,
}})
root := newPlatformCoverageRoot()
var output bytes.Buffer
root.SetOut(&output)
root.SetArgs(baseArgs)
if err := root.Execute(); err != nil {
t.Fatalf("getwd ledger error = %v", err)
}
var payload map[string]any
if err := json.Unmarshal(output.Bytes(), &payload); err != nil {
t.Fatal(err)
}
ledger, _ := payload["resourceDownloads"].(map[string]any)
if ledger["requestedCount"] != float64(1) ||
ledger["failedCount"] != ledger["requestedCount"] {
t.Fatalf("getwd ledger = %#v", ledger)
}
})
t.Run("zero resources skip getwd", func(t *testing.T) {
resetResourceDownloadHooks(t)
getwdCalled := false
resourceGetwd = func() (string, error) {
getwdCalled = true
return "", errors.New("getwd")
}
helpers.InitDeps(&larkAlignmentCaller{responses: map[string]string{
"im/list_messages_by_ids": `{"result":[{"openMessageId":"msg","openConversationId":"cid","content":"plain text"}]}`,
}})
root := newPlatformCoverageRoot()
var output bytes.Buffer
root.SetOut(&output)
root.SetArgs(baseArgs)
if err := root.Execute(); err != nil {
t.Fatalf("zero-resource download error = %v", err)
}
if getwdCalled {
t.Fatal("zero-resource download unnecessarily read the working directory")
}
var payload map[string]any
if err := json.Unmarshal(output.Bytes(), &payload); err != nil {
t.Fatal(err)
}
ledger, _ := payload["resourceDownloads"].(map[string]any)
failures, _ := ledger["failures"].([]any)
if ledger["ok"] != true ||
ledger["requestedCount"] != float64(0) ||
ledger["failedCount"] != float64(0) ||
len(failures) != 0 {
t.Fatalf("zero-resource ledger = %#v", ledger)
}
})
cases := []struct {
name string
mget string
info string
failProductTool string
outputDir string
downloadErr error
pathErr bool
}{
{name: "missing context", mget: missingContextMget, info: validInfo},
{name: "resource lookup", mget: readyMget, failProductTool: "im/get_resource_download_url"},
{name: "invalid info", mget: readyMget, info: `{"result":{}}`},
{name: "path", mget: readyMget, info: validInfo, outputDir: "go.mod", pathErr: true},
{name: "download", mget: readyMget, info: validInfo, downloadErr: errors.New("download")},
{name: "success", mget: readyMget, info: validInfo},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
resetResourceDownloadHooks(t)
if tc.pathErr {
resourceAbs = func(string) (string, error) { return "", errors.New("path") }
}
resourceDownload = func(
_ context.Context,
_ *http.Client,
_ string,
_ map[string]string,
_ string,
_ bool,
) (int64, error) {
return 4, tc.downloadErr
}
caller := &larkAlignmentCaller{
failProductTool: tc.failProductTool,
responses: map[string]string{
"im/list_messages_by_ids": tc.mget,
"im/get_resource_download_url": tc.info,
},
}
helpers.InitDeps(caller)
root := newPlatformCoverageRoot()
args := append([]string{}, baseArgs...)
if tc.outputDir != "" {
args = append(args, "--output-dir", tc.outputDir)
}
root.SetArgs(args)
if err := root.Execute(); err != nil {
t.Fatal(err)
}
})
}
}
func TestCrossPlatformCoverageMgetDownloadRunsWithoutConfirmation(t *testing.T) {
resetResourceDownloadHooks(t)
t.Chdir(t.TempDir())
resourceDownload = func(
_ context.Context,
_ *http.Client,
_ string,
_ map[string]string,
dest string,
_ bool,
) (int64, error) {
return 7, nil
}
fake := &larkAlignmentCaller{responses: map[string]string{
"im/list_messages_by_ids": `{"result":[{"openMessageId":"msg","openConversationId":"cid","content":"{\"mediaId\":\"@file\"}"}]}`,
"im/get_resource_download_url": `{"result":{"resourceUrl":"https://download.dingtalk.com/resource.bin"}}`,
}}
helpers.InitDeps(fake)
root := newPlatformCoverageRoot()
var output bytes.Buffer
root.SetOut(&output)
root.SetIn(bytes.NewBuffer(nil))
root.SetArgs([]string{
"chat", "+messages-mget",
"--msg-ids", "msg",
"--download-resources",
})
if err := root.Execute(); err != nil {
t.Fatal(err)
}
if len(fake.calls) != 2 ||
fake.calls[0].tool != "list_messages_by_ids" ||
fake.calls[1].tool != "get_resource_download_url" {
t.Fatalf("download calls = %#v", fake.calls)
}
var payload map[string]any
if err := json.Unmarshal(output.Bytes(), &payload); err != nil {
t.Fatal(err)
}
ledger, _ := payload["resourceDownloads"].(map[string]any)
if ledger["downloadedCount"] != float64(1) || ledger["failedCount"] != float64(0) {
t.Fatalf("download ledger = %#v", ledger)
}
}