Files
dingtalk-workspace-cli/internal/shortcut/runner_coverage_test.go

234 lines
9.2 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
package shortcut
import (
"bytes"
"context"
"errors"
"strings"
"testing"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/helpers"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/output"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
"github.com/spf13/cobra"
)
type runtimeContextKey struct{}
type runtimeContextCaller struct {
value any
}
func (c *runtimeContextCaller) CallTool(ctx context.Context, _ string, _ string, _ map[string]any) (*edition.ToolResult, error) {
c.value = ctx.Value(runtimeContextKey{})
return nil, errors.New("stop after context capture")
}
func (*runtimeContextCaller) Format() string { return "json" }
func (*runtimeContextCaller) DryRun() bool { return false }
func (*runtimeContextCaller) Fields() string { return "" }
func (*runtimeContextCaller) JQ() string { return "" }
func TestCrossPlatformCoverageRuntimeContextForTest(t *testing.T) {
cmd := &cobra.Command{Use: "run"}
rt := RuntimeContextForTest(cmd, Shortcut{Service: "sample", Command: "run"})
if rt == nil || rt.cmd != cmd || rt.shortcut.Service != "sample" {
t.Fatalf("RuntimeContextForTest = %#v", rt)
}
}
func TestCrossPlatformCoverageRuntimeMCPCallsPreserveCommandContext(t *testing.T) {
caller := &runtimeContextCaller{}
old := helpers.GetCaller()
t.Cleanup(func() { helpers.InitDeps(old) })
helpers.InitDeps(caller)
ctx := context.WithValue(context.Background(), runtimeContextKey{}, "command-context")
cmd := &cobra.Command{Use: "+write"}
cmd.SetContext(ctx)
output.SetCommandRollout(cmd, output.RolloutDualValidate)
rt := RuntimeContextForTest(cmd, Shortcut{Service: "aitable", Command: "+write"})
if err := rt.CallMCP("update_records", map[string]any{"id": "r1"}); err == nil {
t.Fatal("dual-validate context capture unexpectedly succeeded")
}
if caller.value != "command-context" {
t.Fatalf("dual-validate caller context value = %#v", caller.value)
}
if _, err := rt.CallMCPWriteDataStrict("aitable", "update_records", map[string]any{"id": "r1"}); err == nil {
t.Fatal("capture caller unexpectedly succeeded")
}
if caller.value != "command-context" {
t.Fatalf("caller context value = %#v", caller.value)
}
dryCaller := &dualValidateCaller{format: "json", dryRun: true}
helpers.InitDeps(dryCaller)
dryCmd := &cobra.Command{Use: "+read"}
dryCmd.Flags().Bool("dry-run", true, "")
output.SetCommandRollout(dryCmd, output.RolloutDualValidate)
var dryOut bytes.Buffer
helpers.GetFormatter().SetWriters(&dryOut, &dryOut)
dryRT := RuntimeContextForTest(dryCmd, Shortcut{Service: "aitable", Command: "+read"})
if err := dryRT.CallMCP("get_fields", map[string]any{"baseId": "b"}); err != nil {
t.Fatalf("dual-validate dry-run call = %v", err)
}
if !strings.Contains(dryOut.String(), `"dry_run": true`) {
t.Fatalf("dual-validate dry-run output = %q", dryOut.String())
}
}
func TestShortcutCommandResultRejectsStringSuccess(t *testing.T) {
result := shortcutCommandResult(map[string]any{"success": "false"})
env, err := output.EnvelopeFromResult(result)
if err != nil {
t.Fatal(err)
}
if env.Outcome != output.OutcomeFailure || env.Error == nil ||
env.Error.Subtype != "invalid_success_type" || env.Error.Hint == "" {
t.Fatalf("string success envelope = %+v", env)
}
}
func TestGenericWriteProjectionRequiresExplicitSuccessEvidence(t *testing.T) {
rt := RuntimeContextForTest(&cobra.Command{Use: "+write"}, Shortcut{
Service: "sample",
Command: "+write",
Risk: RiskWrite,
})
result := rt.resultForPayload("update_item", map[string]any{"id": "item-1"})
env, err := output.EnvelopeFromResult(result)
if err != nil {
t.Fatal(err)
}
if env.Outcome != output.OutcomeFailure || env.Error == nil ||
env.Error.Subtype != "projection_unknown" || env.Error.ExecutionStarted == nil ||
!*env.Error.ExecutionStarted || env.Error.Retryable {
t.Fatalf("opaque write envelope = %+v", env)
}
result = rt.resultForPayload("update_item", map[string]any{"success": true, "id": "item-1"})
if result.Outcome() != output.OutcomeSuccess {
t.Fatalf("explicit write outcome = %q", result.Outcome())
}
}
func TestCrossPlatformCoverageRuntimeWriteDataRemainingBranches(t *testing.T) {
caller := &runtimeReadCoverageCaller{text: `not-json`}
old := helpers.GetCaller()
t.Cleanup(func() { helpers.InitDeps(old) })
helpers.InitDeps(caller)
rt := &RuntimeContext{}
if _, err := rt.callMCPWriteData("aitable", "update_records", nil); err == nil || !strings.Contains(err.Error(), "解析") {
t.Fatalf("invalid write JSON = %v", err)
}
if caller.args == nil {
t.Fatal("nil write parameters were not normalized")
}
caller.text = ""
legacy, err := rt.CallMCPWriteData("chat", "send_personal_message", nil)
if err != nil || legacy == nil || len(legacy) != 0 {
t.Fatalf("legacy empty acknowledgement = %#v, %v", legacy, err)
}
if _, err = rt.CallMCPWriteDataStrict("aitable", "update_records", nil); err == nil {
t.Fatal("strict empty acknowledgement was accepted")
} else {
var typed *apperrors.Error
if !errors.As(err, &typed) || typed.Reason != "empty_tool_response" || !typed.RetryableSet || typed.Retryable {
t.Fatalf("strict empty acknowledgement error = %#v", err)
}
}
}
func TestFrameworkShortcutUnifiedOutputAndProjectionEdges(t *testing.T) {
oldCaller := helpers.GetCaller()
t.Cleanup(func() { helpers.InitDeps(oldCaller) })
ctx, _ := output.WithResultStore(context.Background())
cmd := &cobra.Command{Use: "+read"}
cmd.SetContext(ctx)
output.SetCommandRollout(cmd, output.RolloutUnifiedActive)
rt := RuntimeContextForTest(cmd, Shortcut{Service: "sample", Command: "+read", Safety: contract.SafetySpec{Effect: "read"}})
if err := rt.Output(map[string]any{"id": "x"}); err != nil {
t.Fatal(err)
}
caller := &runtimeReadCoverageCaller{text: `{"success":true,"id":"server"}`}
helpers.InitDeps(caller)
callCtx, _ := output.WithResultStore(context.Background())
callCmd := &cobra.Command{Use: "+call"}
callCmd.SetContext(callCtx)
output.SetCommandRollout(callCmd, output.RolloutUnifiedActive)
callRT := RuntimeContextForTest(callCmd, Shortcut{Service: "sample", Command: "+call", Safety: contract.SafetySpec{Effect: "read"}})
if err := callRT.CallMCP("get", nil); err != nil {
t.Fatal(err)
}
caller.err = errors.New("backend")
errorCtx, _ := output.WithResultStore(context.Background())
errorCmd := &cobra.Command{Use: "+error"}
errorCmd.SetContext(errorCtx)
output.SetCommandRollout(errorCmd, output.RolloutUnifiedActive)
errorRT := RuntimeContextForTest(errorCmd, Shortcut{Service: "sample", Command: "+error", Safety: contract.SafetySpec{Effect: "read"}})
if err := errorRT.CallMCP("get", nil); err == nil {
t.Fatal("backend error swallowed")
}
caller.err = nil
dryCtx, _ := output.WithResultStore(context.Background())
dryCmd := &cobra.Command{Use: "+dry"}
dryCmd.SetContext(dryCtx)
dryCmd.Flags().Bool("dry-run", true, "")
_ = dryCmd.Flags().Set("dry-run", "true")
output.SetCommandRollout(dryCmd, output.RolloutUnifiedActive)
dryRT := RuntimeContextForTest(dryCmd, Shortcut{Service: "sample", Command: "+dry", Safety: contract.SafetySpec{Effect: "read"}})
if err := dryRT.CallMCP("get", nil); err != nil {
t.Fatal(err)
}
dualCmd := &cobra.Command{Use: "+dual"}
dualCmd.SetContext(context.Background())
dualCmd.SetOut(&bytes.Buffer{})
output.SetCommandRollout(dualCmd, output.RolloutDualValidate)
dualRT := RuntimeContextForTest(dualCmd, Shortcut{Service: "sample", Command: "+dual", Safety: contract.SafetySpec{Effect: "read"}})
if err := dualRT.Output(map[string]any{"success": "false"}); err != nil {
t.Fatalf("dual validation should accept a typed failure result: %v", err)
}
for _, tc := range []struct {
name string
payload any
outcome output.Outcome
}{
{"scalar", "value", output.OutcomeSuccess},
{"nested success", map[string]any{"content": map[string]any{"success": true}}, output.OutcomeSuccess},
{"failure fallback", map[string]any{"success": false}, output.OutcomeFailure},
{"failure message", map[string]any{"content": map[string]any{"success": false, "errorMessage": "bad"}}, output.OutcomeFailure},
} {
t.Run(tc.name, func(t *testing.T) {
result := shortcutCommandResult(tc.payload, output.WithDryRun())
if result.Outcome() != tc.outcome {
t.Fatalf("outcome=%s", result.Outcome())
}
})
}
if hasExplicitShortcutSuccess("scalar") || hasExplicitShortcutSuccess(map[string]any{"success": false}) || !hasExplicitShortcutSuccess(map[string]any{"content": map[string]any{"success": true}}) {
t.Fatal("explicit success classification mismatch")
}
for _, shortcut := range []Shortcut{
{Safety: contract.SafetySpec{Effect: "write"}},
{Safety: contract.SafetySpec{Effect: "destructive"}},
{Risk: RiskHighWrite},
{Risk: RiskRead},
} {
probe := RuntimeContextForTest(&cobra.Command{Use: "probe"}, shortcut)
_ = probe.isWriteShortcut()
}
devRT := RuntimeContextForTest(&cobra.Command{Use: "dev"}, Shortcut{Service: "devapp", Command: "+get"})
if got := devRT.resultForPayload("get_dev_app", map[string]any{"success": true}); got.Outcome() != output.OutcomeSuccess {
t.Fatalf("devapp result=%s", got.Outcome())
}
}