mirror of
https://github.com/larksuite/cli.git
synced 2026-07-08 10:08:02 +08:00
703 lines
21 KiB
Go
703 lines
21 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package sheets
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/larksuite/cli/internal/cmdutil"
|
|
"github.com/larksuite/cli/internal/httpmock"
|
|
"github.com/larksuite/cli/internal/output"
|
|
"github.com/larksuite/cli/shortcuts/common"
|
|
"github.com/tidwall/gjson"
|
|
)
|
|
|
|
func TestSheetCreateSheetValidateMissingToken(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rt := newDimTestRuntime(t,
|
|
map[string]string{"url": "", "spreadsheet-token": "", "title": "Sheet 2"},
|
|
nil, nil)
|
|
err := SheetCreateSheet.Validate(context.Background(), rt)
|
|
if err == nil || !strings.Contains(err.Error(), "--url or --spreadsheet-token") {
|
|
t.Fatalf("expected token error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSheetInfoRequiresSpreadsheetMetaAndReadScopes(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
want := []string{"sheets:spreadsheet.meta:read", "sheets:spreadsheet:read"}
|
|
if !reflect.DeepEqual(SheetInfo.Scopes, want) {
|
|
t.Fatalf("SheetInfo scopes = %v, want %v", SheetInfo.Scopes, want)
|
|
}
|
|
}
|
|
|
|
func TestSheetManageValidateRejectsURLAndTokenTogether(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
shortcut common.Shortcut
|
|
args map[string]string
|
|
}{
|
|
{
|
|
name: "create-sheet",
|
|
shortcut: SheetCreateSheet,
|
|
args: map[string]string{
|
|
"url": "https://example.feishu.cn/sheets/shtFromURL",
|
|
"spreadsheet-token": "shtTOKEN",
|
|
"title": "Data",
|
|
},
|
|
},
|
|
{
|
|
name: "copy-sheet",
|
|
shortcut: SheetCopySheet,
|
|
args: map[string]string{
|
|
"url": "https://example.feishu.cn/sheets/shtFromURL",
|
|
"spreadsheet-token": "shtTOKEN",
|
|
"sheet-id": "sheet1",
|
|
"title": "Copy",
|
|
},
|
|
},
|
|
{
|
|
name: "delete-sheet",
|
|
shortcut: SheetDeleteSheet,
|
|
args: map[string]string{
|
|
"url": "https://example.feishu.cn/sheets/shtFromURL",
|
|
"spreadsheet-token": "shtTOKEN",
|
|
"sheet-id": "sheet1",
|
|
},
|
|
},
|
|
{
|
|
name: "update-sheet",
|
|
shortcut: SheetUpdateSheet,
|
|
args: map[string]string{
|
|
"url": "https://example.feishu.cn/sheets/shtFromURL",
|
|
"spreadsheet-token": "shtTOKEN",
|
|
"sheet-id": "sheet1",
|
|
"title": "Renamed",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
rt := newDimTestRuntime(t, tt.args, nil, nil)
|
|
err := tt.shortcut.Validate(context.Background(), rt)
|
|
if err == nil || !strings.Contains(err.Error(), "mutually exclusive") {
|
|
t.Fatalf("expected mutual exclusivity error, got: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSheetCreateSheetValidateRejectsInvalidTitle(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
title string
|
|
wantSubst string
|
|
}{
|
|
{name: "special chars", title: "bad/title", wantSubst: "must not contain"},
|
|
{name: "empty", title: "", wantSubst: "must not be empty"},
|
|
{name: "tab", title: "bad\ttitle", wantSubst: "tabs or line breaks"},
|
|
{name: "newline", title: "bad\ntitle", wantSubst: "tabs or line breaks"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
rt := newDimTestRuntime(t,
|
|
map[string]string{"spreadsheet-token": "sht1", "title": tt.title},
|
|
nil, nil)
|
|
err := SheetCreateSheet.Validate(context.Background(), rt)
|
|
if err == nil || !strings.Contains(err.Error(), tt.wantSubst) {
|
|
t.Fatalf("expected title error containing %q, got: %v", tt.wantSubst, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSheetCreateSheetValidateRejectsNegativeIndexWhenTitleProvided(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rt := newDimTestRuntime(t,
|
|
map[string]string{"spreadsheet-token": "sht1", "title": "Data"},
|
|
map[string]int{"index": -1}, nil)
|
|
err := SheetCreateSheet.Validate(context.Background(), rt)
|
|
if err == nil || !strings.Contains(err.Error(), "--index must be >= 0") {
|
|
t.Fatalf("expected index validation error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSheetCopySheetValidateRejectsInvalidTitle(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rt := newDimTestRuntime(t,
|
|
map[string]string{"spreadsheet-token": "sht1", "sheet-id": "sheet1", "title": "bad\ttitle"},
|
|
nil, nil)
|
|
err := SheetCopySheet.Validate(context.Background(), rt)
|
|
if err == nil || !strings.Contains(err.Error(), "tabs or line breaks") {
|
|
t.Fatalf("expected title error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSheetCopySheetValidateRejectsNegativeIndexWhenTitleProvided(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rt := newDimTestRuntime(t,
|
|
map[string]string{"spreadsheet-token": "sht1", "sheet-id": "sheet1", "title": "Copy"},
|
|
map[string]int{"index": -1}, nil)
|
|
err := SheetCopySheet.Validate(context.Background(), rt)
|
|
if err == nil || !strings.Contains(err.Error(), "--index must be >= 0") {
|
|
t.Fatalf("expected index validation error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSheetUpdateSheetValidateRejectsEmptyTitle(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rt := newDimTestRuntime(t,
|
|
map[string]string{"spreadsheet-token": "sht1", "sheet-id": "sheet1", "title": ""},
|
|
nil, nil)
|
|
err := SheetUpdateSheet.Validate(context.Background(), rt)
|
|
if err == nil || !strings.Contains(err.Error(), "must not be empty") {
|
|
t.Fatalf("expected empty-title error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSheetCreateSheetDryRun(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rt := newDimTestRuntime(t,
|
|
map[string]string{"spreadsheet-token": "shtTOKEN", "title": "Data"},
|
|
map[string]int{"index": 0}, nil)
|
|
got := mustMarshalSheetsDryRun(t, SheetCreateSheet.DryRun(context.Background(), rt))
|
|
if !strings.Contains(got, `"/open-apis/sheets/v2/spreadsheets/shtTOKEN/sheets_batch_update"`) {
|
|
t.Fatalf("DryRun URL mismatch: %s", got)
|
|
}
|
|
if !strings.Contains(got, `"addSheet"`) || !strings.Contains(got, `"title":"Data"`) || !strings.Contains(got, `"index":0`) {
|
|
t.Fatalf("DryRun body mismatch: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestSheetCreateSheetExecuteSuccess(t *testing.T) {
|
|
f, stdout, _, reg := cmdutil.TestFactory(t, sheetsTestConfig())
|
|
stub := &httpmock.Stub{
|
|
Method: "POST",
|
|
URL: "/open-apis/sheets/v2/spreadsheets/shtTOKEN/sheets_batch_update",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "success",
|
|
"data": map[string]interface{}{
|
|
"replies": []interface{}{
|
|
map[string]interface{}{
|
|
"addSheet": map[string]interface{}{
|
|
"properties": map[string]interface{}{
|
|
"sheetId": "sheet_new",
|
|
"title": "Data",
|
|
"index": 0,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
reg.Register(stub)
|
|
|
|
err := mountAndRunSheets(t, SheetCreateSheet, []string{
|
|
"+create-sheet",
|
|
"--spreadsheet-token", "shtTOKEN",
|
|
"--title", "Data",
|
|
"--index", "0",
|
|
"--as", "user",
|
|
}, f, stdout)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if gjson.Get(stdout.String(), "data.sheet_id").String() != "sheet_new" {
|
|
t.Fatalf("stdout missing sheet_id: %s", stdout.String())
|
|
}
|
|
|
|
var body map[string]interface{}
|
|
if err := json.Unmarshal(stub.CapturedBody, &body); err != nil {
|
|
t.Fatalf("parse body: %v", err)
|
|
}
|
|
requests, _ := body["requests"].([]interface{})
|
|
if len(requests) != 1 {
|
|
t.Fatalf("unexpected body: %#v", body)
|
|
}
|
|
req0, _ := requests[0].(map[string]interface{})
|
|
addSheet, _ := req0["addSheet"].(map[string]interface{})
|
|
props, _ := addSheet["properties"].(map[string]interface{})
|
|
if props["title"] != "Data" {
|
|
t.Fatalf("request title = %#v", props["title"])
|
|
}
|
|
if idx, ok := props["index"].(float64); !ok || idx != 0 {
|
|
t.Fatalf("request index = %#v", props["index"])
|
|
}
|
|
}
|
|
|
|
func TestSheetCopySheetValidateMissingSheetID(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rt := newDimTestRuntime(t,
|
|
map[string]string{"spreadsheet-token": "sht1", "sheet-id": ""},
|
|
nil, nil)
|
|
err := SheetCopySheet.Validate(context.Background(), rt)
|
|
if err == nil || !strings.Contains(err.Error(), "--sheet-id") {
|
|
t.Fatalf("expected sheet-id error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSheetCopySheetDryRun(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rt := newDimTestRuntime(t,
|
|
map[string]string{"spreadsheet-token": "shtTOKEN", "sheet-id": "sheet1", "title": "Copy"},
|
|
map[string]int{"index": 2}, nil)
|
|
got := mustMarshalSheetsDryRun(t, SheetCopySheet.DryRun(context.Background(), rt))
|
|
if !strings.Contains(got, `"/open-apis/sheets/v2/spreadsheets/shtTOKEN/sheets_batch_update"`) {
|
|
t.Fatalf("DryRun URL mismatch: %s", got)
|
|
}
|
|
if !strings.Contains(got, `"copySheet"`) || !strings.Contains(got, `"sheetId":"sheet1"`) || !strings.Contains(got, `"title":"Copy"`) {
|
|
t.Fatalf("DryRun body mismatch: %s", got)
|
|
}
|
|
if !strings.Contains(got, `"[2] Move copied sheet to requested index"`) || !strings.Contains(got, `\u003ccopied_sheet_id\u003e`) || !strings.Contains(got, `"index":2`) {
|
|
t.Fatalf("DryRun should describe follow-up move: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestSheetCopySheetExecuteSuccess(t *testing.T) {
|
|
f, stdout, _, reg := cmdutil.TestFactory(t, sheetsTestConfig())
|
|
copyStub := &httpmock.Stub{
|
|
Method: "POST",
|
|
URL: "/open-apis/sheets/v2/spreadsheets/shtTOKEN/sheets_batch_update",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "success",
|
|
"data": map[string]interface{}{
|
|
"replies": []interface{}{
|
|
map[string]interface{}{
|
|
"copySheet": map[string]interface{}{
|
|
"properties": map[string]interface{}{
|
|
"sheetId": "sheet_copy",
|
|
"title": "Copy",
|
|
"index": 1,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
moveStub := &httpmock.Stub{
|
|
Method: "POST",
|
|
URL: "/open-apis/sheets/v2/spreadsheets/shtTOKEN/sheets_batch_update",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "success",
|
|
"data": map[string]interface{}{
|
|
"replies": []interface{}{
|
|
map[string]interface{}{
|
|
"updateSheet": map[string]interface{}{
|
|
"properties": map[string]interface{}{
|
|
"sheetId": "sheet_copy",
|
|
"index": 2,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
reg.Register(copyStub)
|
|
reg.Register(moveStub)
|
|
|
|
err := mountAndRunSheets(t, SheetCopySheet, []string{
|
|
"+copy-sheet",
|
|
"--spreadsheet-token", "shtTOKEN",
|
|
"--sheet-id", "sheet1",
|
|
"--title", "Copy",
|
|
"--index", "2",
|
|
"--as", "user",
|
|
}, f, stdout)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if gjson.Get(stdout.String(), "data.sheet_id").String() != "sheet_copy" {
|
|
t.Fatalf("stdout missing copied sheet id: %s", stdout.String())
|
|
}
|
|
if gjson.Get(stdout.String(), "data.sheet.index").Int() != 2 {
|
|
t.Fatalf("stdout missing moved index: %s", stdout.String())
|
|
}
|
|
|
|
var copyBody map[string]interface{}
|
|
if err := json.Unmarshal(copyStub.CapturedBody, ©Body); err != nil {
|
|
t.Fatalf("parse copy body: %v", err)
|
|
}
|
|
if !strings.Contains(string(copyStub.CapturedBody), `"copySheet"`) {
|
|
t.Fatalf("copy request missing copySheet: %s", string(copyStub.CapturedBody))
|
|
}
|
|
if !strings.Contains(string(moveStub.CapturedBody), `"updateSheet"`) || !strings.Contains(string(moveStub.CapturedBody), `"index":2`) {
|
|
t.Fatalf("move request mismatch: %s", string(moveStub.CapturedBody))
|
|
}
|
|
}
|
|
|
|
func TestSheetCopySheetExecuteMoveFailureIncludesCopiedSheetRecovery(t *testing.T) {
|
|
f, _, _, reg := cmdutil.TestFactory(t, sheetsTestConfig())
|
|
reg.Register(&httpmock.Stub{
|
|
Method: "POST",
|
|
URL: "/open-apis/sheets/v2/spreadsheets/shtTOKEN/sheets_batch_update",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "success",
|
|
"data": map[string]interface{}{
|
|
"replies": []interface{}{
|
|
map[string]interface{}{
|
|
"copySheet": map[string]interface{}{
|
|
"properties": map[string]interface{}{
|
|
"sheetId": "sheet_copy",
|
|
"title": "Copy",
|
|
"index": 1,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
reg.Register(&httpmock.Stub{
|
|
Method: "POST",
|
|
URL: "/open-apis/sheets/v2/spreadsheets/shtTOKEN/sheets_batch_update",
|
|
Status: 400,
|
|
Body: map[string]interface{}{
|
|
"code": 1310211,
|
|
"msg": "wrong sheet id",
|
|
"error": map[string]interface{}{
|
|
"log_id": "log-move-failed",
|
|
},
|
|
},
|
|
})
|
|
|
|
err := mountAndRunSheets(t, SheetCopySheet, []string{
|
|
"+copy-sheet",
|
|
"--spreadsheet-token", "shtTOKEN",
|
|
"--sheet-id", "sheet1",
|
|
"--title", "Copy",
|
|
"--index", "2",
|
|
"--as", "user",
|
|
}, f, nil)
|
|
if err == nil {
|
|
t.Fatal("expected move failure, got nil")
|
|
}
|
|
|
|
var exitErr *output.ExitError
|
|
if !errors.As(err, &exitErr) || exitErr.Detail == nil {
|
|
t.Fatalf("expected *output.ExitError with detail, got %T: %v", err, err)
|
|
}
|
|
if exitErr.Detail.Code != 1310211 {
|
|
t.Fatalf("error code = %d, want 1310211", exitErr.Detail.Code)
|
|
}
|
|
if !strings.Contains(exitErr.Detail.Message, `sheet copied successfully as "sheet_copy"`) {
|
|
t.Fatalf("message missing copied sheet id: %q", exitErr.Detail.Message)
|
|
}
|
|
if !strings.Contains(exitErr.Detail.Hint, "do not retry +copy-sheet") {
|
|
t.Fatalf("hint missing retry guard: %q", exitErr.Detail.Hint)
|
|
}
|
|
if !strings.Contains(exitErr.Detail.Hint, "+update-sheet --spreadsheet-token shtTOKEN --sheet-id sheet_copy --index 2") {
|
|
t.Fatalf("hint missing recovery command: %q", exitErr.Detail.Hint)
|
|
}
|
|
|
|
detail, _ := exitErr.Detail.Detail.(map[string]interface{})
|
|
if detail["partial_success"] != true {
|
|
t.Fatalf("partial_success = %#v, want true", detail["partial_success"])
|
|
}
|
|
if detail["sheet_id"] != "sheet_copy" {
|
|
t.Fatalf("sheet_id = %#v, want %q", detail["sheet_id"], "sheet_copy")
|
|
}
|
|
if detail["requested_index"] != 2 {
|
|
t.Fatalf("requested_index = %#v, want 2", detail["requested_index"])
|
|
}
|
|
if detail["retry_command"] != "lark-cli sheets +update-sheet --spreadsheet-token shtTOKEN --sheet-id sheet_copy --index 2" {
|
|
t.Fatalf("retry_command = %#v", detail["retry_command"])
|
|
}
|
|
if detail["log_id"] != "log-move-failed" {
|
|
t.Fatalf("log_id = %#v, want %q", detail["log_id"], "log-move-failed")
|
|
}
|
|
}
|
|
|
|
func TestSheetDeleteSheetDryRun(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rt := newDimTestRuntime(t,
|
|
map[string]string{"spreadsheet-token": "shtTOKEN", "sheet-id": "sheet1"},
|
|
nil, nil)
|
|
got := mustMarshalSheetsDryRun(t, SheetDeleteSheet.DryRun(context.Background(), rt))
|
|
if !strings.Contains(got, `"method":"POST"`) {
|
|
t.Fatalf("DryRun should use POST: %s", got)
|
|
}
|
|
if !strings.Contains(got, `"/open-apis/sheets/v2/spreadsheets/shtTOKEN/sheets_batch_update"`) {
|
|
t.Fatalf("DryRun URL mismatch: %s", got)
|
|
}
|
|
if !strings.Contains(got, `"deleteSheet"`) || !strings.Contains(got, `"sheetId":"sheet1"`) {
|
|
t.Fatalf("DryRun body mismatch: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestSheetDeleteSheetExecuteSuccess(t *testing.T) {
|
|
f, stdout, _, reg := cmdutil.TestFactory(t, sheetsTestConfig())
|
|
reg.Register(&httpmock.Stub{
|
|
Method: "POST",
|
|
URL: "/open-apis/sheets/v2/spreadsheets/shtTOKEN/sheets_batch_update",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "success",
|
|
"data": map[string]interface{}{
|
|
"replies": []interface{}{
|
|
map[string]interface{}{
|
|
"deleteSheet": map[string]interface{}{
|
|
"result": true,
|
|
"sheetId": "sheet1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
err := mountAndRunSheets(t, SheetDeleteSheet, []string{
|
|
"+delete-sheet",
|
|
"--spreadsheet-token", "shtTOKEN",
|
|
"--sheet-id", "sheet1",
|
|
"--yes",
|
|
"--as", "user",
|
|
}, f, stdout)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if !gjson.Get(stdout.String(), "data.deleted").Bool() {
|
|
t.Fatalf("stdout missing deleted=true: %s", stdout.String())
|
|
}
|
|
if gjson.Get(stdout.String(), "data.sheet_id").String() != "sheet1" {
|
|
t.Fatalf("stdout missing sheet_id: %s", stdout.String())
|
|
}
|
|
}
|
|
|
|
func TestSheetUpdateSheetValidateRequiresMutation(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rt := newDimTestRuntime(t,
|
|
map[string]string{"spreadsheet-token": "shtTOKEN", "sheet-id": "sheet1"},
|
|
nil, nil)
|
|
err := SheetUpdateSheet.Validate(context.Background(), rt)
|
|
if err == nil || !strings.Contains(err.Error(), "specify at least one") {
|
|
t.Fatalf("expected mutation error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSheetUpdateSheetValidateRejectsBadProtectionConfig(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
strFlags map[string]string
|
|
intFlags map[string]int
|
|
wantSubst string
|
|
}{
|
|
{
|
|
name: "lock-info requires lock",
|
|
strFlags: map[string]string{
|
|
"spreadsheet-token": "shtTOKEN", "sheet-id": "sheet1", "lock-info": "private",
|
|
},
|
|
wantSubst: "--lock when updating protection settings",
|
|
},
|
|
{
|
|
name: "user-ids requires user-id-type",
|
|
strFlags: map[string]string{
|
|
"spreadsheet-token": "shtTOKEN", "sheet-id": "sheet1", "lock": "LOCK",
|
|
"user-ids": `["ou_1"]`,
|
|
},
|
|
wantSubst: "--user-ids requires --user-id-type",
|
|
},
|
|
{
|
|
name: "negative frozen rows rejected",
|
|
strFlags: map[string]string{
|
|
"spreadsheet-token": "shtTOKEN", "sheet-id": "sheet1",
|
|
},
|
|
intFlags: map[string]int{"frozen-row-count": -1},
|
|
wantSubst: "--frozen-row-count must be >= 0",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
rt := newDimTestRuntime(t, tt.strFlags, tt.intFlags, nil)
|
|
err := SheetUpdateSheet.Validate(context.Background(), rt)
|
|
if err == nil || !strings.Contains(err.Error(), tt.wantSubst) {
|
|
t.Fatalf("want error containing %q, got: %v", tt.wantSubst, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSheetUpdateSheetDryRun(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rt := newDimTestRuntime(t,
|
|
map[string]string{
|
|
"spreadsheet-token": "shtTOKEN",
|
|
"sheet-id": "sheet1",
|
|
"title": "Hidden Sheet",
|
|
"lock": "LOCK",
|
|
"lock-info": "private",
|
|
"user-ids": `["ou_1"]`,
|
|
"user-id-type": "open_id",
|
|
},
|
|
map[string]int{
|
|
"index": 3,
|
|
"frozen-row-count": 2,
|
|
"frozen-col-count": 1,
|
|
},
|
|
map[string]bool{"hidden": false},
|
|
)
|
|
got := mustMarshalSheetsDryRun(t, SheetUpdateSheet.DryRun(context.Background(), rt))
|
|
for _, want := range []string{
|
|
`"/open-apis/sheets/v2/spreadsheets/shtTOKEN/sheets_batch_update"`,
|
|
`"user_id_type":"open_id"`,
|
|
`"sheetId":"sheet1"`,
|
|
`"title":"Hidden Sheet"`,
|
|
`"index":3`,
|
|
`"hidden":false`,
|
|
`"frozenRowCount":2`,
|
|
`"frozenColCount":1`,
|
|
`"lock":"LOCK"`,
|
|
`"lockInfo":"private"`,
|
|
`"userIDs":["ou_1"]`,
|
|
} {
|
|
if !strings.Contains(got, want) {
|
|
t.Fatalf("DryRun missing %s: %s", want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSheetUpdateSheetExecuteSuccess(t *testing.T) {
|
|
f, stdout, _, reg := cmdutil.TestFactory(t, sheetsTestConfig())
|
|
stub := &httpmock.Stub{
|
|
Method: "POST",
|
|
URL: "/open-apis/sheets/v2/spreadsheets/shtTOKEN/sheets_batch_update?user_id_type=open_id",
|
|
Body: map[string]interface{}{
|
|
"code": 0,
|
|
"msg": "success",
|
|
"data": map[string]interface{}{
|
|
"replies": []interface{}{
|
|
map[string]interface{}{
|
|
"updateSheet": map[string]interface{}{
|
|
"properties": map[string]interface{}{
|
|
"sheetId": "sheet1",
|
|
"title": "Renamed",
|
|
"index": 1,
|
|
"hidden": true,
|
|
"frozenRowCount": 2,
|
|
"frozenColCount": 1,
|
|
"protect": map[string]interface{}{
|
|
"lock": "LOCK",
|
|
"lockInfo": "private",
|
|
"userIDs": []interface{}{"ou_1"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
reg.Register(stub)
|
|
|
|
err := mountAndRunSheets(t, SheetUpdateSheet, []string{
|
|
"+update-sheet",
|
|
"--spreadsheet-token", "shtTOKEN",
|
|
"--sheet-id", "sheet1",
|
|
"--title", "Renamed",
|
|
"--index", "1",
|
|
"--hidden=true",
|
|
"--frozen-row-count", "2",
|
|
"--frozen-col-count", "1",
|
|
"--lock", "LOCK",
|
|
"--lock-info", "private",
|
|
"--user-ids", `["ou_1"]`,
|
|
"--user-id-type", "open_id",
|
|
"--as", "user",
|
|
}, f, stdout)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if gjson.Get(stdout.String(), "data.sheet_id").String() != "sheet1" {
|
|
t.Fatalf("stdout missing sheet_id: %s", stdout.String())
|
|
}
|
|
if gjson.Get(stdout.String(), "data.sheet.title").String() != "Renamed" {
|
|
t.Fatalf("stdout missing title: %s", stdout.String())
|
|
}
|
|
if gjson.Get(stdout.String(), "data.sheet.grid_properties.frozen_row_count").Int() != 2 {
|
|
t.Fatalf("stdout missing frozen_row_count: %s", stdout.String())
|
|
}
|
|
if gjson.Get(stdout.String(), "data.sheet.protect.lock_info").String() != "private" {
|
|
t.Fatalf("stdout missing lock_info: %s", stdout.String())
|
|
}
|
|
|
|
var body map[string]interface{}
|
|
if err := json.Unmarshal(stub.CapturedBody, &body); err != nil {
|
|
t.Fatalf("parse body: %v", err)
|
|
}
|
|
requests, ok := body["requests"].([]interface{})
|
|
if !ok || len(requests) != 1 {
|
|
t.Fatalf("unexpected requests body: %#v", body)
|
|
}
|
|
req0, _ := requests[0].(map[string]interface{})
|
|
updateSheet, _ := req0["updateSheet"].(map[string]interface{})
|
|
props, _ := updateSheet["properties"].(map[string]interface{})
|
|
if props["sheetId"] != "sheet1" || props["title"] != "Renamed" {
|
|
t.Fatalf("unexpected properties: %#v", props)
|
|
}
|
|
}
|
|
|
|
func TestBuildUpdateSheetOutputOmitsBlankTitleWhenTitleNotChanged(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
out, ok := buildUpdateSheetOutput("shtTOKEN", map[string]interface{}{
|
|
"replies": []interface{}{
|
|
map[string]interface{}{
|
|
"updateSheet": map[string]interface{}{
|
|
"properties": map[string]interface{}{
|
|
"sheetId": "sheet1",
|
|
"title": "",
|
|
"hidden": false,
|
|
"frozenRowCount": 0,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, false)
|
|
if !ok {
|
|
t.Fatal("expected output")
|
|
}
|
|
sheet, _ := out["sheet"].(map[string]interface{})
|
|
if _, exists := sheet["title"]; exists {
|
|
t.Fatalf("blank title should be omitted when title is unchanged: %#v", sheet)
|
|
}
|
|
if sheet["sheet_id"] != "sheet1" {
|
|
t.Fatalf("unexpected sheet output: %#v", sheet)
|
|
}
|
|
}
|