Files
larksuite__cli/shortcuts/task/task_output_test.go
ILUO b9cced677a feat: expose compact task fields (#2109)
* docs: define task compact field projection

* docs: add task compact fields implementation plan

* feat: add task output field projector

* feat: expose missing fields in task summaries

* feat: expand task write result fields

* feat: expose fields in nested task results

* chore: remove internal planning artifacts

* docs: require progressive task command discovery

* docs: require schema-backed task create data
2026-08-11 17:54:06 +08:00

145 lines
4.3 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package task
import (
"encoding/json"
"net/http"
"reflect"
"testing"
"github.com/larksuite/cli/internal/httpmock"
"github.com/larksuite/cli/shortcuts/common"
)
func TestProjectTaskFields(t *testing.T) {
task := map[string]interface{}{
"summary": "Ship compact fields",
"members": []interface{}{map[string]interface{}{
"id": "ou_owner", "name": "Owner", "type": "user", "role": "assignee",
}},
"start": map[string]interface{}{"timestamp": "1000", "is_all_day": false},
"due": map[string]interface{}{"timestamp": "2000", "is_all_day": false},
"status": "todo",
}
out := map[string]interface{}{"guid": "task-1"}
projectTaskFields(out, task, standardTaskOutputFields...)
for _, field := range standardTaskOutputFields {
key := string(field)
if !reflect.DeepEqual(out[key], task[key]) {
t.Fatalf("%s = %#v, want %#v", key, out[key], task[key])
}
}
if out["guid"] != "task-1" {
t.Fatalf("existing guid changed: %#v", out)
}
}
func TestProjectTaskFieldsOmitsAbsentFields(t *testing.T) {
out := map[string]interface{}{}
projectTaskFields(out, map[string]interface{}{"summary": "Only title"}, standardTaskOutputFields...)
if out["summary"] != "Only title" {
t.Fatalf("summary = %#v, want Only title", out["summary"])
}
for _, key := range []string{"members", "start", "due", "status"} {
if _, ok := out[key]; ok {
t.Fatalf("%s unexpectedly projected: %#v", key, out)
}
}
}
func TestTaskRootOutputFields(t *testing.T) {
tests := []struct {
name string
shortcut common.Shortcut
method string
url string
args []string
}{
{
name: "create", shortcut: CreateTask, method: http.MethodPost,
url: "/open-apis/task/v2/tasks",
args: []string{"+create", "--summary", "Requested title", "--as", "bot", "--format", "json"},
},
{
name: "reopen", shortcut: ReopenTask, method: http.MethodPatch,
url: "/open-apis/task/v2/tasks/task-1",
args: []string{"+reopen", "--task-id", "task-1", "--as", "bot", "--format", "json"},
},
{
name: "assign", shortcut: AssignTask, method: http.MethodPost,
url: "/open-apis/task/v2/tasks/task-1/add_members",
args: []string{"+assign", "--task-id", "task-1", "--add", "ou_owner", "--as", "bot", "--format", "json"},
},
{
name: "followers", shortcut: FollowersTask, method: http.MethodPost,
url: "/open-apis/task/v2/tasks/task-1/add_members",
args: []string{"+followers", "--task-id", "task-1", "--add", "ou_follower", "--as", "bot", "--format", "json"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f, stdout, _, reg := taskShortcutTestFactory(t)
warmTenantToken(t, f, reg)
reg.Register(&httpmock.Stub{
Method: tt.method,
URL: tt.url,
Body: map[string]interface{}{
"code": 0,
"msg": "success",
"data": map[string]interface{}{"task": fullTaskOutputFixture()},
},
})
shortcut := tt.shortcut
shortcut.AuthTypes = []string{"bot", "user"}
if err := runMountedTaskShortcut(t, shortcut, tt.args, f, stdout); err != nil {
t.Fatalf("runMountedTaskShortcut() error = %v", err)
}
assertStandardTaskFields(t, decodeTaskOutputData(t, stdout.Bytes()))
})
}
}
func fullTaskOutputFixture() map[string]interface{} {
return map[string]interface{}{
"guid": "task-1",
"url": "https://example.com/task-1&suite_entity_num=t1",
"summary": "Server title",
"members": []interface{}{
map[string]interface{}{"id": "ou_owner", "name": "Owner", "type": "user", "role": "assignee"},
},
"start": map[string]interface{}{"timestamp": "1000", "is_all_day": false},
"due": map[string]interface{}{"timestamp": "2000", "is_all_day": false},
"status": "todo",
}
}
func decodeTaskOutputData(t *testing.T, raw []byte) map[string]interface{} {
t.Helper()
var envelope map[string]interface{}
if err := json.Unmarshal(raw, &envelope); err != nil {
t.Fatalf("decode output: %v\n%s", err, raw)
}
data, ok := envelope["data"].(map[string]interface{})
if !ok {
t.Fatalf("data = %#v, want object", envelope["data"])
}
return data
}
func assertStandardTaskFields(t *testing.T, data map[string]interface{}) {
t.Helper()
for _, key := range []string{"summary", "members", "start", "due", "status"} {
if _, ok := data[key]; !ok {
t.Errorf("data missing %q: %#v", key, data)
}
}
}