mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 14:02:43 +08:00
* feat(task): add task shortcuts with skill docs and tests * docs(task): document task event payload shape * refactor(task): remove unused buildUserIDs helper * fix(task): handle api error codes in set-ancestor * docs(task): clarify get-related-tasks page-token unit * feat(task): support bot identity for subscribe-event * docs(task): clarify bot subscribe-event scope * docs(task): clarify related-task pagination semantics * docs(task): add BOE selftest report (boe_task_tasklist_oapi_support) * docs(task): prefer related-task shortcuts over search for scoped queries * docs(task): clarify tasklist search routing * docs(task): route keywordless tasklist queries to list API * docs(task): refine search routing heuristics * feat(event): include task user-access updates in catch-all subscribe * docs(task): remove auth status --json guidance
208 lines
5.7 KiB
Go
208 lines
5.7 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package task
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/larksuite/cli/internal/httpmock"
|
|
"github.com/larksuite/cli/shortcuts/common"
|
|
)
|
|
|
|
func TestTaskFollowedBy(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
task map[string]interface{}
|
|
userOpenID string
|
|
want bool
|
|
}{
|
|
{
|
|
name: "contains follower",
|
|
task: map[string]interface{}{
|
|
"members": []interface{}{
|
|
map[string]interface{}{"id": "ou_1", "role": "assignee"},
|
|
map[string]interface{}{"id": "ou_2", "role": "follower"},
|
|
},
|
|
},
|
|
userOpenID: "ou_2",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "missing follower",
|
|
task: map[string]interface{}{
|
|
"members": []interface{}{
|
|
map[string]interface{}{"id": "ou_1", "role": "assignee"},
|
|
},
|
|
},
|
|
userOpenID: "ou_3",
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := taskFollowedBy(tt.task, tt.userOpenID)
|
|
if got != tt.want {
|
|
t.Fatalf("taskFollowedBy() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetRelatedTasks_DryRun(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setup func(*cobra.Command)
|
|
wantParts []string
|
|
}{
|
|
{
|
|
name: "with page token and incomplete filter",
|
|
setup: func(cmd *cobra.Command) {
|
|
_ = cmd.Flags().Set("include-complete", "false")
|
|
_ = cmd.Flags().Set("page-token", "pt_001")
|
|
},
|
|
wantParts: []string{"GET /open-apis/task/v2/task_v2/list_related_task", "page_token=pt_001", "completed=false"},
|
|
},
|
|
{
|
|
name: "default query params",
|
|
setup: func(cmd *cobra.Command) {},
|
|
wantParts: []string{"GET /open-apis/task/v2/task_v2/list_related_task", "page_size=100", "user_id_type=open_id"},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
cmd := &cobra.Command{Use: "test"}
|
|
cmd.Flags().Bool("include-complete", true, "")
|
|
cmd.Flags().String("page-token", "", "")
|
|
tt.setup(cmd)
|
|
runtime := common.TestNewRuntimeContextWithIdentity(cmd, taskTestConfig(t), "user")
|
|
out := GetRelatedTasks.DryRun(nil, runtime).Format()
|
|
for _, want := range tt.wantParts {
|
|
if !strings.Contains(out, want) {
|
|
t.Fatalf("dry run output missing %q: %s", want, out)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetRelatedTasks_Execute(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
args []string
|
|
register func(*httpmock.Registry)
|
|
wantParts []string
|
|
}{
|
|
{
|
|
name: "json created by me",
|
|
args: []string{"+get-related-tasks", "--as", "bot", "--format", "json", "--created-by-me"},
|
|
register: func(reg *httpmock.Registry) {
|
|
reg.Register(&httpmock.Stub{
|
|
Method: "GET",
|
|
URL: "/open-apis/task/v2/task_v2/list_related_task",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "success",
|
|
"data": map[string]interface{}{
|
|
"has_more": false,
|
|
"page_token": "",
|
|
"items": []interface{}{
|
|
map[string]interface{}{
|
|
"guid": "task-123",
|
|
"summary": "Related Task",
|
|
"description": "desc",
|
|
"status": "done",
|
|
"source": 1,
|
|
"mode": 2,
|
|
"subtask_count": 0,
|
|
"tasklists": []interface{}{},
|
|
"url": "https://example.com/task-123",
|
|
"creator": map[string]interface{}{"id": "ou_testuser", "type": "user"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
},
|
|
wantParts: []string{`"guid": "task-123"`, `"summary": "Related Task"`},
|
|
},
|
|
{
|
|
name: "pretty pagination followed by me",
|
|
args: []string{"+get-related-tasks", "--as", "bot", "--format", "pretty", "--followed-by-me", "--page-limit", "2"},
|
|
register: func(reg *httpmock.Registry) {
|
|
reg.Register(&httpmock.Stub{
|
|
Method: "GET",
|
|
URL: "/open-apis/task/v2/task_v2/list_related_task",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "success",
|
|
"data": map[string]interface{}{
|
|
"has_more": true,
|
|
"page_token": "pt_2",
|
|
"items": []interface{}{
|
|
map[string]interface{}{
|
|
"guid": "task-1",
|
|
"summary": "Task One",
|
|
"url": "https://example.com/task-1",
|
|
"creator": map[string]interface{}{"id": "ou_other", "type": "user"},
|
|
"members": []interface{}{map[string]interface{}{"id": "ou_testuser", "role": "follower"}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
reg.Register(&httpmock.Stub{
|
|
Method: "GET",
|
|
URL: "page_token=pt_2",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "success",
|
|
"data": map[string]interface{}{
|
|
"has_more": false,
|
|
"page_token": "",
|
|
"items": []interface{}{
|
|
map[string]interface{}{
|
|
"guid": "task-2",
|
|
"summary": "Task Two",
|
|
"url": "https://example.com/task-2",
|
|
"creator": map[string]interface{}{"id": "ou_other", "type": "user"},
|
|
"members": []interface{}{map[string]interface{}{"id": "ou_testuser", "role": "follower"}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
},
|
|
wantParts: []string{"Task One", "Task Two"},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
f, stdout, _, reg := taskShortcutTestFactory(t)
|
|
warmTenantToken(t, f, reg)
|
|
tt.register(reg)
|
|
|
|
s := GetRelatedTasks
|
|
s.AuthTypes = []string{"bot", "user"}
|
|
err := runMountedTaskShortcut(t, s, tt.args, f, stdout)
|
|
if err != nil {
|
|
t.Fatalf("runMountedTaskShortcut() error = %v", err)
|
|
}
|
|
|
|
out := stdout.String()
|
|
outNorm := strings.ReplaceAll(out, `":"`, `": "`)
|
|
for _, want := range tt.wantParts {
|
|
if !strings.Contains(out, want) && !strings.Contains(outNorm, want) {
|
|
t.Fatalf("output missing %q: %s", want, out)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|