bf9395e022
CI / results (push) Blocked by required conditions
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / e2e-live (push) Waiting to run
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / deadcode (push) Waiting to run
333 lines
9.2 KiB
Go
333 lines
9.2 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package drive
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/larksuite/cli/internal/cmdutil"
|
|
"github.com/larksuite/cli/internal/core"
|
|
"github.com/larksuite/cli/internal/httpmock"
|
|
"github.com/larksuite/cli/shortcuts/common"
|
|
)
|
|
|
|
func TestValidateDriveCreateFolderSpecRejectsInvalidInputs(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
spec driveCreateFolderSpec
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "empty name",
|
|
spec: driveCreateFolderSpec{},
|
|
wantErr: "--name must not be empty",
|
|
},
|
|
{
|
|
name: "name too long",
|
|
spec: driveCreateFolderSpec{
|
|
Name: strings.Repeat("a", 257),
|
|
},
|
|
wantErr: "maximum of 256 bytes",
|
|
},
|
|
{
|
|
name: "invalid folder token",
|
|
spec: driveCreateFolderSpec{
|
|
Name: "Reports",
|
|
FolderToken: "../bad",
|
|
},
|
|
wantErr: "--folder-token",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
err := validateDriveCreateFolderSpec(tt.spec)
|
|
if err == nil {
|
|
t.Fatal("expected validation error, got nil")
|
|
}
|
|
if !strings.Contains(err.Error(), tt.wantErr) {
|
|
t.Fatalf("error = %q, want substring %q", err.Error(), tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDriveCreateFolderDryRunIncludesCreateRequest(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cmd := &cobra.Command{Use: "drive +create-folder"}
|
|
cmd.Flags().String("name", "", "")
|
|
cmd.Flags().String("folder-token", "", "")
|
|
if err := cmd.Flags().Set("name", " Weekly Reports "); err != nil {
|
|
t.Fatalf("set --name: %v", err)
|
|
}
|
|
if err := cmd.Flags().Set("folder-token", " fld_parent "); err != nil {
|
|
t.Fatalf("set --folder-token: %v", err)
|
|
}
|
|
|
|
runtime := common.TestNewRuntimeContextWithIdentity(cmd, nil, core.AsBot)
|
|
dry := DriveCreateFolder.DryRun(context.Background(), runtime)
|
|
if dry == nil {
|
|
t.Fatal("DryRun returned nil")
|
|
}
|
|
|
|
data, err := json.Marshal(dry)
|
|
if err != nil {
|
|
t.Fatalf("marshal dry run: %v", err)
|
|
}
|
|
|
|
var got struct {
|
|
API []struct {
|
|
Method string `json:"method"`
|
|
URL string `json:"url"`
|
|
Body map[string]interface{} `json:"body"`
|
|
} `json:"api"`
|
|
}
|
|
if err := json.Unmarshal(data, &got); err != nil {
|
|
t.Fatalf("unmarshal dry run json: %v", err)
|
|
}
|
|
if len(got.API) != 1 {
|
|
t.Fatalf("expected 1 API call, got %d", len(got.API))
|
|
}
|
|
if got.API[0].Method != "POST" || got.API[0].URL != "/open-apis/drive/v1/files/create_folder" {
|
|
t.Fatalf("unexpected dry-run API call: %#v", got.API[0])
|
|
}
|
|
if got.API[0].Body["name"] != "Weekly Reports" {
|
|
t.Fatalf("name = %#v, want %q", got.API[0].Body["name"], "Weekly Reports")
|
|
}
|
|
if got.API[0].Body["folder_token"] != "fld_parent" {
|
|
t.Fatalf("folder_token = %#v, want %q", got.API[0].Body["folder_token"], "fld_parent")
|
|
}
|
|
}
|
|
|
|
func TestDriveCreateFolderBotAutoGrantSuccess(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
f, stdout, _, reg := cmdutil.TestFactory(t, drivePermissionGrantTestConfig(t, "ou_current_user"))
|
|
|
|
createStub := &httpmock.Stub{
|
|
Method: "POST",
|
|
URL: "/open-apis/drive/v1/files/create_folder",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "ok",
|
|
"data": map[string]interface{}{
|
|
"token": "fld_created",
|
|
"url": "https://example.feishu.cn/drive/folder/fld_created",
|
|
},
|
|
},
|
|
}
|
|
reg.Register(createStub)
|
|
|
|
permStub := &httpmock.Stub{
|
|
Method: "POST",
|
|
URL: "/open-apis/drive/v1/permissions/fld_created/members",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "ok",
|
|
},
|
|
}
|
|
reg.Register(permStub)
|
|
|
|
err := mountAndRunDrive(t, DriveCreateFolder, []string{
|
|
"+create-folder",
|
|
"--name", " Weekly Reports ",
|
|
"--folder-token", " fld_parent ",
|
|
"--as", "bot",
|
|
}, f, stdout)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
body := decodeCapturedJSONBody(t, createStub)
|
|
if body["name"] != "Weekly Reports" {
|
|
t.Fatalf("name = %#v, want %q", body["name"], "Weekly Reports")
|
|
}
|
|
if body["folder_token"] != "fld_parent" {
|
|
t.Fatalf("folder_token = %#v, want %q", body["folder_token"], "fld_parent")
|
|
}
|
|
|
|
data := decodeDriveEnvelope(t, stdout)
|
|
if data["folder_token"] != "fld_created" {
|
|
t.Fatalf("folder_token = %#v, want %q", data["folder_token"], "fld_created")
|
|
}
|
|
if data["parent_folder_token"] != "fld_parent" {
|
|
t.Fatalf("parent_folder_token = %#v, want %q", data["parent_folder_token"], "fld_parent")
|
|
}
|
|
if data["name"] != "Weekly Reports" {
|
|
t.Fatalf("name = %#v, want %q", data["name"], "Weekly Reports")
|
|
}
|
|
if data["url"] != "https://example.feishu.cn/drive/folder/fld_created" {
|
|
t.Fatalf("url = %#v, want folder url", data["url"])
|
|
}
|
|
|
|
grant, _ := data["permission_grant"].(map[string]interface{})
|
|
if grant["status"] != common.PermissionGrantGranted {
|
|
t.Fatalf("permission_grant.status = %#v, want %q", grant["status"], common.PermissionGrantGranted)
|
|
}
|
|
if grant["user_open_id"] != "ou_current_user" {
|
|
t.Fatalf("permission_grant.user_open_id = %#v, want %q", grant["user_open_id"], "ou_current_user")
|
|
}
|
|
if grant["message"] != "Granted the current CLI user full_access (可管理权限) on the new folder." {
|
|
t.Fatalf("permission_grant.message = %#v", grant["message"])
|
|
}
|
|
|
|
permBody := decodeCapturedJSONBody(t, permStub)
|
|
if permBody["member_type"] != "openid" || permBody["member_id"] != "ou_current_user" || permBody["perm"] != "full_access" || permBody["type"] != "user" {
|
|
t.Fatalf("unexpected permission request body: %#v", permBody)
|
|
}
|
|
}
|
|
|
|
func TestDriveCreateFolderUsesRootWhenParentIsOmitted(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
f, stdout, _, reg := cmdutil.TestFactory(t, drivePermissionGrantTestConfig(t, "ou_current_user"))
|
|
|
|
createStub := &httpmock.Stub{
|
|
Method: "POST",
|
|
URL: "/open-apis/drive/v1/files/create_folder",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "ok",
|
|
"data": map[string]interface{}{
|
|
"token": "fld_root_child",
|
|
},
|
|
},
|
|
}
|
|
reg.Register(createStub)
|
|
|
|
err := mountAndRunDrive(t, DriveCreateFolder, []string{
|
|
"+create-folder",
|
|
"--name", "Inbox",
|
|
"--as", "user",
|
|
}, f, stdout)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
body := decodeCapturedJSONBody(t, createStub)
|
|
if body["folder_token"] != "" {
|
|
t.Fatalf("folder_token = %#v, want empty string for root create", body["folder_token"])
|
|
}
|
|
|
|
data := decodeDriveEnvelope(t, stdout)
|
|
if data["folder_token"] != "fld_root_child" {
|
|
t.Fatalf("folder_token = %#v, want %q", data["folder_token"], "fld_root_child")
|
|
}
|
|
if data["parent_folder_token"] != "" {
|
|
t.Fatalf("parent_folder_token = %#v, want empty string", data["parent_folder_token"])
|
|
}
|
|
if _, ok := data["permission_grant"]; ok {
|
|
t.Fatalf("did not expect permission_grant in user mode output: %#v", data)
|
|
}
|
|
}
|
|
|
|
func TestDriveCreateFolderFallbackURLWhenBackendOmitsIt(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
f, stdout, _, reg := cmdutil.TestFactory(t, drivePermissionGrantTestConfig(t, ""))
|
|
|
|
reg.Register(&httpmock.Stub{
|
|
Method: "POST",
|
|
URL: "/open-apis/drive/v1/files/create_folder",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "ok",
|
|
"data": map[string]interface{}{
|
|
"token": "fld_created",
|
|
// "url" deliberately omitted to exercise the fallback.
|
|
},
|
|
},
|
|
})
|
|
|
|
err := mountAndRunDrive(t, DriveCreateFolder, []string{
|
|
"+create-folder",
|
|
"--name", "Weekly Reports",
|
|
"--as", "user",
|
|
}, f, stdout)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
data := decodeDriveEnvelope(t, stdout)
|
|
if got, want := data["url"], "https://www.feishu.cn/drive/folder/fld_created"; got != want {
|
|
t.Fatalf("url = %#v, want %q (brand-standard fallback)", got, want)
|
|
}
|
|
}
|
|
|
|
func TestDriveCreateFolderFallbackURLWhenBackendURLIsWhitespace(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
f, stdout, _, reg := cmdutil.TestFactory(t, drivePermissionGrantTestConfig(t, ""))
|
|
|
|
reg.Register(&httpmock.Stub{
|
|
Method: "POST",
|
|
URL: "/open-apis/drive/v1/files/create_folder",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "ok",
|
|
"data": map[string]interface{}{
|
|
"token": "fld_created",
|
|
"url": " ", // whitespace-only must trigger fallback, not pass through.
|
|
},
|
|
},
|
|
})
|
|
|
|
err := mountAndRunDrive(t, DriveCreateFolder, []string{
|
|
"+create-folder",
|
|
"--name", "Weekly Reports",
|
|
"--as", "user",
|
|
}, f, stdout)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
data := decodeDriveEnvelope(t, stdout)
|
|
if got, want := data["url"], "https://www.feishu.cn/drive/folder/fld_created"; got != want {
|
|
t.Fatalf("url = %#v, want %q (whitespace-only backend URL must yield fallback)", got, want)
|
|
}
|
|
}
|
|
|
|
func TestDriveCreateFolderRejectsCreateResponseWithoutToken(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
f, stdout, _, reg := cmdutil.TestFactory(t, drivePermissionGrantTestConfig(t, "ou_current_user"))
|
|
|
|
reg.Register(&httpmock.Stub{
|
|
Method: "POST",
|
|
URL: "/open-apis/drive/v1/files/create_folder",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "ok",
|
|
"data": map[string]interface{}{
|
|
"url": "https://example.feishu.cn/drive/folder/unknown",
|
|
},
|
|
},
|
|
})
|
|
|
|
err := mountAndRunDrive(t, DriveCreateFolder, []string{
|
|
"+create-folder",
|
|
"--name", "Broken Folder",
|
|
"--as", "bot",
|
|
}, f, stdout)
|
|
if err == nil {
|
|
t.Fatal("expected error, got nil")
|
|
}
|
|
if !strings.Contains(err.Error(), "returned no folder token") {
|
|
t.Fatalf("err = %v, want missing folder token error", err)
|
|
}
|
|
if stdout.Len() != 0 {
|
|
t.Fatalf("stdout should be empty on error, got %s", stdout.String())
|
|
}
|
|
}
|