Cover remaining cli/agentmetadata/pat edge paths so aggregate coverage stays at or above the merge-base overall percentage. Co-authored-by: Cursor <cursoragent@cursor.com>
628 lines
26 KiB
Go
628 lines
26 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
package pat
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
|
|
)
|
|
|
|
func TestOverallCoverageGapBatchGrantConfirmation(t *testing.T) {
|
|
cmd := &cobra.Command{Use: "chmod"}
|
|
cmd.Flags().Bool("yes", false, "")
|
|
if err := requireBatchGrantConfirmation(cmd, true, []string{"a", "b"}); err == nil {
|
|
t.Fatal("batch grant without --yes must fail")
|
|
}
|
|
_ = cmd.Flags().Set("yes", "true")
|
|
if err := requireBatchGrantConfirmation(cmd, true, []string{"a", "b"}); err != nil {
|
|
t.Fatalf("--yes should allow batch grant: %v", err)
|
|
}
|
|
if err := requireBatchGrantConfirmation(cmd, false, []string{"only"}); err != nil {
|
|
t.Fatalf("single-scope non-plan grant should pass: %v", err)
|
|
}
|
|
}
|
|
|
|
type patEdgeCaller struct {
|
|
results []*edition.ToolResult
|
|
errs []error
|
|
calls int
|
|
dryRun bool
|
|
}
|
|
|
|
func (c *patEdgeCaller) CallTool(context.Context, string, string, map[string]any) (*edition.ToolResult, error) {
|
|
index := c.calls
|
|
c.calls++
|
|
var result *edition.ToolResult
|
|
var err error
|
|
if index < len(c.results) {
|
|
result = c.results[index]
|
|
}
|
|
if index < len(c.errs) {
|
|
err = c.errs[index]
|
|
}
|
|
return result, err
|
|
}
|
|
func (*patEdgeCaller) Format() string { return "json" }
|
|
func (c *patEdgeCaller) DryRun() bool { return c.dryRun }
|
|
func (*patEdgeCaller) Fields() string { return "" }
|
|
func (*patEdgeCaller) JQ() string { return "" }
|
|
|
|
type patFailWriter struct{}
|
|
|
|
func (patFailWriter) Write([]byte) (int, error) { return 0, errors.New("write failed") }
|
|
|
|
func patText(text string) *edition.ToolResult {
|
|
return &edition.ToolResult{Content: []edition.ContentBlock{{Type: "text", Text: text}}}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageBrowserPolicyInjectedEdges(t *testing.T) {
|
|
originalRead := patPolicyReadFile
|
|
originalHome := patPolicyUserHomeDir
|
|
originalMarshal := patPolicyMarshal
|
|
originalWrite := patPolicyAtomicWrite
|
|
originalEdition := edition.Get()
|
|
t.Cleanup(func() {
|
|
patPolicyReadFile = originalRead
|
|
patPolicyUserHomeDir = originalHome
|
|
patPolicyMarshal = originalMarshal
|
|
patPolicyAtomicWrite = originalWrite
|
|
edition.Override(originalEdition)
|
|
})
|
|
failure := errors.New("injected failure")
|
|
|
|
t.Setenv("DWS_CONFIG_DIR", "/env/config")
|
|
if got := patConfigDir(); got != "/env/config" {
|
|
t.Fatalf("env config dir = %q", got)
|
|
}
|
|
t.Setenv("DWS_CONFIG_DIR", "")
|
|
edition.Override(&edition.Hooks{ConfigDir: func() string { return "/edition/config" }})
|
|
if got := patConfigDir(); got != "/edition/config" {
|
|
t.Fatalf("edition config dir = %q", got)
|
|
}
|
|
edition.Override(&edition.Hooks{})
|
|
patPolicyUserHomeDir = func() (string, error) { return "", failure }
|
|
if got := patConfigDir(); got != ".dws" {
|
|
t.Fatalf("failed home config dir = %q", got)
|
|
}
|
|
homeDir := filepath.Join(t.TempDir(), "home")
|
|
patPolicyUserHomeDir = func() (string, error) { return homeDir, nil }
|
|
if got := patConfigDir(); got != filepath.Join(homeDir, ".dws") {
|
|
t.Fatalf("home config dir = %q", got)
|
|
}
|
|
|
|
patPolicyReadFile = func(string) ([]byte, error) { return nil, failure }
|
|
if _, err := LoadBrowserPolicy("x"); !errors.Is(err, failure) {
|
|
t.Fatalf("read error = %v", err)
|
|
}
|
|
patPolicyReadFile = func(string) ([]byte, error) { return []byte(`{`), nil }
|
|
if _, err := LoadBrowserPolicy("x"); err == nil {
|
|
t.Fatal("invalid policy decoded")
|
|
}
|
|
patPolicyReadFile = func(string) ([]byte, error) { return []byte(`{}`), nil }
|
|
if policy, err := LoadBrowserPolicy("x"); err != nil || policy.Agents == nil {
|
|
t.Fatalf("empty policy = %#v, %v", policy, err)
|
|
}
|
|
patPolicyReadFile = originalRead
|
|
|
|
patPolicyMarshal = func(any, string, string) ([]byte, error) { return nil, failure }
|
|
if err := saveBrowserPolicy("x", nil); !errors.Is(err, failure) {
|
|
t.Fatalf("marshal error = %v", err)
|
|
}
|
|
patPolicyMarshal = originalMarshal
|
|
patPolicyAtomicWrite = func(string, []byte) error { return failure }
|
|
if err := saveBrowserPolicy("x", &BrowserPolicy{}); !errors.Is(err, failure) {
|
|
t.Fatalf("write error = %v", err)
|
|
}
|
|
patPolicyAtomicWrite = originalWrite
|
|
|
|
policyDir := t.TempDir()
|
|
patPolicyReadFile = func(string) ([]byte, error) { return nil, failure }
|
|
if _, err := ResolveBrowserPolicy(policyDir, ""); !errors.Is(err, failure) {
|
|
t.Fatalf("resolve policy read error = %v", err)
|
|
}
|
|
if EffectiveOpenBrowser(policyDir) != true {
|
|
t.Fatal("policy error should preserve browser-open fallback")
|
|
}
|
|
if _, err := SetBrowserPolicy(policyDir, "", true); !errors.Is(err, failure) {
|
|
t.Fatalf("set policy read error = %v", err)
|
|
}
|
|
patPolicyReadFile = originalRead
|
|
if _, err := ResolveBrowserPolicy(t.TempDir(), "bad code"); err == nil {
|
|
t.Fatal("invalid agent code accepted")
|
|
}
|
|
if _, err := SetBrowserPolicy(t.TempDir(), "bad code", true); err == nil {
|
|
t.Fatal("invalid policy agent code accepted")
|
|
}
|
|
patPolicyAtomicWrite = func(string, []byte) error { return failure }
|
|
if _, err := SetBrowserPolicy(t.TempDir(), "agent", true); !errors.Is(err, failure) {
|
|
t.Fatalf("agent save error = %v", err)
|
|
}
|
|
if _, err := SetBrowserPolicy(t.TempDir(), "", true); !errors.Is(err, failure) {
|
|
t.Fatalf("default save error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageBrowserPolicyCommandWriteError(t *testing.T) {
|
|
t.Setenv("DWS_CONFIG_DIR", t.TempDir())
|
|
cmd := newBrowserPolicyCommand()
|
|
cmd.SetOut(patFailWriter{})
|
|
cmd.SetArgs([]string{"--enabled=true"})
|
|
if err := cmd.Execute(); err == nil {
|
|
t.Fatal("output write error did not propagate")
|
|
}
|
|
if got := EffectiveOpenBrowser(os.Getenv("DWS_CONFIG_DIR")); !got {
|
|
t.Fatal("saved browser policy was not effective")
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageBrowserPolicyCommandSetError(t *testing.T) {
|
|
badDir := filepath.Join(t.TempDir(), "file")
|
|
if err := os.WriteFile(badDir, []byte("x"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Setenv("DWS_CONFIG_DIR", badDir)
|
|
cmd := newBrowserPolicyCommand()
|
|
cmd.SetArgs([]string{"--enabled=true"})
|
|
if err := cmd.Execute(); err == nil {
|
|
t.Fatal("policy command ignored load error")
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoveragePATPureHelperEdges(t *testing.T) {
|
|
if got := collectChmodProductCodes([]string{"", "a,a", " b "}, []string{"b", "c"}); strings.Join(got, ",") != "a,b,c" {
|
|
t.Fatalf("product codes = %#v", got)
|
|
}
|
|
if _, err := callPATBatchGrantWithLegacyFallback(t.Context(), nil, "", "", nil, nil, nil); err == nil {
|
|
t.Fatal("nil batch grant caller accepted")
|
|
}
|
|
if _, err := callPATBatchPlan(t.Context(), nil, "", "", nil); err == nil {
|
|
t.Fatal("nil batch plan caller accepted")
|
|
}
|
|
if _, err := callPATToolWithLegacyFallback(t.Context(), nil, "", "", "", nil, nil); err == nil {
|
|
t.Fatal("nil legacy caller accepted")
|
|
}
|
|
caller := &patEdgeCaller{errs: []error{errors.New("boom")}}
|
|
if _, err := callPATToolWithLegacyFallback(t.Context(), caller, "pat", "tool", "", nil, nil); err == nil {
|
|
t.Fatal("empty alias error did not propagate")
|
|
}
|
|
caller = &patEdgeCaller{results: []*edition.ToolResult{patText(`{"code":"PAT_FORGED_IDENTITY_FIELD"}`)}, errs: []error{nil, errors.New("second failed")}}
|
|
if _, err := callPATBatchToolWithIdentityFallback(t.Context(), caller, "agent", "", "tool", map[string]any{"agentCode": "agent"}); err == nil {
|
|
t.Fatal("identity fallback retry error did not propagate")
|
|
}
|
|
|
|
if !strings.HasPrefix(newBatchClientRequestID(""), "batch-") {
|
|
t.Fatal("default request ID prefix missing")
|
|
}
|
|
if got := normalizeProductCodes([]string{"", "a,a", " b "}); strings.Join(got, ",") != "a,b" {
|
|
t.Fatalf("normalized product codes = %#v", got)
|
|
}
|
|
for scope, want := range map[string]string{"": "", "mail.read": "mail", "doc:read": "doc", "drive": "drive", " x.y:z ": "x"} {
|
|
if got := productCodeFromScope(scope); got != want {
|
|
t.Fatalf("productCodeFromScope(%q) = %q, want %q", scope, got, want)
|
|
}
|
|
}
|
|
if !isEmptyToolResult(nil) || !isEmptyToolResult(&edition.ToolResult{}) || !isEmptyToolResult(&edition.ToolResult{Content: []edition.ContentBlock{{Type: "image"}}}) {
|
|
t.Fatal("empty tool result detection failed")
|
|
}
|
|
if firstToolResultText(nil) != "" || firstToolResultText(&edition.ToolResult{Content: []edition.ContentBlock{{Type: "image", Text: "x"}}}) != "" {
|
|
t.Fatal("first text extraction failed")
|
|
}
|
|
if patResultAgentCode(nil) != "" || patResultAgentCode(patText(`{`)) != "" {
|
|
t.Fatal("invalid PAT result produced agent code")
|
|
}
|
|
for raw, want := range map[string]string{
|
|
`{"agentCode":"top"}`: "top",
|
|
`{"data":{"agentCode":"data"}}`: "data",
|
|
`{"result":{"agentCode":"result"}}`: "result",
|
|
`{"result":{"data":{"agentCode":"deep"}}}`: "deep",
|
|
`{}`: "",
|
|
} {
|
|
if got := patResultAgentCode(patText(raw)); got != want {
|
|
t.Fatalf("agent code from %s = %q, want %q", raw, got, want)
|
|
}
|
|
}
|
|
if err := ensurePATResultAgentCode(nil, ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := ensurePATIdentityFallbackAgentCode(nil, ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := ensurePATIdentityFallbackAgentCode(nil, "expected"); err == nil {
|
|
t.Fatal("missing fallback agent code accepted")
|
|
}
|
|
if patBatchResultHasCode(nil, func(string) bool { return true }) || patBatchResultHasCode(patText(`{`), func(string) bool { return true }) {
|
|
t.Fatal("invalid batch result matched")
|
|
}
|
|
if !patBatchResultHasCode(patText(`{"error_code":"match"}`), func(code string) bool { return code == "match" }) {
|
|
t.Fatal("error_code was not matched")
|
|
}
|
|
if isPATBatchFallbackError(nil) || !isPATBatchFallbackError(errors.New("PAT_BATCH_AUTH_UNSUPPORTED")) {
|
|
t.Fatal("batch fallback error detection failed")
|
|
}
|
|
if isToolNotRegisteredError(nil) || !isToolNotRegisteredError(errors.New("unknown tool")) {
|
|
t.Fatal("tool registration error detection failed")
|
|
}
|
|
if hasScopeKeyShapeMismatch(nil, nil) || hasScopeKeyShapeMismatch(map[string]any{}, map[string]any{}) {
|
|
t.Fatal("scope shape mismatch false positive")
|
|
}
|
|
canonical := map[string]any{"scopes": []string{"a"}}
|
|
legacy := map[string]any{"scope": "a"}
|
|
if isLegacyGrantSchemaMismatchError(nil, canonical, legacy) || isLegacyGrantSchemaMismatchError(&apperrors.PATError{RawJSON: "scope validation"}, canonical, legacy) {
|
|
t.Fatal("legacy mismatch accepted excluded error")
|
|
}
|
|
for _, message := range []string{"missing parameter", "scope missing_scope", "scope invalid permission denied", "scope unknown"} {
|
|
_ = isLegacyGrantSchemaMismatchError(errors.New(message), canonical, legacy)
|
|
}
|
|
if normalizedPATErrorText(nil) != "" {
|
|
t.Fatal("nil normalized error text was non-empty")
|
|
}
|
|
typed := apperrors.NewAPI("message", apperrors.WithReason("reason"), apperrors.WithHint("hint"))
|
|
if got := normalizedPATErrorText(typed); !strings.Contains(got, "reason") || !strings.Contains(got, "hint") {
|
|
t.Fatalf("normalized typed error = %q", got)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoveragePATPlanExtractionEdges(t *testing.T) {
|
|
invalidResults := []*edition.ToolResult{nil, patText(`{`), patText(`{}`)}
|
|
for _, result := range invalidResults {
|
|
_, _ = extractLoginRecommendProducts(result)
|
|
_, _ = extractBatchPlanAllGranted(result)
|
|
_, _ = extractSelectedScopes(result)
|
|
_, _ = extractSelectedScopesAllowEmpty(result)
|
|
}
|
|
productsJSON := `{"data":{"selectedScopes":["mail.read",12,""],"items":[12,{"scope":""},{"scope":"mail.read","productName":"","displayName":"Read"},{"scope":"mail.write","operationSummary":"Write"},{"scope":"mail.other","operationSummary":"Write"},{"scope":"mail.more","operationSummary":"More"},{"scope":"mail.last","operationSummary":"Last"}]}}`
|
|
products, err := extractLoginRecommendProducts(patText(productsJSON))
|
|
if err != nil || len(products) != 1 || products[0].ProductCode != "mail" || products[0].SelectedScopeCount != 1 {
|
|
t.Fatalf("products = %#v, %v", products, err)
|
|
}
|
|
fallbackJSON := `{"data":{"selectedScopes":["doc.read","doc.write","",12],"items":[]}}`
|
|
products, err = extractLoginRecommendProducts(patText(fallbackJSON))
|
|
if err != nil || len(products) != 1 || products[0].ScopeCount != 2 {
|
|
t.Fatalf("fallback products = %#v, %v", products, err)
|
|
}
|
|
if _, err := extractSelectedScopes(patText(`{"data":{}}`)); err == nil {
|
|
t.Fatal("empty selected scopes accepted")
|
|
}
|
|
if got, err := extractSelectedScopes(patText(`{"data":{"allGranted":true}}`)); err != nil || len(got) != 0 {
|
|
t.Fatalf("all-granted scopes = %#v, %v", got, err)
|
|
}
|
|
if _, err := extractSelectedScopes(patText(`{"data":{"selectedScopes":[12," "]}}`)); err == nil {
|
|
t.Fatal("non-string scopes accepted")
|
|
}
|
|
if got, err := extractSelectedScopes(patText(`{"data":{"selectedScopes":[12," "],"allGranted":true}}`)); err != nil || len(got) != 0 {
|
|
t.Fatalf("filtered all-granted scopes = %#v, %v", got, err)
|
|
}
|
|
if got, err := extractSelectedScopesAllowEmpty(patText(`{"data":{"selectedScopes":[12," ","one"]}}`)); err != nil || len(got) != 1 {
|
|
t.Fatalf("allow-empty scopes = %#v, %v", got, err)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoveragePATResultAndFlagEdges(t *testing.T) {
|
|
if err := handleToolResult(nil, nil, nil); err == nil {
|
|
t.Fatal("nil result accepted")
|
|
}
|
|
if err := handleToolResultForWriter(io.Discard, nil, nil); err == nil {
|
|
t.Fatal("nil writer result accepted")
|
|
}
|
|
if err := classifyToolResultText(nil); err == nil {
|
|
t.Fatal("nil result classified successfully")
|
|
}
|
|
nonText := &edition.ToolResult{Content: []edition.ContentBlock{{Type: "image", Text: "x"}}}
|
|
if err := handleToolResultForWriter(io.Discard, nonText, nil); err == nil {
|
|
t.Fatal("non-text result accepted")
|
|
}
|
|
if err := classifyToolResultText(nonText); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
patFailure := patText(`{"code":"PAT_NO_PERMISSION"}`)
|
|
if _, err := captureStdout(t, func() error { return handleToolResult(nil, nil, patFailure) }); err == nil {
|
|
t.Fatal("PAT error result was accepted by stdout handler")
|
|
}
|
|
if _, err := captureStdout(t, func() error { return handleToolResult(nil, nil, nonText) }); err == nil {
|
|
t.Fatal("non-text result was accepted by stdout handler")
|
|
}
|
|
if err := handleToolResultForWriter(io.Discard, patFailure, nil); err == nil {
|
|
t.Fatal("PAT error result accepted")
|
|
}
|
|
if err := classifyToolResultText(patFailure); err == nil {
|
|
t.Fatal("PAT error was not classified")
|
|
}
|
|
if err := handleToolResultForWriter(nil, patText(`{"data":{}}`), nil); err != nil {
|
|
t.Fatalf("nil output writer: %v", err)
|
|
}
|
|
var buf bytes.Buffer
|
|
if err := handleToolResultForWriter(&buf, patText(`plain text`), nil); err != nil || !strings.Contains(buf.String(), "plain text") {
|
|
t.Fatalf("plain writer result = %q, %v", buf.String(), err)
|
|
}
|
|
|
|
if commandBoolFlag(nil, "verbose") || commandFlagChanged(nil, "format") || commandStringFlag(nil, "format") != "" {
|
|
t.Fatal("nil command flag helpers returned values")
|
|
}
|
|
root := &cobra.Command{Use: "root"}
|
|
root.PersistentFlags().Bool("verbose", true, "")
|
|
root.PersistentFlags().String("format", "raw", "")
|
|
child := &cobra.Command{Use: "child"}
|
|
root.AddCommand(child)
|
|
if !commandBoolFlag(child, "verbose") || commandStringFlag(child, "format") != "raw" {
|
|
t.Fatal("persistent flags were not resolved")
|
|
}
|
|
if commandFlagChanged(child, "format") {
|
|
t.Fatal("unchanged format reported changed")
|
|
}
|
|
if err := root.PersistentFlags().Set("format", "json"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !commandFlagChanged(child, "format") {
|
|
t.Fatal("changed persistent format not detected")
|
|
}
|
|
local := &cobra.Command{Use: "local"}
|
|
local.Flags().Bool("verbose", true, "")
|
|
local.Flags().String("format", "json", "")
|
|
if !commandBoolFlag(local, "verbose") || commandStringFlag(local, "format") != "json" {
|
|
t.Fatal("local flags were not resolved")
|
|
}
|
|
if err := local.Flags().Set("format", "raw"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !commandFlagChanged(local, "format") {
|
|
t.Fatal("changed local format not detected")
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoveragePATAuthorizationSummaryEdges(t *testing.T) {
|
|
if formatPATAuthorizationSummary("not json", nil) != "" || formatPATAuthorizationSummary(`{}`, nil) != "" {
|
|
t.Fatal("invalid summary payload rendered")
|
|
}
|
|
dry := &patEdgeCaller{dryRun: true}
|
|
texts := []string{
|
|
`{"success":true,"data":{"agentCode":"a","grantType":"once","allGranted":true,"items":[],"selectedScopes":[],"grantedScopes":[],"alreadyGrantedScopes":[],"skippedScopes":[],"pendingScopes":[]}}`,
|
|
`{"code":"OK","data":{"selectedScopes":["a"]}}`,
|
|
`{"data":{"selectedScopes":["a"]}}`,
|
|
`{"data":{"pendingScopes":["a"]}}`,
|
|
`{"data":{"items":["a"],"selectedScopes":"wrong"}}`,
|
|
}
|
|
for i, text := range texts {
|
|
caller := edition.ToolCaller(nil)
|
|
if i == 1 {
|
|
caller = dry
|
|
}
|
|
if got := formatPATAuthorizationSummary(text, caller); got == "" {
|
|
t.Fatalf("summary %d was empty", i)
|
|
}
|
|
}
|
|
if _, ok := countField(map[string]any{"values": []string{"a"}}, "values"); !ok {
|
|
t.Fatal("[]string count was not recognized")
|
|
}
|
|
if _, ok := countField(map[string]any{}, "missing"); ok {
|
|
t.Fatal("missing count was recognized")
|
|
}
|
|
if _, ok := countField(map[string]any{"bad": 1}, "bad"); ok {
|
|
t.Fatal("invalid count was recognized")
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageLoginRecommendWrappers(t *testing.T) {
|
|
if err := RunLoginRecommendAuthorization(t.Context(), nil, io.Discard); err == nil {
|
|
t.Fatal("nil login recommend caller accepted")
|
|
}
|
|
if _, err := PlanLoginRecommendAuthorization(t.Context(), nil); err == nil {
|
|
t.Fatal("nil plan caller accepted")
|
|
}
|
|
allGranted := patText(`{"data":{"selectedScopes":[],"allGranted":true,"items":[]}}`)
|
|
caller := &patEdgeCaller{results: []*edition.ToolResult{allGranted}}
|
|
var buf bytes.Buffer
|
|
if err := RunLoginRecommendAuthorization(t.Context(), caller, &buf); err != nil || buf.Len() == 0 {
|
|
t.Fatalf("login recommend wrapper = %q, %v", buf.String(), err)
|
|
}
|
|
caller = &patEdgeCaller{results: []*edition.ToolResult{allGranted}}
|
|
plan, err := PlanLoginRecommendAuthorization(t.Context(), caller)
|
|
if err != nil || !plan.AllGranted {
|
|
t.Fatalf("login recommend plan = %#v, %v", plan, err)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageChmodCommandRemainingBranches(t *testing.T) {
|
|
cmd := newChmodCommand(nil)
|
|
if err := cmd.Args(cmd, []string{"scope"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := cmd.Args(cmd, nil); err == nil {
|
|
t.Fatal("empty chmod arguments accepted")
|
|
}
|
|
cmd = newChmodCommand(nil)
|
|
if err := cmd.Flags().Parse([]string{"--recommend"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := cmd.Args(cmd, nil); err != nil {
|
|
t.Fatalf("recommend args: %v", err)
|
|
}
|
|
cmd = newChmodCommand(nil)
|
|
if err := cmd.Flags().Parse([]string{"--product", "mail"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := cmd.Args(cmd, nil); err != nil {
|
|
t.Fatalf("product args: %v", err)
|
|
}
|
|
|
|
run := func(c edition.ToolCaller, args ...string) error {
|
|
command := newChmodCommand(c)
|
|
root := &cobra.Command{Use: "dws"}
|
|
root.PersistentFlags().Bool("yes", false, "")
|
|
root.PersistentFlags().Bool("dry-run", false, "")
|
|
root.AddCommand(command)
|
|
// Catalog user_required: pass --dry-run when the caller is dry-run,
|
|
// otherwise --yes so ConfirmSafety does not mask the branch under test.
|
|
if c != nil && c.DryRun() {
|
|
args = append([]string{"chmod", "--dry-run"}, args...)
|
|
} else {
|
|
args = append([]string{"chmod", "--yes"}, args...)
|
|
}
|
|
root.SetArgs(args)
|
|
root.SilenceErrors = true
|
|
root.SilenceUsage = true
|
|
return root.Execute()
|
|
}
|
|
dryError := &patEdgeCaller{dryRun: true, errs: []error{errors.New("plan failed")}}
|
|
if err := run(dryError, "--recommend"); err == nil {
|
|
t.Fatal("dry-run plan error did not propagate")
|
|
}
|
|
dryMismatch := &patEdgeCaller{dryRun: true, results: []*edition.ToolResult{patText(`{"data":{"agentCode":"other"}}`)}}
|
|
if err := run(dryMismatch, "--recommend", "--agentCode", "expected"); err == nil {
|
|
t.Fatal("dry-run plan agent mismatch accepted")
|
|
}
|
|
dryPreview := &patEdgeCaller{dryRun: true}
|
|
if err := run(dryPreview, "scope", "--grant-type", "session", "--session-id", "sid"); err != nil {
|
|
t.Fatalf("dry-run session preview: %v", err)
|
|
}
|
|
if err := run(nil, "scope"); err == nil {
|
|
t.Fatal("nil chmod runtime accepted")
|
|
}
|
|
planError := &patEdgeCaller{errs: []error{errors.New("plan failed")}}
|
|
if err := run(planError, "--recommend"); err == nil {
|
|
t.Fatal("plan error did not propagate")
|
|
}
|
|
planMismatch := &patEdgeCaller{results: []*edition.ToolResult{patText(`{"data":{"agentCode":"other"}}`)}}
|
|
if err := run(planMismatch, "--recommend", "--agentCode", "expected"); err == nil {
|
|
t.Fatal("plan agent mismatch accepted")
|
|
}
|
|
badScopes := &patEdgeCaller{results: []*edition.ToolResult{patText(`{"data":{}}`)}}
|
|
if err := run(badScopes, "--recommend"); err == nil {
|
|
t.Fatal("invalid selected scopes accepted")
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageLoginRecommendErrorMatrix(t *testing.T) {
|
|
caller := &patEdgeCaller{errs: []error{errors.New("plan failed")}}
|
|
if _, err := PlanLoginRecommendAuthorization(t.Context(), caller); err == nil {
|
|
t.Fatal("plan wrapper error did not propagate")
|
|
}
|
|
if _, _, _, err := planLoginRecommend(t.Context(), &patEdgeCaller{}, nil, false); err == nil {
|
|
t.Fatal("all-scope plan without products accepted")
|
|
}
|
|
if _, _, _, err := planLoginRecommend(t.Context(), &patEdgeCaller{errs: []error{errors.New("call failed")}}, nil, true); err == nil {
|
|
t.Fatal("plan call error did not propagate")
|
|
}
|
|
if _, _, _, err := planLoginRecommend(t.Context(), &patEdgeCaller{results: []*edition.ToolResult{patText(`{"code":"PAT_NO_PERMISSION"}`)}}, nil, true); err == nil {
|
|
t.Fatal("classified plan result accepted")
|
|
}
|
|
if _, _, _, err := planLoginRecommend(t.Context(), &patEdgeCaller{results: []*edition.ToolResult{patText(`{}`)}}, nil, true); err == nil {
|
|
t.Fatal("malformed plan result accepted")
|
|
}
|
|
|
|
validProductPlan := patText(`{"data":{"selectedScopes":["mail.read"],"items":[{"scope":"mail.read","productCode":"mail"}]}}`)
|
|
initial := &LoginRecommendPlan{Result: validProductPlan, Scopes: []string{"mail.read"}}
|
|
if err := RunLoginRecommendAuthorizationWithOptions(t.Context(), &patEdgeCaller{}, io.Discard, LoginRecommendOptions{
|
|
InitialPlan: initial,
|
|
ProductSelector: func([]LoginRecommendProduct) ([]string, error) {
|
|
return nil, errors.New("selection failed")
|
|
},
|
|
}); err == nil {
|
|
t.Fatal("selector error did not propagate")
|
|
}
|
|
if err := RunLoginRecommendAuthorizationWithOptions(t.Context(), &patEdgeCaller{}, io.Discard, LoginRecommendOptions{
|
|
InitialPlan: initial,
|
|
ProductSelector: func([]LoginRecommendProduct) ([]string, error) {
|
|
return nil, nil
|
|
},
|
|
}); err == nil {
|
|
t.Fatal("empty product selection accepted")
|
|
}
|
|
if err := RunLoginRecommendAuthorizationWithOptions(t.Context(), &patEdgeCaller{errs: []error{errors.New("replan failed")}}, io.Discard, LoginRecommendOptions{
|
|
InitialPlan: initial,
|
|
ProductSelector: func([]LoginRecommendProduct) ([]string, error) {
|
|
return []string{"mail"}, nil
|
|
},
|
|
}); err == nil {
|
|
t.Fatal("selector replan error did not propagate")
|
|
}
|
|
if err := RunLoginRecommendAuthorizationWithOptions(t.Context(), &patEdgeCaller{}, io.Discard, LoginRecommendOptions{
|
|
ScopeMode: LoginRecommendScopeAll,
|
|
InitialPlan: &LoginRecommendPlan{Result: patText(`{"data":{"selectedScopes":[],"items":[]}}`), Scopes: []string{"synthetic"}},
|
|
ProductSelector: func([]LoginRecommendProduct) ([]string, error) {
|
|
return nil, nil
|
|
},
|
|
}); err == nil {
|
|
t.Fatal("all-scope mode without products accepted")
|
|
}
|
|
allGranted := patText(`{"data":{"selectedScopes":[],"allGranted":true,"items":[]}}`)
|
|
var output bytes.Buffer
|
|
if err := RunLoginRecommendAuthorizationWithOptions(t.Context(), &patEdgeCaller{results: []*edition.ToolResult{allGranted}}, &output, LoginRecommendOptions{
|
|
InitialPlan: initial,
|
|
ProductSelector: func([]LoginRecommendProduct) ([]string, error) {
|
|
return []string{"mail"}, nil
|
|
},
|
|
}); err != nil || output.Len() == 0 {
|
|
t.Fatalf("all-granted replan = %q, %v", output.String(), err)
|
|
}
|
|
if err := RunLoginRecommendAuthorizationWithOptions(t.Context(), &patEdgeCaller{errs: []error{errors.New("grant failed")}}, io.Discard, LoginRecommendOptions{
|
|
InitialPlan: initial,
|
|
}); err == nil {
|
|
t.Fatal("grant error did not propagate")
|
|
}
|
|
if err := RunLoginRecommendAuthorizationWithOptions(t.Context(), &patEdgeCaller{}, io.Discard, LoginRecommendOptions{
|
|
InitialPlan: &LoginRecommendPlan{Result: patText(`{`), Scopes: []string{"scope"}},
|
|
ProductSelector: func([]LoginRecommendProduct) ([]string, error) {
|
|
return []string{"mail"}, nil
|
|
},
|
|
}); err == nil {
|
|
t.Fatal("invalid initial plan products accepted")
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageLegacySchemaMismatchWithoutValidationKeyword(t *testing.T) {
|
|
canonical := map[string]any{"scopes": []string{"a"}}
|
|
legacy := map[string]any{"scope": "a"}
|
|
if isLegacyGrantSchemaMismatchError(errors.New("scope problem"), canonical, legacy) {
|
|
t.Fatal("ambiguous scope error triggered legacy fallback")
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageChmodValidateRequiresTargetSelection(t *testing.T) {
|
|
// The declared Validate hook mirrors the cobra Args gate for callers that
|
|
// bypass Args (the contract RunE pipeline runs Validate first): with no
|
|
// positional scope, no product/domain expansion, and no --recommend, the
|
|
// command must fail closed before any tool call.
|
|
cmd := newChmodCommand(&patEdgeCaller{})
|
|
err := cmd.RunE(cmd, nil)
|
|
if err == nil || !strings.Contains(err.Error(), "accepts 1 arg(s), received 0") {
|
|
t.Fatalf("chmod validate without targets err = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageChmodValidateGrantTypeAndSessionRules(t *testing.T) {
|
|
// Pure string validation in the declared Validate hook: platform-neutral,
|
|
// fails closed before any tool call on both Windows and macOS.
|
|
cmd := newChmodCommand(&patEdgeCaller{})
|
|
if err := cmd.Flags().Set("grant-type", "forever"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err := cmd.RunE(cmd, []string{"mail.mail:read"})
|
|
if err == nil || !strings.Contains(err.Error(), `invalid --grant-type "forever"`) {
|
|
t.Fatalf("chmod invalid grant-type err = %v", err)
|
|
}
|
|
|
|
// session grants need an explicit --session-id when no session env is set.
|
|
t.Setenv("DINGTALK_SESSION_ID", "")
|
|
t.Setenv("DWS_SESSION_ID", "")
|
|
t.Setenv("REWIND_SESSION_ID", "")
|
|
cmd = newChmodCommand(&patEdgeCaller{})
|
|
if err := cmd.Flags().Set("grant-type", "session"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = cmd.RunE(cmd, []string{"mail.mail:read"})
|
|
if err == nil || !strings.Contains(err.Error(), "--session-id is required when --grant-type is session") {
|
|
t.Fatalf("chmod session without session-id err = %v", err)
|
|
}
|
|
}
|