From a519a2ff8a30b8057b8eacbc4754ed085f87d232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BF=AE=E9=9B=A8?= Date: Thu, 16 Jul 2026 12:38:05 +0800 Subject: [PATCH] fix(contact): validate enterprise onboarding writes --- internal/helpers/contact.go | 50 +++++++++++++---- internal/helpers/contact_enterprise_test.go | 61 +++++++++++++++++++++ 2 files changed, 100 insertions(+), 11 deletions(-) diff --git a/internal/helpers/contact.go b/internal/helpers/contact.go index 8e7d86ce..6a5035f4 100644 --- a/internal/helpers/contact.go +++ b/internal/helpers/contact.go @@ -680,9 +680,14 @@ contact user profile fields 获取可用字段列表。 if err := validateRequiredFlags(cmd, "org-name", "creator-username"); err != nil { return err } + orgName := strings.TrimSpace(mustGetFlag(cmd, "org-name")) + creatorUsername := strings.TrimSpace(mustGetFlag(cmd, "creator-username")) + if orgName == "" || creatorUsername == "" { + return fmt.Errorf("--org-name 和 --creator-username 不能为空") + } return callMCPTool("org_create", map[string]any{ - "orgName": mustGetFlag(cmd, "org-name"), - "creatorUsername": mustGetFlag(cmd, "creator-username"), + "orgName": orgName, + "creatorUsername": creatorUsername, }) }, } @@ -717,13 +722,18 @@ contact user profile fields 获取可用字段列表。 if err := validateRequiredFlags(cmd, "org-user-name", "login-id"); err != nil { return err } + orgUserName := strings.TrimSpace(mustGetFlag(cmd, "org-user-name")) + loginID := strings.TrimSpace(mustGetFlag(cmd, "login-id")) + if orgUserName == "" || loginID == "" { + return fmt.Errorf("--org-user-name 和 --login-id 不能为空") + } toolArgs := map[string]any{ - "orgUserName": strings.TrimSpace(mustGetFlag(cmd, "org-user-name")), - "loginId": strings.TrimSpace(mustGetFlag(cmd, "login-id")), - "sendPwdViaSms": cmd.Flags().Changed("send-pwd-via-sms") && func() bool { - v, _ := cmd.Flags().GetBool("send-pwd-via-sms") - return v - }(), + "orgUserName": orgUserName, + "loginId": loginID, + } + if cmd.Flags().Changed("send-pwd-via-sms") { + sendPwdViaSMS, _ := cmd.Flags().GetBool("send-pwd-via-sms") + toolArgs["sendPwdViaSms"] = sendPwdViaSMS } if mobile := strings.TrimSpace(mustGetFlag(cmd, "org-user-mobile")); mobile != "" { toolArgs["orgUserMobile"] = mobile @@ -732,10 +742,11 @@ contact user profile fields 获取可用字段列表。 toolArgs["email"] = email } if cmd.Flags().Changed("dept-ids") { - ids := parseCSVInts(mustGetFlag(cmd, "dept-ids")) - if len(ids) > 0 { - toolArgs["deptIds"] = ids + ids, err := parseCSVIntsStrict(mustGetFlag(cmd, "dept-ids")) + if err != nil { + return fmt.Errorf("--dept-ids 解析失败: %w", err) } + toolArgs["deptIds"] = ids } return callMCPTool("exclusive_account_create", toolArgs) }, @@ -983,3 +994,20 @@ func parseCSVInts(s string) []int64 { } return result } + +func parseCSVIntsStrict(s string) ([]int64, error) { + parts := strings.Split(s, ",") + result := make([]int64, 0, len(parts)) + for i, p := range parts { + trimmed := strings.TrimSpace(p) + if trimmed == "" { + return nil, fmt.Errorf("第 %d 项为空", i+1) + } + n, err := strconv.ParseInt(trimmed, 10, 64) + if err != nil { + return nil, fmt.Errorf("第 %d 项 %q 不是整数: %w", i+1, trimmed, err) + } + result = append(result, n) + } + return result, nil +} diff --git a/internal/helpers/contact_enterprise_test.go b/internal/helpers/contact_enterprise_test.go index 635bcdb6..21028941 100644 --- a/internal/helpers/contact_enterprise_test.go +++ b/internal/helpers/contact_enterprise_test.go @@ -123,6 +123,25 @@ func TestCrossPlatformCoverageContactEnterpriseCommandsMapMCPArguments(t *testin "sendPwdViaSms": true, }, }, + { + name: "create enterprise account without optional send flag", + args: []string{"account", "create", "--org-user-name", "王五", "--login-id", "wangwu001"}, + toolName: "exclusive_account_create", + wantArgs: map[string]any{ + "orgUserName": "王五", + "loginId": "wangwu001", + }, + }, + { + name: "create enterprise account with explicit false send flag", + args: []string{"account", "create", "--org-user-name", "王五", "--login-id", "wangwu001", "--send-pwd-via-sms=false"}, + toolName: "exclusive_account_create", + wantArgs: map[string]any{ + "orgUserName": "王五", + "loginId": "wangwu001", + "sendPwdViaSms": false, + }, + }, } for _, tt := range tests { @@ -160,6 +179,48 @@ func TestCrossPlatformCoverageContactUserInviteRejectsInvalidDepartmentsJSON(t * } } +func TestCrossPlatformCoverageContactAccountCreateRejectsInvalidDepartmentIDs(t *testing.T) { + for _, deptIDs := range []string{"1,not-an-id,3", "1,,3", " "} { + t.Run(deptIDs, func(t *testing.T) { + caller, err := runContactEnterpriseCommand(t, + "account", "create", + "--org-user-name", "张三", + "--login-id", "zhangsan001", + "--dept-ids", deptIDs, + ) + if err == nil || !strings.Contains(err.Error(), "--dept-ids 解析失败") { + t.Fatalf("error = %v, want department ID validation", err) + } + if len(caller.calls) != 0 { + t.Fatalf("invalid input made %d remote call(s)", len(caller.calls)) + } + }) + } +} + +func TestCrossPlatformCoverageContactOrgAndAccountCreateRejectWhitespaceIdentityFlags(t *testing.T) { + tests := []struct { + name string + args []string + }{ + {"organization name", []string{"org", "create", "--org-name", " ", "--creator-username", "张三"}}, + {"organization creator", []string{"org", "create", "--org-name", "测试企业", "--creator-username", " "}}, + {"account user name", []string{"account", "create", "--org-user-name", " ", "--login-id", "zhangsan001"}}, + {"account login ID", []string{"account", "create", "--org-user-name", "张三", "--login-id", " "}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + caller, err := runContactEnterpriseCommand(t, tt.args...) + if err == nil || !strings.Contains(err.Error(), "不能为空") { + t.Fatalf("error = %v, want non-blank identity validation", err) + } + if len(caller.calls) != 0 { + t.Fatalf("blank input made %d remote call(s)", len(caller.calls)) + } + }) + } +} + func TestCrossPlatformCoverageContactEnterpriseCommandsRequireCoreIdentityFlags(t *testing.T) { tests := [][]string{ {"org", "create", "--org-name", "测试企业"},