- add 45 public document shortcuts and 2 reviewed expert-only paths - preserve six historical command and Schema identities alongside canonical leaves - add safe local download primitives and document access/share orchestration - keep comment create/reply confirmation backward-compatible - ensure grant-and-share upgrades insufficient roles before messaging - return non-zero partial/failure message ledgers and structured partial-write recovery metadata - enumerate every selection candidate and use rune-safe Unicode keyword contexts - assert zero-call confirmation boundaries for destructive shortcuts Validation: - full Go test suite and repository policy - real DingTalk E2E for 34 canonical shortcuts, all 8 compatibility-affected entries, READER-to-EDITOR grant-and-share upgrade, and same-block selection ambiguity with zero comment writes - command compatibility across 1,221 historical nodes and complete Schema compatibility - 1,152 Agent examples including 62 real Cobra dry-runs - 100% changed-code coverage across 1,260 executable statements
397 lines
21 KiB
Go
397 lines
21 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// Licensed under the Apache License, Version 2.0
|
|
|
|
package smart
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd"
|
|
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/shortcut"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/testseam"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func docAccessCoverageResponses(permission string) map[string][]string {
|
|
return map[string][]string{
|
|
"contact/search_contact_by_key_word": {`{"result":[{"userId":"u1","name":"张三","openDingTalkId":"open1"}]}`},
|
|
"doc/list_permission": {permission},
|
|
"doc/add_permission": {`{"result":{"ok":true}}`},
|
|
"doc/update_permission": {`{"result":{"ok":true}}`},
|
|
"doc/remove_permission": {`{"result":{"ok":true}}`},
|
|
"chat/send_personal_message": {`{"result":{"messageId":"m1"}}`},
|
|
}
|
|
}
|
|
|
|
type docAccessErrorWriter struct{}
|
|
|
|
func (docAccessErrorWriter) Write([]byte) (int, error) { return 0, errors.New("output failure") }
|
|
|
|
func runDocAccessCoverage(t *testing.T, caller *smartCoverageCaller, args ...string) error {
|
|
t.Helper()
|
|
helpers.InitDeps(caller)
|
|
root := newPlatformCoverageRoot()
|
|
root.SetIn(bytes.NewReader(nil))
|
|
root.SetArgs(args)
|
|
return root.Execute()
|
|
}
|
|
|
|
func runDocAccessCoverageOutput(t *testing.T, caller *smartCoverageCaller, args ...string) (map[string]any, error) {
|
|
t.Helper()
|
|
helpers.InitDeps(caller)
|
|
root := newPlatformCoverageRoot()
|
|
var stdout bytes.Buffer
|
|
root.SetOut(&stdout)
|
|
root.SetErr(io.Discard)
|
|
root.SetIn(bytes.NewReader(nil))
|
|
root.SetArgs(args)
|
|
err := root.Execute()
|
|
if stdout.Len() == 0 {
|
|
return nil, err
|
|
}
|
|
var payload map[string]any
|
|
if err := json.Unmarshal(stdout.Bytes(), &payload); err != nil {
|
|
t.Fatalf("decode output %q: %v", stdout.String(), err)
|
|
}
|
|
return payload, err
|
|
}
|
|
|
|
func runDocAccessDeclaration(t *testing.T, declaration shortcut.Shortcut, caller *smartCoverageCaller, args ...string) error {
|
|
t.Helper()
|
|
helpers.InitDeps(caller)
|
|
root := &cobra.Command{Use: "dws", SilenceErrors: true, SilenceUsage: true}
|
|
root.SetOut(io.Discard)
|
|
root.SetErr(io.Discard)
|
|
root.PersistentFlags().Bool("yes", false, "")
|
|
root.PersistentFlags().Bool("dry-run", false, "")
|
|
root.PersistentFlags().String("format", "json", "")
|
|
service := &cobra.Command{Use: "doc"}
|
|
service.AddCommand(corecmd.New(shortcut.FromShortcut(declaration)))
|
|
root.AddCommand(service)
|
|
root.SetArgs(append([]string{"doc", declaration.Command}, args...))
|
|
return root.Execute()
|
|
}
|
|
|
|
func TestCrossPlatformCoverageDocAccessSuccessDryRunAndPreflight(t *testing.T) {
|
|
if docSmartContract("+test", "d", "i", nil, true).DryRun == nil {
|
|
t.Fatal("explicit doc smart dry-run declaration missing")
|
|
}
|
|
present := `{"result":[{"userId":"u1","roleId":"READER"}]}`
|
|
empty := `{"result":[]}`
|
|
cases := []struct {
|
|
name string
|
|
permission string
|
|
args []string
|
|
wantErr bool
|
|
}{
|
|
{"grant", present, []string{"doc", "+access-grant", "--node", "n", "--to", "张三", "--role", "READER", "--workspace", "w", "--yes"}, false},
|
|
{"grant dry", present, []string{"doc", "+access-grant", "--node", "n", "--to", "张三", "--dry-run", "--yes"}, false},
|
|
{"change", present, []string{"doc", "+access-change", "--node", "n", "--to", "张三", "--role", "EDITOR", "--yes"}, false},
|
|
{"change dry", present, []string{"doc", "+access-change", "--node", "n", "--to", "张三", "--dry-run", "--yes"}, false},
|
|
{"change missing", empty, []string{"doc", "+access-change", "--node", "n", "--to", "张三", "--yes"}, true},
|
|
{"revoke", present, []string{"doc", "+access-revoke", "--node", "n", "--to", "张三", "--workspace", "w", "--yes"}, false},
|
|
{"revoke dry", present, []string{"doc", "+access-revoke", "--node", "n", "--to", "张三", "--dry-run", "--yes"}, false},
|
|
{"revoke missing", empty, []string{"doc", "+access-revoke", "--node", "n", "--to", "张三", "--yes"}, true},
|
|
{"share", present, []string{"doc", "+share", "--to", "张三", "--url", "https://example.com/doc", "--note", "看一下", "--yes"}, false},
|
|
{"share dry", present, []string{"doc", "+share", "--to", "张三", "--url", "https://example.com/doc", "--dry-run", "--yes"}, false},
|
|
{"grant share existing", present, []string{"doc", "+grant-and-share", "--node", "n", "--to", "张三", "--url", "https://example.com/doc", "--yes"}, false},
|
|
{"grant share missing", empty, []string{"doc", "+grant-and-share", "--node", "n", "--to", "张三", "--url", "https://example.com/doc", "--yes"}, false},
|
|
{"grant share dry", empty, []string{"doc", "+grant-and-share", "--node", "n", "--to", "张三", "--url", "https://example.com/doc", "--dry-run", "--yes"}, false},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
caller := &smartCoverageCaller{responses: docAccessCoverageResponses(tc.permission), failAt: map[string]int{}}
|
|
err := runDocAccessCoverage(t, caller, tc.args...)
|
|
if (err != nil) != tc.wantErr {
|
|
t.Fatalf("error = %v, wantErr %v", err, tc.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageDocGrantAndShareProjectionMatchesWrite(t *testing.T) {
|
|
permission := func(payload map[string]any) string {
|
|
t.Helper()
|
|
data, _ := payload["data"].(map[string]any)
|
|
recipients, _ := data["recipients"].([]any)
|
|
if len(recipients) != 1 {
|
|
t.Fatalf("recipients = %#v", data["recipients"])
|
|
}
|
|
row, _ := recipients[0].(map[string]any)
|
|
value, _ := row["permission"].(string)
|
|
return value
|
|
}
|
|
|
|
missingCaller := &smartCoverageCaller{responses: docAccessCoverageResponses(`{"result":[]}`), failAt: map[string]int{}}
|
|
missing, err := runDocAccessCoverageOutput(t, missingCaller, "doc", "+grant-and-share", "--node", "n", "--to", "张三", "--url", "https://example.com/doc", "--yes")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if missing["operation"] != "doc.grant_and_share" || permission(missing) != "granted" {
|
|
t.Fatalf("missing projection = %#v", missing)
|
|
}
|
|
if missingCaller.counts["doc/add_permission"] != 1 {
|
|
t.Fatalf("missing add calls = %d", missingCaller.counts["doc/add_permission"])
|
|
}
|
|
|
|
presentCaller := &smartCoverageCaller{responses: docAccessCoverageResponses(`{"result":[{"id":"u1","roleId":"READER"}]}`), failAt: map[string]int{}}
|
|
present, err := runDocAccessCoverageOutput(t, presentCaller, "doc", "+grant-and-share", "--node", "n", "--to", "张三", "--url", "https://example.com/doc", "--yes")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if present["operation"] != "doc.grant_and_share" || permission(present) != "unchanged" {
|
|
t.Fatalf("present projection = %#v", present)
|
|
}
|
|
if presentCaller.counts["doc/add_permission"] != 0 || presentCaller.counts["doc/update_permission"] != 0 {
|
|
t.Fatalf("present writes = %#v", presentCaller.counts)
|
|
}
|
|
|
|
upgradeCaller := &smartCoverageCaller{responses: docAccessCoverageResponses(`{"result":[{"userId":"u1","roleId":"READER"}]}`), failAt: map[string]int{}}
|
|
upgraded, err := runDocAccessCoverageOutput(t, upgradeCaller, "doc", "+grant-and-share", "--node", "n", "--to", "张三", "--url", "https://example.com/doc", "--role", "EDITOR", "--yes")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if permission(upgraded) != "upgraded" || upgradeCaller.counts["doc/add_permission"] != 0 || upgradeCaller.counts["doc/update_permission"] != 1 || upgradeCaller.counts["chat/send_personal_message"] != 1 {
|
|
t.Fatalf("upgrade projection=%#v calls=%#v", upgraded, upgradeCaller.counts)
|
|
}
|
|
wantUpgrade := map[string]any{"nodeId": "n", "roleId": "EDITOR", "userIds": []string{"u1"}}
|
|
if got := upgradeCaller.arguments["doc/update_permission"]; len(got) != 1 || !reflect.DeepEqual(got[0], wantUpgrade) {
|
|
t.Fatalf("upgrade params = %#v, want %#v", got, wantUpgrade)
|
|
}
|
|
|
|
unknownCaller := &smartCoverageCaller{responses: docAccessCoverageResponses(`{"result":[{"userId":"u1"}]}`), failAt: map[string]int{}}
|
|
if _, err := runDocAccessCoverageOutput(t, unknownCaller, "doc", "+grant-and-share", "--node", "n", "--to", "张三", "--url", "https://example.com/doc", "--yes"); err == nil {
|
|
t.Fatal("unknown existing role must stop before permission writes and messaging")
|
|
}
|
|
if unknownCaller.counts["doc/add_permission"] != 0 || unknownCaller.counts["doc/update_permission"] != 0 || unknownCaller.counts["chat/send_personal_message"] != 0 {
|
|
t.Fatalf("unknown role continued: %#v", unknownCaller.counts)
|
|
}
|
|
|
|
shareCaller := &smartCoverageCaller{responses: docAccessCoverageResponses(`{"result":[]}`), failAt: map[string]int{}}
|
|
shared, err := runDocAccessCoverageOutput(t, shareCaller, "doc", "+share", "--to", "张三", "--url", "https://example.com/doc", "--yes")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if shared["operation"] != "doc.share" || permission(shared) != "unchanged" {
|
|
t.Fatalf("share projection = %#v", shared)
|
|
}
|
|
|
|
if missing := usersMissingPermission(map[string]any{"result": []any{map[string]any{"userId": "u10"}}}, []contactUser{{name: "张三", userID: "u1"}}); len(missing) != 1 {
|
|
t.Fatalf("substring permission match must not pass: %#v", missing)
|
|
}
|
|
|
|
roles := map[string]int{"READER": 1, "DOWNLOADER": 2, "EDITOR": 3, "MANAGER": 4, "OWNER": 5, "unknown": 0}
|
|
for role, want := range roles {
|
|
if got := permissionRoleRank(role); got != want {
|
|
t.Fatalf("permissionRoleRank(%q) = %d, want %d", role, got, want)
|
|
}
|
|
}
|
|
collected := map[string]string{}
|
|
collectPermissionRoles(map[string]any{"result": []any{
|
|
map[string]any{"userID": "u2", "permissionRole": "READER"},
|
|
map[string]any{"memberId": "u2", "permissionType": "EDITOR"},
|
|
map[string]any{"targetId": "u3", "roleType": "DOWNLOADER"},
|
|
map[string]any{"uid": "u4", "roleID": "MANAGER"},
|
|
map[string]any{"id": "u5", "role": "OWNER"},
|
|
}}, collected)
|
|
if !reflect.DeepEqual(collected, map[string]string{"u2": "EDITOR", "u3": "DOWNLOADER", "u4": "MANAGER", "u5": "OWNER"}) {
|
|
t.Fatalf("collected roles = %#v", collected)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageDocAccessRevokeConfirmationBoundary(t *testing.T) {
|
|
present := `{"result":[{"userId":"u1","roleId":"READER"}]}`
|
|
unconfirmed := &smartCoverageCaller{responses: docAccessCoverageResponses(present), failAt: map[string]int{}}
|
|
if err := runDocAccessCoverage(t, unconfirmed, "doc", "+access-revoke", "--node", "n", "--to", "张三"); err == nil {
|
|
t.Fatal("access revoke without --yes must reject")
|
|
}
|
|
if len(unconfirmed.counts) != 0 {
|
|
t.Fatalf("unconfirmed access revoke called MCP: %#v", unconfirmed.counts)
|
|
}
|
|
|
|
confirmed := &smartCoverageCaller{responses: docAccessCoverageResponses(present), failAt: map[string]int{}}
|
|
if err := runDocAccessCoverage(t, confirmed, "doc", "+access-revoke", "--node", "n", "--to", "张三", "--yes"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
wantCounts := map[string]int{
|
|
"contact/search_contact_by_key_word": 1,
|
|
"doc/list_permission": 1,
|
|
"doc/remove_permission": 1,
|
|
}
|
|
if !reflect.DeepEqual(confirmed.counts, wantCounts) {
|
|
t.Fatalf("confirmed calls = %#v, want %#v", confirmed.counts, wantCounts)
|
|
}
|
|
wantRemove := map[string]any{"nodeId": "n", "userIds": []string{"u1"}}
|
|
if got := confirmed.arguments["doc/remove_permission"]; len(got) != 1 || !reflect.DeepEqual(got[0], wantRemove) {
|
|
t.Fatalf("remove params = %#v, want %#v", got, wantRemove)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageDocShareKeepsLegacyStringFlagAndCSV(t *testing.T) {
|
|
testseam.Swap(t, &resolveDocShareUser, func(_ *shortcut.RuntimeContext, name string) (contactUser, error) {
|
|
return contactUser{name: name, userID: "user-" + name, openDingTalkID: "open-" + name}, nil
|
|
})
|
|
responses := docAccessCoverageResponses(`{"result":[]}`)
|
|
responses["chat/send_personal_message"] = []string{
|
|
`{"result":{"messageId":"m1"}}`,
|
|
`{"result":{"messageId":"m2"}}`,
|
|
}
|
|
caller := &smartCoverageCaller{responses: responses, failAt: map[string]int{}}
|
|
payload, err := runDocAccessCoverageOutput(t, caller, "doc", "+share-doc", "--to", "alice,bob", "--url", "https://example.com/doc", "--yes")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
data, _ := payload["data"].(map[string]any)
|
|
recipients, _ := data["recipients"].([]any)
|
|
if len(recipients) != 2 || caller.counts["chat/send_personal_message"] != 2 {
|
|
t.Fatalf("recipients=%#v send calls=%d", recipients, caller.counts["chat/send_personal_message"])
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageDocShareFailureExitContracts(t *testing.T) {
|
|
resolve := func(_ *shortcut.RuntimeContext, name string) (contactUser, error) {
|
|
return contactUser{name: name, userID: "user-" + name, openDingTalkID: "open-" + name}, nil
|
|
}
|
|
testseam.Swap(t, &resolveDocShareUser, resolve)
|
|
testseam.Swap(t, &resolveDocPermissionUser, resolve)
|
|
|
|
assertFailure := func(t *testing.T, payload map[string]any, err error, wantStatus string, wantSucceeded, wantFailed int) {
|
|
t.Helper()
|
|
if err == nil {
|
|
t.Fatal("message failure must return a non-zero exit error")
|
|
}
|
|
var typed *apperrors.Error
|
|
if !errors.As(err, &typed) || typed.Reason != "doc_share_message_failed" || typed.ExitCode() != 1 {
|
|
t.Fatalf("message error = %#v", err)
|
|
}
|
|
if payload["ok"] != false || payload["status"] != wantStatus {
|
|
t.Fatalf("failure payload = %#v", payload)
|
|
}
|
|
data, _ := payload["data"].(map[string]any)
|
|
if data["succeededCount"] != float64(wantSucceeded) || data["failedCount"] != float64(wantFailed) {
|
|
t.Fatalf("failure counts = %#v", data)
|
|
}
|
|
}
|
|
|
|
allShareCaller := &smartCoverageCaller{responses: docAccessCoverageResponses(`{"result":[]}`), failAt: map[string]int{"chat/send_personal_message": -1}}
|
|
allShare, err := runDocAccessCoverageOutput(t, allShareCaller, "doc", "+share", "--to", "alice", "--url", "https://example.com/doc", "--yes")
|
|
assertFailure(t, allShare, err, "failed", 0, 1)
|
|
|
|
permissions := `{"result":[{"userId":"user-alice","roleId":"READER"},{"userId":"user-bob","roleId":"READER"}]}`
|
|
allGrantCaller := &smartCoverageCaller{responses: docAccessCoverageResponses(permissions), failAt: map[string]int{"chat/send_personal_message": -1}}
|
|
allGrant, err := runDocAccessCoverageOutput(t, allGrantCaller, "doc", "+grant-and-share", "--node", "n", "--to", "alice,bob", "--url", "https://example.com/doc", "--yes")
|
|
assertFailure(t, allGrant, err, "failed", 0, 2)
|
|
|
|
partialCaller := &smartCoverageCaller{responses: docAccessCoverageResponses(permissions), failAt: map[string]int{"chat/send_personal_message": 2}}
|
|
partial, err := runDocAccessCoverageOutput(t, partialCaller, "doc", "+grant-and-share", "--node", "n", "--to", "alice,bob", "--url", "https://example.com/doc", "--yes")
|
|
assertFailure(t, partial, err, "partial_success", 1, 1)
|
|
|
|
helpers.InitDeps(&smartCoverageCaller{responses: docAccessCoverageResponses(`{"result":[]}`), failAt: map[string]int{}})
|
|
root := newPlatformCoverageRoot()
|
|
root.SetOut(docAccessErrorWriter{})
|
|
root.SetErr(io.Discard)
|
|
root.SetIn(bytes.NewReader(nil))
|
|
root.SetArgs([]string{"doc", "+share", "--to", "alice", "--url", "https://example.com/doc", "--yes"})
|
|
if err := root.Execute(); err == nil || err.Error() != "output failure" {
|
|
t.Fatalf("output failure = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageDocGrantAndSharePartialPermissionFailure(t *testing.T) {
|
|
resolve := func(_ *shortcut.RuntimeContext, name string) (contactUser, error) {
|
|
return contactUser{name: name, userID: "user-" + name, openDingTalkID: "open-" + name}, nil
|
|
}
|
|
testseam.Swap(t, &resolveDocPermissionUser, resolve)
|
|
|
|
responses := docAccessCoverageResponses(`{"result":[{"userId":"user-bob","roleId":"READER"}]}`)
|
|
caller := &smartCoverageCaller{responses: responses, failAt: map[string]int{"doc/update_permission": 1}}
|
|
err := runDocAccessCoverage(t, caller, "doc", "+grant-and-share", "--node", "n", "--to", "alice,bob", "--url", "https://example.com/doc", "--role", "EDITOR", "--yes")
|
|
if err == nil {
|
|
t.Fatal("partial permission write unexpectedly succeeded")
|
|
}
|
|
var typed *apperrors.Error
|
|
if !errors.As(err, &typed) || typed.Reason != "doc_grant_permission_partial_failure" || typed.FailureStage != "update_permission" || typed.ExecutionStarted == nil || !*typed.ExecutionStarted || !typed.RetryableSet || typed.Retryable {
|
|
t.Fatalf("partial permission error = %#v", err)
|
|
}
|
|
if typed.Details["status"] != "partial_success" {
|
|
t.Fatalf("partial permission details = %#v", typed.Details)
|
|
}
|
|
steps, _ := typed.Details["steps"].([]map[string]any)
|
|
if len(steps) != 3 || steps[0]["status"] != "success" || steps[1]["status"] != "failed" || steps[2]["status"] != "not_started" {
|
|
t.Fatalf("partial permission steps = %#v", steps)
|
|
}
|
|
if caller.counts["doc/add_permission"] != 1 || caller.counts["doc/update_permission"] != 1 || caller.counts["chat/send_personal_message"] != 0 {
|
|
t.Fatalf("partial permission calls = %#v", caller.counts)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageDocAccessFailureBoundaries(t *testing.T) {
|
|
present := `{"result":[{"userId":"u1","roleId":"READER"}]}`
|
|
commands := []struct {
|
|
args []string
|
|
fails []string
|
|
}{
|
|
{[]string{"doc", "+access-grant", "--node", "n", "--to", "张三", "--yes"}, []string{"contact/search_contact_by_key_word", "doc/add_permission"}},
|
|
{[]string{"doc", "+access-change", "--node", "n", "--to", "张三", "--yes"}, []string{"contact/search_contact_by_key_word", "doc/list_permission", "doc/update_permission"}},
|
|
{[]string{"doc", "+access-revoke", "--node", "n", "--to", "张三", "--yes"}, []string{"contact/search_contact_by_key_word", "doc/list_permission", "doc/remove_permission"}},
|
|
{[]string{"doc", "+share", "--to", "张三", "--url", "https://example.com/doc", "--yes"}, []string{"contact/search_contact_by_key_word", "chat/send_personal_message"}},
|
|
{[]string{"doc", "+grant-and-share", "--node", "n", "--to", "张三", "--url", "https://example.com/doc", "--yes"}, []string{"contact/search_contact_by_key_word", "doc/list_permission", "chat/send_personal_message"}},
|
|
}
|
|
for _, command := range commands {
|
|
for _, fail := range command.fails {
|
|
responses := docAccessCoverageResponses(present)
|
|
if fail == "doc/add_permission" {
|
|
responses["doc/list_permission"] = []string{`{"result":[]}`}
|
|
}
|
|
caller := &smartCoverageCaller{responses: responses, failAt: map[string]int{fail: 1}}
|
|
_ = runDocAccessCoverage(t, caller, command.args...)
|
|
}
|
|
}
|
|
|
|
external := &smartCoverageCaller{responses: docAccessCoverageResponses(present), failAt: map[string]int{}}
|
|
external.responses["contact/search_contact_by_key_word"] = []string{`{"result":[{"name":"外部","openDingTalkId":"open-ext"}]}`}
|
|
_ = runDocAccessCoverage(t, external, "doc", "+access-grant", "--node", "n", "--to", "外部", "--yes")
|
|
noOpenID := &smartCoverageCaller{responses: docAccessCoverageResponses(present), failAt: map[string]int{}}
|
|
noOpenID.responses["contact/search_contact_by_key_word"] = []string{`{"result":[{"name":"张三","userId":"u1"}]}`}
|
|
_ = runDocAccessCoverage(t, noOpenID, "doc", "+grant-and-share", "--node", "n", "--to", "张三", "--url", "https://example.com/doc", "--yes")
|
|
emptyName := &smartCoverageCaller{responses: docAccessCoverageResponses(present), failAt: map[string]int{}}
|
|
_ = runDocAccessCoverage(t, emptyName, "doc", "+access-grant", "--node", "n", "--to", " ", "--yes")
|
|
duplicate := &smartCoverageCaller{responses: docAccessCoverageResponses(present), failAt: map[string]int{}}
|
|
_ = runDocAccessCoverage(t, duplicate, "doc", "+access-grant", "--node", "n", "--to", "张三,张三", "--yes")
|
|
_ = usersMissingPermission(map[string]any{}, []contactUser{{name: "empty"}})
|
|
|
|
optionalTo := AccessGrant
|
|
optionalTo.Flags = append([]shortcut.Flag(nil), AccessGrant.Flags...)
|
|
optionalTo.Flags[1].Required = false
|
|
_ = runDocAccessDeclaration(t, optionalTo, &smartCoverageCaller{responses: docAccessCoverageResponses(present), failAt: map[string]int{}}, "--node", "n", "--to", " ", "--yes")
|
|
_ = runDocAccessDeclaration(t, optionalTo, &smartCoverageCaller{responses: docAccessCoverageResponses(present), failAt: map[string]int{}}, "--node", "n", "--yes")
|
|
|
|
t.Run("permission resolver defensive identity check", func(t *testing.T) {
|
|
testseam.Swap(t, &resolveDocPermissionUser, func(*shortcut.RuntimeContext, string) (contactUser, error) {
|
|
return contactUser{name: "external", openDingTalkID: "open"}, nil
|
|
})
|
|
_ = runDocAccessCoverage(t, &smartCoverageCaller{responses: docAccessCoverageResponses(present), failAt: map[string]int{}}, "doc", "+access-grant", "--node", "n", "--to", "external", "--yes")
|
|
})
|
|
t.Run("permission resolver error", func(t *testing.T) {
|
|
testseam.Swap(t, &resolveDocPermissionUser, func(*shortcut.RuntimeContext, string) (contactUser, error) {
|
|
return contactUser{}, errors.New("resolve")
|
|
})
|
|
_ = runDocAccessCoverage(t, &smartCoverageCaller{responses: docAccessCoverageResponses(present), failAt: map[string]int{}}, "doc", "+access-grant", "--node", "n", "--to", "x", "--yes")
|
|
})
|
|
|
|
grantShareAddFailure := &smartCoverageCaller{responses: docAccessCoverageResponses(`{"result":[]}`), failAt: map[string]int{"doc/add_permission": 1}}
|
|
_ = runDocAccessCoverage(t, grantShareAddFailure, "doc", "+grant-and-share", "--node", "n", "--to", "张三", "--url", "https://example.com/doc", "--yes")
|
|
grantShareUpdateFailure := &smartCoverageCaller{responses: docAccessCoverageResponses(`{"result":[{"userId":"u1","roleId":"READER"}]}`), failAt: map[string]int{"doc/update_permission": 1}}
|
|
_ = runDocAccessCoverage(t, grantShareUpdateFailure, "doc", "+grant-and-share", "--node", "n", "--to", "张三", "--url", "https://example.com/doc", "--role", "EDITOR", "--yes")
|
|
if grantShareUpdateFailure.counts["chat/send_personal_message"] != 0 {
|
|
t.Fatalf("message sent after permission upgrade failure: %#v", grantShareUpdateFailure.counts)
|
|
}
|
|
}
|