Files

100 lines
3.4 KiB
Go

package corecmd
import (
"bytes"
"context"
"errors"
"strings"
"testing"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/output"
"github.com/spf13/cobra"
)
func TestResultInvokeCarriesOneFrameworkResult(t *testing.T) {
calls := 0
ctx, store := output.WithResultStore(context.Background())
cmd := New(Spec{
Use: "result",
OutputRollout: output.RolloutUnifiedActive,
Safety: contract.SafetySpec{
Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent",
},
ResultInvoke: func(*Ctx, map[string]any) (output.CommandResult, error) {
calls++
return output.Success(map[string]any{"id": "a"}), nil
},
})
cmd.SetContext(ctx)
cmd.PersistentFlags().String("format", "json", "")
var stdout bytes.Buffer
cmd.SetOut(&stdout)
cmd.PersistentPostRunE = func(executed *cobra.Command, _ []string) error {
_, _, err := output.EmitStoredResult(executed)
return err
}
if err := cmd.Execute(); err != nil {
t.Fatal(err)
}
if calls != 1 {
t.Fatalf("calls=%d, want 1", calls)
}
if code, emitted := output.StoredExitCode(store); !emitted || code != 0 {
t.Fatalf("stored code/emitted=%d/%v", code, emitted)
}
if !strings.Contains(stdout.String(), `"outcome": "success"`) || strings.Contains(stdout.String(), `"contract_version"`) {
t.Fatalf("stdout=%s", stdout.String())
}
if output.CommandRollout(cmd) != output.RolloutUnifiedActive {
t.Fatalf("rollout=%s", output.CommandRollout(cmd))
}
}
func TestFrameworkResultInvokeErrorLegacyAndStoreEdges(t *testing.T) {
wantErr := errors.New("invoke failed")
cases := []struct {
name string
rollout output.RolloutState
invoke func(*Ctx, map[string]any) (output.CommandResult, error)
want string
}{
{"invoke error", output.RolloutUnifiedActive, func(*Ctx, map[string]any) (output.CommandResult, error) { return nil, wantErr }, "invoke failed"},
{"legacy guard", output.RolloutLegacyOnly, func(*Ctx, map[string]any) (output.CommandResult, error) { return output.Success(nil), nil }, "without an active unified-result rollout"},
{"missing store", output.RolloutUnifiedActive, func(*Ctx, map[string]any) (output.CommandResult, error) { return output.Success(nil), nil }, "no result store"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
cmd := New(Spec{Use: "result", OutputRollout: tc.rollout, Safety: contract.SafetySpec{Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent"}, ResultInvoke: tc.invoke})
cmd.SetArgs(nil)
err := cmd.Execute()
if err == nil || !strings.Contains(err.Error(), tc.want) {
t.Fatalf("Execute error=%v, want %q", err, tc.want)
}
})
}
}
func TestLegacyResultInvokeIsRejectedBeforeBusinessDispatch(t *testing.T) {
calls := 0
cmd := New(Spec{
Use: "result",
OutputRollout: output.RolloutLegacyOnly,
Safety: contract.SafetySpec{
Effect: "write", Risk: "high", Confirmation: "not_required", Idempotency: "unknown",
},
ResultInvoke: func(*Ctx, map[string]any) (output.CommandResult, error) {
calls++
return output.Success(map[string]any{"changed": true}), nil
},
})
cmd.SetArgs(nil)
err := cmd.Execute()
if err == nil || !strings.Contains(err.Error(), "without an active unified-result rollout") {
t.Fatalf("Execute error=%v", err)
}
if calls != 0 {
t.Fatalf("business dispatcher ran %d time(s), want 0", calls)
}
}