Files
dingtalk-workspace-cli/internal/helpers/doc_style_test.go
玉澜 9567cfd3d8 fix(review): align wukong port with upstream behavior and PR #621 review findings
Must-fix: drive permission apply now gates on confirmDangerousAction and
declares confirmation=user_required, matching its help-text promise.

Wukong parity restored: formula-verify --exit-on-error (payload-parsing
exit path) and --targets conflict error, sheet info --include, chat
location/profile message types, search-advanced wukong flag aliases,
and a dedicated drive download-version leaf replacing the removed
polymorphic download --version.

Consistency fixes: transfer-owner --node/--workspace XOR and JSON-aware
dry-run after --yes validation; drive list --versions rejects
--depth/--pattern instead of misleading depth errors; depth BFS resumes
rate-limited folders from the failed page cursor to avoid duplicates;
doc style cover upload honors cmd.Context() and a 20 MiB size cap; chat
user-settings set validates per-item openConversationId and is
risk=medium.

Hardened the skill static audit to scan fenced code blocks and reject
unknown subcommands on group commands, fixing the stale aitable/drive
doc examples it exposed. Added CHANGELOG entry and coverage tests for
all changed statements plus previously untested ported commands.
2026-07-29 15:34:59 +08:00

231 lines
7.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package helpers
import (
"context"
"errors"
"os"
"path/filepath"
"strings"
"testing"
)
func writeTempImage(t *testing.T, name string) string {
t.Helper()
path := filepath.Join(t.TempDir(), name)
if err := os.WriteFile(path, []byte("fake-image-bytes"), 0o600); err != nil {
t.Fatal(err)
}
return path
}
func TestCrossPlatformCoverageValidateCoverImageFile(t *testing.T) {
if _, _, err := validateCoverImageFile(t.TempDir()); err == nil {
t.Fatal("directory returned nil")
}
path := writeTempImage(t, "cover.png")
mimeType, size, err := validateCoverImageFile(path)
if err != nil {
t.Fatal(err)
}
if mimeType != "image/png" || size != int64(len("fake-image-bytes")) {
t.Fatalf("mimeType=%q size=%d", mimeType, size)
}
}
func TestCrossPlatformCoverageIsHexColorEdge(t *testing.T) {
for _, valid := range []string{"#E8F2FE", "#e8f2fe", "#000000", "#aB1234"} {
if !isHexColor(valid) {
t.Fatalf("%s should be valid", valid)
}
}
for _, invalid := range []string{"#GG0000", "E8F2FE", "#E8F2F", "#E8F2FEE", ""} {
if isHexColor(invalid) {
t.Fatalf("%s should be invalid", invalid)
}
}
}
func executeDocStyleCommand(t *testing.T, caller *scriptedToolCaller, args ...string) error {
t.Helper()
installScriptedCaller(t, caller)
root := newDocStyleCommand()
root.SilenceErrors = true
root.SilenceUsage = true
root.SetArgs(args)
return root.Execute()
}
func TestCrossPlatformCoverageDocStyleCoverSetMutualExclusion(t *testing.T) {
err := executeDocStyleCommand(t, &scriptedToolCaller{},
"cover", "set", "--node", "n1", "--image", "https://img.test/a.png", "--file-path", "a.png")
if err == nil || !strings.Contains(err.Error(), "mutually exclusive") {
t.Fatalf("err = %v, want mutual exclusion", err)
}
}
func TestCrossPlatformCoverageDocStyleCoverSetPositionValidation(t *testing.T) {
err := executeDocStyleCommand(t, &scriptedToolCaller{},
"cover", "set", "--node", "n1", "--image", "https://img.test/a.png", "--position", "1.5")
if err == nil || !strings.Contains(err.Error(), "--position must be within [0,1]") {
t.Fatalf("err = %v, want position range error", err)
}
caller := &scriptedToolCaller{}
if err := executeDocStyleCommand(t, caller,
"cover", "set", "--node", "n1", "--image", "https://img.test/a.png", "--position", "0.5"); err != nil {
t.Fatal(err)
}
if caller.calls != 1 {
t.Fatalf("calls = %d, want 1", caller.calls)
}
}
func TestCrossPlatformCoverageDocStyleCoverSetFileDryRun(t *testing.T) {
path := writeTempImage(t, "cover.png")
caller := &scriptedToolCaller{dry: true}
if err := executeDocStyleCommand(t, caller, "cover", "set", "--node", "n1", "--file", path); err != nil {
t.Fatal(err)
}
}
func stubHTTPPutFile(t *testing.T, err error) {
t.Helper()
old := httpPutFile
httpPutFile = func(context.Context, string, map[string]string, string, int64) error { return err }
t.Cleanup(func() { httpPutFile = old })
}
func TestCrossPlatformCoverageDocStyleCoverSetFileUpload(t *testing.T) {
path := writeTempImage(t, "cover.png")
stubHTTPPutFile(t, nil)
caller := &scriptedToolCaller{steps: []scriptedToolStep{
{text: `{"uploadUrl":"https://oss.test/put","resourceId":"res-1"}`},
{text: `{}`},
}}
if err := executeDocStyleCommand(t, caller, "cover", "set", "--node", "n1", "--file", path); err != nil {
t.Fatal(err)
}
if caller.calls != 2 {
t.Fatalf("calls = %d, want 2 (upload info + style set)", caller.calls)
}
}
func TestCrossPlatformCoverageDocStyleCoverSetFileUploadFailure(t *testing.T) {
path := writeTempImage(t, "cover.png")
stubHTTPPutFile(t, errors.New("oss down"))
caller := &scriptedToolCaller{steps: []scriptedToolStep{
{text: `{"uploadUrl":"https://oss.test/put","resourceId":"res-1"}`},
}}
err := executeDocStyleCommand(t, caller, "cover", "set", "--node", "n1", "--file", path)
if err == nil || !strings.Contains(err.Error(), "oss down") {
t.Fatalf("err = %v, want oss failure", err)
}
}
func TestCrossPlatformCoverageUploadDocStyleImageFailures(t *testing.T) {
caller := &scriptedToolCaller{steps: []scriptedToolStep{{err: errors.New("cred failed")}}}
installScriptedCaller(t, caller)
if _, err := uploadDocStyleImage(context.Background(), "n1", "a.png", "a.png", "image/png", 10); err == nil {
t.Fatal("cred failure returned nil")
}
caller = &scriptedToolCaller{steps: []scriptedToolStep{{text: `not json`}}}
installScriptedCaller(t, caller)
if _, err := uploadDocStyleImage(context.Background(), "n1", "a.png", "a.png", "image/png", 10); err == nil {
t.Fatal("invalid upload info returned nil")
}
}
func TestCrossPlatformCoverageValidateCoverImageFileSizeLimit(t *testing.T) {
// 上限以内的正常图片通过校验。
path := writeTempImage(t, "cover.png")
if _, _, err := validateCoverImageFile(path); err != nil {
t.Fatal(err)
}
// os.Truncate 生成超过上限 1 字节的稀疏文件(瞬时创建,不占实际磁盘)。
if err := os.Truncate(path, docStyleCoverMaxBytes+1); err != nil {
t.Fatal(err)
}
_, _, err := validateCoverImageFile(path)
if err == nil || !strings.Contains(err.Error(), "封面图片文件过大") || !strings.Contains(err.Error(), "20 MiB") {
t.Fatalf("err = %v, want size limit error", err)
}
// 恰好等于上限时仍应通过。
if err := os.Truncate(path, docStyleCoverMaxBytes); err != nil {
t.Fatal(err)
}
if _, _, err := validateCoverImageFile(path); err != nil {
t.Fatalf("size == limit should pass, got %v", err)
}
}
func TestCrossPlatformCoverageDocStyleCoverSetSizeLimitViaCommand(t *testing.T) {
path := writeTempImage(t, "cover.png")
if err := os.Truncate(path, docStyleCoverMaxBytes+1); err != nil {
t.Fatal(err)
}
caller := &scriptedToolCaller{}
err := executeDocStyleCommand(t, caller, "cover", "set", "--node", "n1", "--file", path)
if err == nil || !strings.Contains(err.Error(), "封面图片文件过大") {
t.Fatalf("err = %v, want size limit error", err)
}
if caller.calls != 0 {
t.Fatalf("calls = %d, want 0 (fail before any network request)", caller.calls)
}
}
func TestCrossPlatformCoverageDocStyleCoverSetNilContextFallback(t *testing.T) {
// 直接调用 RunE(不经 Execute),cmd.Context() 为 nil,覆盖 context.Background() 兜底分支。
path := writeTempImage(t, "cover.png")
stubHTTPPutFile(t, nil)
caller := &scriptedToolCaller{steps: []scriptedToolStep{
{text: `{"uploadUrl":"https://oss.test/put","resourceId":"res-1"}`},
{text: `{}`},
}}
installScriptedCaller(t, caller)
root := newDocStyleCommand()
coverSet, _, err := root.Find([]string{"cover", "set"})
if err != nil {
t.Fatal(err)
}
if coverSet.Context() != nil {
t.Fatal("expected nil context on unexecuted command")
}
if err := coverSet.Flags().Set("node", "n1"); err != nil {
t.Fatal(err)
}
if err := coverSet.Flags().Set("file", path); err != nil {
t.Fatal(err)
}
if err := runDocStyleCoverSet(coverSet, nil); err != nil {
t.Fatal(err)
}
if caller.calls != 2 {
t.Fatalf("calls = %d, want 2 (upload info + style set)", caller.calls)
}
}
func TestCrossPlatformCoverageDocStyleCoverSetUsesCommandContext(t *testing.T) {
// 经 ExecuteContext 执行时,上传流程应使用命令上下文;scriptedToolCaller 忽略 ctx,
// 故此处做行为性断言:正常上下文下上传路径成功走通(覆盖 ctx := cmd.Context() 分支)。
path := writeTempImage(t, "cover.png")
stubHTTPPutFile(t, nil)
caller := &scriptedToolCaller{steps: []scriptedToolStep{
{text: `{"uploadUrl":"https://oss.test/put","resourceId":"res-1"}`},
{text: `{}`},
}}
installScriptedCaller(t, caller)
root := newDocStyleCommand()
root.SilenceErrors = true
root.SilenceUsage = true
root.SetArgs([]string{"cover", "set", "--node", "n1", "--file", path})
if err := root.ExecuteContext(context.Background()); err != nil {
t.Fatal(err)
}
if caller.calls != 2 {
t.Fatalf("calls = %d, want 2 (upload info + style set)", caller.calls)
}
}