Merge branch 'main' into feat/t07-chat-response-envelope

This commit is contained in:
Anonymity-0
2026-08-11 20:22:20 +08:00
committed by GitHub
7 changed files with 341 additions and 66 deletions
+56
View File
@@ -25,11 +25,32 @@ func TestRuntimeSchemaCompletenessCoversPublicCommandTree(t *testing.T) {
if !containsSchemaPath(report.Covered, "chat category create-smart") {
t.Fatal("chat category create-smart is not covered by runtime Schema")
}
for _, path := range missingChatCatalogCoveragePaths() {
if !containsSchemaPath(report.Covered, path) {
t.Fatalf("%s is not covered by runtime Schema", path)
}
}
if !containsSchemaPath(report.Excluded, "agoal strategy list") {
t.Fatal("agoal strategy list is not recorded as a reviewed exclusion")
}
}
func TestRuntimeSchemaCompletenessDoesNotExcludeMissingChatCatalogPaths(t *testing.T) {
exclusions, err := cli.ReviewedRuntimeSchemaExclusions()
if err != nil {
t.Fatal(err)
}
excluded := map[string]bool{}
for _, exclusion := range exclusions {
excluded[exclusion.CLIPath] = true
}
for _, path := range missingChatCatalogCoveragePaths() {
if excluded[path] {
t.Fatalf("%s must not remain in runtime Schema exclusions", path)
}
}
}
func containsSchemaPath(paths []string, want string) bool {
for _, path := range paths {
if path == want {
@@ -38,3 +59,38 @@ func containsSchemaPath(paths []string, want string) bool {
}
return false
}
func missingChatCatalogCoveragePaths() []string {
return []string{
"chat category add-conv",
"chat category create",
"chat category delete",
"chat category remove-conv",
"chat category rename",
"chat chmod",
"chat clear-all-red-point",
"chat clear-messages",
"chat clear-red-point",
"chat data-auth cross-org",
"chat group audit-join-validation",
"chat group list-all",
"chat group list-join-validations",
"chat group members list-by-ids",
"chat group notice create",
"chat group notice edit",
"chat group notice get",
"chat group notice list",
"chat group share-invite",
"chat group update-alias",
"chat hide",
"chat list-all-conversations",
"chat mark-read",
"chat mark-unread",
"chat message list-emotion-replies",
"chat message set-top-msg",
"chat message unset-top-msg",
"chat mute-at-all",
"chat mute-red-envelope",
"chat text translate",
}
}
+132 -1
View File
@@ -114,12 +114,14 @@ func TestDeliveryShortcutProgressiveQueriesReturnCompleteContracts(t *testing.T)
product := executeShortcutSchemaQuery(t, "chat")
productPayload, _ := product["product"].(map[string]any)
if got, want := int(product["count"].(float64)), 187; got != want {
if got, want := int(product["count"].(float64)), 217; got != want {
t.Fatalf("schema chat count = %d, want %d", got, want)
}
summaries := schemaContractObjectSlice(productPayload["tools"])
shortcutCount := 0
summaryByCLIPath := make(map[string]map[string]any, len(summaries))
for _, summary := range summaries {
summaryByCLIPath[schemaContractString(summary["cli_path"])] = summary
if strings.HasPrefix(schemaContractString(summary["canonical_path"]), "chat.shortcut_") {
shortcutCount++
}
@@ -127,6 +129,135 @@ func TestDeliveryShortcutProgressiveQueriesReturnCompleteContracts(t *testing.T)
if shortcutCount != 98 {
t.Fatalf("schema chat shortcut summaries = %d, want 98", shortcutCount)
}
for _, cliPath := range missingChatCatalogCoveragePaths() {
if summaryByCLIPath[cliPath] == nil {
t.Fatalf("schema chat missing expected catalog tool %q", cliPath)
}
}
assertSchemaSummarySafety(t, summaryByCLIPath, "chat clear-messages", "destructive", "high", "user_required")
assertSchemaSummarySafety(t, summaryByCLIPath, "chat data-auth cross-org", "write", "high", "user_required")
assertSchemaSummarySafety(t, summaryByCLIPath, "chat group share-invite", "write", "medium", "user_required")
assertChatCatalogCompleteLeafContracts(t)
}
func assertSchemaSummarySafety(
t testing.TB,
summaries map[string]map[string]any,
cliPath string,
effect string,
risk string,
confirmation string,
) {
t.Helper()
summary := summaries[cliPath]
if summary == nil {
t.Fatalf("schema chat missing expected catalog tool %q", cliPath)
}
if got := schemaContractString(summary["effect"]); got != effect {
t.Fatalf("%s effect = %q, want %q", cliPath, got, effect)
}
if got := schemaContractString(summary["risk"]); got != risk {
t.Fatalf("%s risk = %q, want %q", cliPath, got, risk)
}
if got := schemaContractString(summary["confirmation"]); got != confirmation {
t.Fatalf("%s confirmation = %q, want %q", cliPath, got, confirmation)
}
}
func assertChatCatalogCompleteLeafContracts(t testing.TB) {
t.Helper()
for _, cliPath := range []string{
"chat clear-messages",
"chat clear-red-point",
"chat hide",
"chat mark-read",
"chat mark-unread",
"chat mute-at-all",
"chat mute-red-envelope",
} {
leaf := executeShortcutSchemaQuery(t, "--cli-path", cliPath)
assertSchemaLeafParameterRequired(t, leaf, cliPath, "conversation-id", false)
assertSchemaLeafConstraints(t, leaf, cliPath, map[string]any{
"require_one_of": [][]string{{"conversation-id", "id", "chat"}},
"mutually_exclusive": [][]string{{"conversation-id", "id", "chat"}},
})
}
markRead := executeShortcutSchemaQuery(t, "--cli-path", "chat mark-read")
assertSchemaLeafParameterRequired(t, markRead, "chat mark-read", "message-id", true)
chmod := executeShortcutSchemaQuery(t, "--cli-path", "chat chmod")
assertSchemaLeafConstraints(t, chmod, "chat chmod", map[string]any{
"require_one_of": [][]string{{"conversation-id", "open-dingtalk-id", "user", "permParam"}},
"mutually_exclusive": [][]string{{"conversation-id", "open-dingtalk-id", "user"}},
})
assertChatGrantParameterFacts(t, chmod, "chat chmod")
crossOrg := executeShortcutSchemaQuery(t, "--cli-path", "chat data-auth cross-org")
assertSchemaLeafConstraints(t, crossOrg, "chat data-auth cross-org", map[string]any{
"require_one_of": [][]string{{"target-org-id", "all"}},
"mutually_exclusive": [][]string{{"target-org-id", "all"}},
})
assertChatGrantParameterFacts(t, crossOrg, "chat data-auth cross-org")
shareInvite := executeShortcutSchemaQuery(t, "--cli-path", "chat group share-invite")
assertSchemaLeafConstraints(t, shareInvite, "chat group share-invite", map[string]any{
"require_one_of": [][]string{{"target", "receiver"}},
"mutually_exclusive": [][]string{{"target", "receiver"}},
})
auditJoin := executeShortcutSchemaQuery(t, "--cli-path", "chat group audit-join-validation")
assertSchemaLeafParameterEnum(t, auditJoin, "chat group audit-join-validation", "status", []string{"AuditApprove", "AuditDelete"})
}
func assertSchemaLeafParameterRequired(t testing.TB, leaf map[string]any, cliPath, name string, want bool) {
t.Helper()
parameters := schemaContractMap(leaf["parameters"])
parameter := parameters[name]
if parameter == nil {
t.Fatalf("%s missing --%s parameter: %#v", cliPath, name, parameters)
}
if got, _ := parameter["required"].(bool); got != want {
t.Fatalf("%s --%s required = %#v, want %v", cliPath, name, parameter["required"], want)
}
}
func assertSchemaLeafParameterEnum(t testing.TB, leaf map[string]any, cliPath, name string, want []string) {
t.Helper()
parameters := schemaContractMap(leaf["parameters"])
parameter := parameters[name]
if parameter == nil {
t.Fatalf("%s missing --%s parameter: %#v", cliPath, name, parameters)
}
if got := schemaContractStringSlice(parameter["enum"]); !schemaContractJSONEqual(got, want) {
t.Fatalf("%s --%s enum = %#v, want %#v", cliPath, name, got, want)
}
}
func assertSchemaLeafConstraints(t testing.TB, leaf map[string]any, cliPath string, want map[string]any) {
t.Helper()
if got := leaf["constraints"]; !schemaContractJSONEqual(got, want) {
t.Fatalf("%s constraints = %#v, want %#v", cliPath, got, want)
}
}
func assertChatGrantParameterFacts(t testing.TB, leaf map[string]any, cliPath string) {
t.Helper()
parameters := schemaContractMap(leaf["parameters"])
grantType := parameters["grant-type"]
if grantType == nil {
t.Fatalf("%s missing --grant-type parameter: %#v", cliPath, parameters)
}
wantEnum := []string{"once", "session", "timed", "permanent"}
if got := schemaContractStringSlice(grantType["enum"]); !schemaContractJSONEqual(got, wantEnum) {
t.Fatalf("%s --grant-type enum = %#v, want %#v", cliPath, got, wantEnum)
}
if got := schemaContractString(parameters["session-id"]["required_when"]); got != "grant-type is session" {
t.Fatalf("%s --session-id required_when = %q, want grant-type is session", cliPath, got)
}
if got := schemaContractString(parameters["ttl"]["required_when"]); got != "grant-type is timed" {
t.Fatalf("%s --ttl required_when = %q, want grant-type is timed", cliPath, got)
}
}
func TestDeliveryDocUpdateShortcutPublishesCompleteConditionalContract(t *testing.T) {
-30
View File
@@ -91,36 +91,6 @@ var reviewedRuntimeSchemaExclusionGroups = []runtimeSchemaExclusionGroup{
"calendar acl add",
"calendar acl delete",
"calendar book update",
"chat category add-conv",
"chat category create",
"chat category delete",
"chat category remove-conv",
"chat category rename",
"chat chmod",
"chat clear-all-red-point",
"chat clear-messages",
"chat clear-red-point",
"chat data-auth cross-org",
"chat group audit-join-validation",
"chat group list-all",
"chat group list-join-validations",
"chat group members list-by-ids",
"chat group notice create",
"chat group notice edit",
"chat group notice get",
"chat group notice list",
"chat group share-invite",
"chat group update-alias",
"chat hide",
"chat list-all-conversations",
"chat mark-read",
"chat mark-unread",
"chat message list-emotion-replies",
"chat message set-top-msg",
"chat message unset-top-msg",
"chat mute-at-all",
"chat mute-red-envelope",
"chat text translate",
"contact label get",
"contact label list",
"contact label list-members",
@@ -38,20 +38,33 @@ func init() {
registerRequireTogether("calendar.update_calendar_event", "recurrence-type", "recurrence-interval", "recurrence-range-type")
registerExclusiveOneOf("chat.search_messages_by_sender", "sender-user-id", "sender-open-dingtalk-id")
registerExclusiveOneOf("chat.create_and_send_card", "group", "receiver")
RegisterRuntimeSchemaConstraints("chat.chat_permission_grant", RuntimeSchemaConstraints{
MutuallyExclusive: [][]string{{"conversation-id", "open-dingtalk-id", "user"}},
RequireOneOf: [][]string{{"conversation-id", "open-dingtalk-id", "user", "permParam"}},
})
registerExclusiveOneOf("chat.chat_permission_grant_cross_org_data", "target-org-id", "all")
registerRequireOneOf("chat.add_emoji_reaction", "conversation-id", "group", "id", "chat")
registerRequireOneOf("chat.add_text_emotion", "conversation-id", "group", "id", "chat")
registerExclusiveOneOf("chat.clear_conversation_messages", "conversation-id", "id", "chat")
registerExclusiveOneOf("chat.clear_conversation_red_point", "conversation-id", "id", "chat")
registerRequireOneOf("chat.update_text_emotion", "conversation-id", "group", "id", "chat")
registerExclusiveOneOf("chat.get_conversation_info", "group", "user", "open-dingtalk-id")
registerExclusiveOneOf("chat.hide_conversation", "conversation-id", "id", "chat")
registerExclusiveOneOf("chat.list_conversation_message_v2", "group", "user", "open-dingtalk-id")
registerExclusiveOneOf("chat.list_individual_chat_message", "user", "open-dingtalk-id")
registerExclusiveOneOf("chat.mark_conversation_unread", "conversation-id", "id", "chat")
registerExclusiveOneOf("chat.mark_message_read", "conversation-id", "id", "chat")
registerRequireOneOf("chat.remove_emoji_reaction", "conversation-id", "group", "id", "chat")
registerRequireOneOf("chat.remove_text_emotion", "conversation-id", "group", "id", "chat")
registerRequireOneOf("chat.send_personal_message", "text", "content", "msg-type")
registerExclusiveOneOf("chat.send_robot_message", "group", "users")
registerRequireOneOf("chat.set_group_member_mute_list", "users", "user")
registerExclusiveOneOf("chat.share_group_invite_url", "target", "receiver")
registerExclusiveOneOf("chat.transfer_group_owner", "new-owner", "user")
registerRequireOneOf("chat.update_conv_member_roles", "users", "user")
registerExclusiveOneOf("chat.update_at_all_notification_off", "conversation-id", "id", "chat")
registerRequireOneOf("chat.update_notification_off", "conversation-id", "id", "chat")
registerExclusiveOneOf("chat.update_red_env_notification_off", "conversation-id", "id", "chat")
registerRequireTogether("contact.query_dismission_employee_list", "start", "end")
registerRequireOneOf("dev.connect_status", "robot-client-id", "unified-app-id")
registerRequireOneOf("dev.connect_stop", "robot-client-id", "unified-app-id")
+53 -35
View File
@@ -1598,6 +1598,36 @@ func buildChatCrossOrgDataAuthArgs(cmd *cobra.Command) (map[string]any, error) {
return toolArgs, nil
}
func buildChatGroupShareInviteArgs(cmd *cobra.Command) (map[string]any, error) {
if err := validateRequiredFlags(cmd, "source"); err != nil {
return nil, err
}
target, _ := cmd.Flags().GetString("target")
receiver, _ := cmd.Flags().GetString("receiver")
if target == "" && receiver == "" {
return nil, fmt.Errorf("--target or --receiver is required")
}
if target != "" && receiver != "" {
return nil, fmt.Errorf("--target and --receiver are mutually exclusive")
}
toolArgs := map[string]any{
"sourceOpenConversationId": mustGetFlag(cmd, "source"),
}
if target != "" {
toolArgs["targetOpenConversationId"] = target
}
if receiver != "" {
toolArgs["receiverOpenDingTalkId"] = receiver
}
if v, _ := cmd.Flags().GetInt64("expires-seconds"); v > 0 || cmd.Flags().Changed("expires-seconds") {
toolArgs["expiresSeconds"] = v
}
if v, _ := cmd.Flags().GetString("uuid"); v != "" {
toolArgs["uuid"] = v
}
return toolArgs, nil
}
func appendChatChmodParams(cmd *cobra.Command, toolArgs map[string]any) error {
conversationID, _ := cmd.Flags().GetString("conversation-id")
openDingTalkID, _ := cmd.Flags().GetString("open-dingtalk-id")
@@ -2080,11 +2110,11 @@ func newChatCommand() *cobra.Command {
Parameters: []contract.ParamDecl{
{Name: "agentCode", Property: "agentCode", Required: boolPtr(false)},
{Name: "conversation-id", Property: "grantParams.openCid", Required: boolPtr(false)},
{Name: "grant-type", Property: "grantType", Required: boolPtr(false)},
{Name: "grant-type", Property: "grantType", Required: boolPtr(false), Enum: []string{"once", "session", "timed", "permanent"}},
{Name: "open-dingtalk-id", Property: "grantParams.openDingTalkId", Required: boolPtr(false)},
{Name: "permParam", Property: "grantParams", Required: boolPtr(false)},
{Name: "session-id", Property: "sessionId", Required: boolPtr(false)},
{Name: "ttl", Property: "ttl", Required: boolPtr(false)},
{Name: "session-id", Property: "sessionId", Required: boolPtr(false), RequiredWhen: "grant-type is session"},
{Name: "ttl", Property: "ttl", Required: boolPtr(false), RequiredWhen: "grant-type is timed"},
{Name: "user", Property: "grantParams.userId", Required: boolPtr(false)},
},
},
@@ -2123,10 +2153,15 @@ func newChatCommand() *cobra.Command {
chatDataAuthCrossOrgCmd.Flags().String("grant-type", "timed", "授权策略: once|session|timed|permanent")
chatDataAuthCrossOrgCmd.Flags().String("ttl", "24h", "timed 授权有效期,如 1h/4h/24h/7d")
chatDataAuthCrossOrgCmd.Flags().String("session-id", "", "session 授权的会话标识")
chatDataAuthCrossOrgCmd.Flags().BoolP("yes", "y", false, "确认执行跨组织 chat 数据授权")
DeclareLeafMetadata(chatDataAuthCrossOrgCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "write", Risk: "medium",
Confirmation: "not_required", Idempotency: "unknown",
Effect: "write", Risk: "high",
Confirmation: "user_required", Idempotency: "unknown",
},
Validate: func(cmd *cobra.Command, args []string) error {
_, err := buildChatCrossOrgDataAuthArgs(cmd)
return err
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
@@ -2151,10 +2186,10 @@ func newChatCommand() *cobra.Command {
Parameters: []contract.ParamDecl{
{Name: "agentCode", Property: "agentCode", Required: boolPtr(false)},
{Name: "all", Property: "grantParams.targetOrgId", Required: boolPtr(false)},
{Name: "grant-type", Property: "grantType", Required: boolPtr(false)},
{Name: "session-id", Property: "sessionId", Required: boolPtr(false)},
{Name: "grant-type", Property: "grantType", Required: boolPtr(false), Enum: []string{"once", "session", "timed", "permanent"}},
{Name: "session-id", Property: "sessionId", Required: boolPtr(false), RequiredWhen: "grant-type is session"},
{Name: "target-org-id", Property: "grantParams.targetOrgId", Required: boolPtr(false)},
{Name: "ttl", Property: "ttl", Required: boolPtr(false)},
{Name: "ttl", Property: "ttl", Required: boolPtr(false), RequiredWhen: "grant-type is timed"},
},
},
})
@@ -8338,12 +8373,16 @@ status 可选值:
if err != nil {
return fmt.Errorf("--record-id must be a valid integer: %w", err)
}
status := mustGetFlag(cmd, "status")
if status != "AuditApprove" && status != "AuditDelete" {
return fmt.Errorf("unsupported audit status %q, must be one of: AuditApprove, AuditDelete", status)
}
toolArgs := map[string]any{
"openConversationId": mustGetFlag(cmd, "group"),
"applyRecordId": recordID,
"applicantUid": mustGetFlag(cmd, "applicant"),
"inviterUid": mustGetFlag(cmd, "inviter"),
"status": mustGetFlag(cmd, "status"),
"status": status,
}
if v, _ := cmd.Flags().GetString("description"); v != "" {
toolArgs["auditDescription"] = v
@@ -8393,7 +8432,7 @@ status 可选值:
{Name: "group", Property: "openConversationId", Required: boolPtr(true)},
{Name: "inviter", Property: "inviterUid", Required: boolPtr(true)},
{Name: "record-id", Property: "applyRecordId", Required: boolPtr(true), InterfaceType: "integer"},
{Name: "status", Property: "status", Required: boolPtr(true), Enum: []string{"AuditApprove", "AuditDelete", "AuditIgnore", "AuditRefuse", "AuditBlock"}},
{Name: "status", Property: "status", Required: boolPtr(true), Enum: []string{"AuditApprove", "AuditDelete"}},
},
},
})
@@ -9497,32 +9536,10 @@ status 可选值:
dws chat group share-invite --source <openConversationId> --target <openConversationId> --expires-seconds 86400
# 查询群 ID: dws chat search --query "群名"`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := validateRequiredFlags(cmd, "source"); err != nil {
toolArgs, err := buildChatGroupShareInviteArgs(cmd)
if err != nil {
return err
}
target, _ := cmd.Flags().GetString("target")
receiver, _ := cmd.Flags().GetString("receiver")
if target == "" && receiver == "" {
return fmt.Errorf("--target or --receiver is required")
}
if target != "" && receiver != "" {
return fmt.Errorf("--target and --receiver are mutually exclusive")
}
toolArgs := map[string]any{
"sourceOpenConversationId": mustGetFlag(cmd, "source"),
}
if target != "" {
toolArgs["targetOpenConversationId"] = target
}
if receiver != "" {
toolArgs["receiverOpenDingTalkId"] = receiver
}
if v, _ := cmd.Flags().GetInt64("expires-seconds"); v > 0 || cmd.Flags().Changed("expires-seconds") {
toolArgs["expiresSeconds"] = v
}
if v, _ := cmd.Flags().GetString("uuid"); v != "" {
toolArgs["uuid"] = v
}
return callMCPToolOnServer("im", "share_group_invite_url", toolArgs)
},
}
@@ -9532,10 +9549,11 @@ status 可选值:
chatGroupShareInviteCmd.Flags().String("receiver", "", "接收分享消息的单聊用户 openDingTalkId(与 --target 二选一)")
chatGroupShareInviteCmd.Flags().Int64("expires-seconds", 0, "链接有效期(秒),0 表示永久有效,不传使用服务端默认值")
chatGroupShareInviteCmd.Flags().String("uuid", "", "消息幂等键(可选)")
chatGroupShareInviteCmd.Flags().BoolP("yes", "y", false, "确认分享群邀请链接")
DeclareLeafMetadata(chatGroupShareInviteCmd, LeafSpec{
Safety: contract.SafetySpec{
Effect: "write", Risk: "medium",
Confirmation: "not_required", Idempotency: "unknown",
Confirmation: "user_required", Idempotency: "unknown",
},
Contract: LeafContract{
Identity: contract.ToolIdentitySpec{
@@ -744,6 +744,22 @@ func TestCrossPlatformCoverageChatAuditUsesUserIDs(t *testing.T) {
}
}
func TestCrossPlatformCoverageChatAuditRejectsUnsupportedStatus(t *testing.T) {
caller := &chatChangedContractCaller{}
err := executeChatChangedContract(t, caller,
"group", "audit-join-validation",
"--group", "cid-1", "--record-id", "123", "--applicant", "user-a", "--inviter", "user-b", "--status", "AuditRefuse")
if err == nil {
t.Fatal("expected unsupported audit status error")
}
if !strings.Contains(err.Error(), `unsupported audit status "AuditRefuse"`) {
t.Fatalf("error = %v, want unsupported status", err)
}
if len(caller.calls) != 0 {
t.Fatalf("unsupported status must not call MCP: %#v", caller.calls)
}
}
func TestCrossPlatformCoverageChatSendResolvesUserBeforeDispatch(t *testing.T) {
caller := &chatChangedContractCaller{resolveUsers: true}
err := executeChatChangedContract(t, caller, "message", "send", "--user", "123", "--text", "hello")
@@ -168,6 +168,77 @@ func TestCrossPlatformCoverageWukongWeeklyChatCategoryQueries(t *testing.T) {
})
}
func TestCrossPlatformCoverageChatDataAuthCrossOrgRequiresConfirmation(t *testing.T) {
caller := &wukongWeeklySyncCaller{}
_, _, err := executeWukongWeeklySyncCommand(t, "chat", caller, newChatCommand,
"data-auth", "cross-org", "--target-org-id", "439446171")
requireWukongWeeklySyncConfirmation(t, err)
requireWukongWeeklySyncNoCalls(t, caller)
caller = &wukongWeeklySyncCaller{}
_, _, err = executeWukongWeeklySyncCommand(t, "chat", caller, newChatCommand,
"data-auth", "cross-org", "--target-org-id", "439446171", "--yes")
if err != nil {
t.Fatalf("confirmed cross-org data auth returned error: %v", err)
}
requireWukongWeeklySyncCall(t, caller, wukongWeeklySyncCall{
server: "im",
tool: "chat_permission_grant",
args: map[string]any{
"agentCode": "wukong",
"grantCategory": "data",
"grantParams": `{"targetOrgId":"439446171"}`,
"grantType": "timed",
"scope": "chat.data:cross-org",
"ttl": "24h",
},
})
if _, ok := caller.calls[0].args["yes"]; ok {
t.Fatalf("confirmed payload leaked yes flag: %#v", caller.calls[0].args)
}
}
func TestCrossPlatformCoverageChatGroupShareInviteRequiresConfirmation(t *testing.T) {
caller := &wukongWeeklySyncCaller{}
_, _, err := executeWukongWeeklySyncCommand(t, "chat", caller, newChatCommand,
"group", "share-invite", "--target", "cid-target")
if err == nil || !strings.Contains(err.Error(), "source") {
t.Fatalf("missing source error = %v, want source", err)
}
requireWukongWeeklySyncNoCalls(t, caller)
caller = &wukongWeeklySyncCaller{}
_, _, err = executeWukongWeeklySyncCommand(t, "chat", caller, newChatCommand,
"group", "share-invite", "--source", "cid-source", "--target", "cid-target")
requireWukongWeeklySyncConfirmation(t, err)
requireWukongWeeklySyncNoCalls(t, caller)
caller = &wukongWeeklySyncCaller{}
_, _, err = executeWukongWeeklySyncCommand(t, "chat", caller, newChatCommand,
"group", "share-invite",
"--source", "cid-source",
"--target", "cid-target",
"--expires-seconds", "86400",
"--uuid", "invite-uuid",
"--yes")
if err != nil {
t.Fatalf("confirmed share-invite returned error: %v", err)
}
requireWukongWeeklySyncCall(t, caller, wukongWeeklySyncCall{
server: "im",
tool: "share_group_invite_url",
args: map[string]any{
"expiresSeconds": int64(86400),
"sourceOpenConversationId": "cid-source",
"targetOpenConversationId": "cid-target",
"uuid": "invite-uuid",
},
})
if _, ok := caller.calls[0].args["yes"]; ok {
t.Fatalf("confirmed payload leaked yes flag: %#v", caller.calls[0].args)
}
}
func TestCrossPlatformCoverageWukongWeeklyChatMessageEditValidation(t *testing.T) {
root := newChatCommand()
edit, _, findErr := root.Find([]string{"message", "edit"})