mirror of
https://github.com/larksuite/cli.git
synced 2026-07-06 16:18:05 +08:00
217 lines
6.5 KiB
Go
217 lines
6.5 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package schema
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/larksuite/cli/internal/cmdutil"
|
|
"github.com/larksuite/cli/internal/core"
|
|
)
|
|
|
|
func TestSchemaCmd_FlagParsing(t *testing.T) {
|
|
f, _, _, _ := cmdutil.TestFactory(t, nil)
|
|
|
|
var gotOpts *SchemaOptions
|
|
cmd := NewCmdSchema(f, func(opts *SchemaOptions) error {
|
|
gotOpts = opts
|
|
return nil
|
|
})
|
|
cmd.SetArgs([]string{"calendar.events.list"})
|
|
err := cmd.Execute()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(gotOpts.Args) != 1 || gotOpts.Args[0] != "calendar.events.list" {
|
|
t.Errorf("expected args [calendar.events.list], got %v", gotOpts.Args)
|
|
}
|
|
}
|
|
|
|
func TestSchemaCmd_OutputFlagsAcceptedForCompat(t *testing.T) {
|
|
// Agents are habituated to --format/--json/--as from api/service commands.
|
|
// schema must accept them without erroring and always emit the JSON envelope —
|
|
// its output is structured JSON and identity-independent, so the values have
|
|
// no effect.
|
|
argSets := [][]string{
|
|
{"--format", "json"},
|
|
{"--format", "pretty"},
|
|
{"--format", "table"}, // no table rendering for a nested schema -> JSON
|
|
{"--format", "csv"},
|
|
{"--json"},
|
|
{"--json", "--format", "ndjson"},
|
|
{"--as", "user"},
|
|
{"--as", "bot"},
|
|
{"--as", "user", "--json"},
|
|
}
|
|
for _, extra := range argSets {
|
|
f, stdout, _, _ := cmdutil.TestFactory(t, nil)
|
|
cmd := NewCmdSchema(f, nil)
|
|
cmd.SetArgs(append([]string{"im.images.create"}, extra...))
|
|
if err := cmd.Execute(); err != nil {
|
|
t.Fatalf("args %v should be accepted, got error: %v", extra, err)
|
|
}
|
|
var env map[string]interface{}
|
|
if err := json.Unmarshal(stdout.Bytes(), &env); err != nil {
|
|
t.Fatalf("args %v: output is not a JSON envelope: %v\n%s", extra, err, stdout.String())
|
|
}
|
|
if env["name"] != "im images create" {
|
|
t.Errorf("args %v: expected the im images create envelope, got name=%v", extra, env["name"])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSchemaCmd_NoArgs_JSON_IsArray(t *testing.T) {
|
|
f, stdout, _, _ := cmdutil.TestFactory(t, nil)
|
|
|
|
cmd := NewCmdSchema(f, nil)
|
|
cmd.SetArgs([]string{})
|
|
if err := cmd.Execute(); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
out := strings.TrimSpace(stdout.String())
|
|
if !strings.HasPrefix(out, "[") {
|
|
head := out
|
|
if len(head) > 80 {
|
|
head = head[:80]
|
|
}
|
|
t.Errorf("expected JSON array root, first 80 chars:\n%s", head)
|
|
}
|
|
var envs []map[string]interface{}
|
|
if err := json.Unmarshal([]byte(out), &envs); err != nil {
|
|
t.Fatalf("unmarshal failed: %v", err)
|
|
}
|
|
if len(envs) < 193 {
|
|
t.Errorf("envelopes count = %d, want >= 193", len(envs))
|
|
}
|
|
}
|
|
|
|
func TestSchemaCmd_JSONIsEnvelope(t *testing.T) {
|
|
f, stdout, _, _ := cmdutil.TestFactory(t, nil)
|
|
|
|
cmd := NewCmdSchema(f, nil)
|
|
cmd.SetArgs([]string{"im.images.create"})
|
|
if err := cmd.Execute(); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
var env map[string]interface{}
|
|
if err := json.Unmarshal(stdout.Bytes(), &env); err != nil {
|
|
t.Fatalf("not valid JSON: %v\n%s", err, stdout.String())
|
|
}
|
|
if env["name"] != "im images create" {
|
|
t.Errorf("name = %v, want \"im images create\"", env["name"])
|
|
}
|
|
for _, key := range []string{"description", "inputSchema", "outputSchema", "_meta"} {
|
|
if _, ok := env[key]; !ok {
|
|
t.Errorf("missing top-level key: %s", key)
|
|
}
|
|
}
|
|
meta, _ := env["_meta"].(map[string]interface{})
|
|
if meta["envelope_version"] != "1.0" {
|
|
t.Errorf("envelope_version = %v, want \"1.0\"", meta["envelope_version"])
|
|
}
|
|
}
|
|
|
|
func TestSchemaCmd_SpaceSeparatedPath_EqualsDotted(t *testing.T) {
|
|
f1, out1, _, _ := cmdutil.TestFactory(t, nil)
|
|
cmd1 := NewCmdSchema(f1, nil)
|
|
cmd1.SetArgs([]string{"im", "images", "create"})
|
|
if err := cmd1.Execute(); err != nil {
|
|
t.Fatalf("space form failed: %v", err)
|
|
}
|
|
|
|
f2, out2, _, _ := cmdutil.TestFactory(t, nil)
|
|
cmd2 := NewCmdSchema(f2, nil)
|
|
cmd2.SetArgs([]string{"im.images.create"})
|
|
if err := cmd2.Execute(); err != nil {
|
|
t.Fatalf("dotted form failed: %v", err)
|
|
}
|
|
|
|
if out1.String() != out2.String() {
|
|
t.Errorf("space and dotted forms produced different output")
|
|
}
|
|
}
|
|
|
|
func TestSchemaCmd_ServiceListIsArray(t *testing.T) {
|
|
f, stdout, _, _ := cmdutil.TestFactory(t, nil)
|
|
|
|
cmd := NewCmdSchema(f, nil)
|
|
cmd.SetArgs([]string{"im"})
|
|
if err := cmd.Execute(); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
var envs []map[string]interface{}
|
|
if err := json.Unmarshal(stdout.Bytes(), &envs); err != nil {
|
|
t.Fatalf("unmarshal failed: %v\n%s", err, stdout.String())
|
|
}
|
|
if len(envs) == 0 {
|
|
t.Fatal("expected non-empty array for service im")
|
|
}
|
|
for _, e := range envs {
|
|
name, _ := e["name"].(string)
|
|
if !strings.HasPrefix(name, "im ") {
|
|
t.Errorf("envelope name %q does not start with \"im \"", name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSchemaCmd_HighRiskYesInjection(t *testing.T) {
|
|
f, stdout, _, _ := cmdutil.TestFactory(t, nil)
|
|
|
|
cmd := NewCmdSchema(f, nil)
|
|
cmd.SetArgs([]string{"im.messages.delete"})
|
|
if err := cmd.Execute(); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
var env map[string]interface{}
|
|
if err := json.Unmarshal(stdout.Bytes(), &env); err != nil {
|
|
t.Fatalf("unmarshal failed: %v", err)
|
|
}
|
|
is, _ := env["inputSchema"].(map[string]interface{})
|
|
props, _ := is["properties"].(map[string]interface{})
|
|
if _, ok := props["yes"]; !ok {
|
|
t.Errorf("inputSchema.properties.yes missing for high-risk-write command")
|
|
}
|
|
}
|
|
|
|
func TestSchemaCmd_NoYesForReadRisk(t *testing.T) {
|
|
f, stdout, _, _ := cmdutil.TestFactory(t, nil)
|
|
|
|
cmd := NewCmdSchema(f, nil)
|
|
cmd.SetArgs([]string{"im.reactions.list"})
|
|
if err := cmd.Execute(); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
var env map[string]interface{}
|
|
if err := json.Unmarshal(stdout.Bytes(), &env); err != nil {
|
|
t.Fatalf("unmarshal failed: %v", err)
|
|
}
|
|
is, _ := env["inputSchema"].(map[string]interface{})
|
|
props, _ := is["properties"].(map[string]interface{})
|
|
if _, ok := props["yes"]; ok {
|
|
t.Errorf("yes property should not appear for risk=read command")
|
|
}
|
|
}
|
|
|
|
func TestSchemaCmd_UnknownService(t *testing.T) {
|
|
f, _, _, _ := cmdutil.TestFactory(t, &core.CliConfig{
|
|
AppID: "test-app", AppSecret: "test-secret", Brand: core.BrandFeishu,
|
|
})
|
|
|
|
cmd := NewCmdSchema(f, nil)
|
|
cmd.SetArgs([]string{"nonexistent_service"})
|
|
err := cmd.Execute()
|
|
if err == nil {
|
|
t.Error("expected error for unknown service")
|
|
}
|
|
if !strings.Contains(err.Error(), "Unknown service") {
|
|
t.Errorf("expected 'Unknown service' error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
// Completion candidate generation (dotted + space forms, strict-mode filtering,
|
|
// dotted-resource handling) now lives in internal/apicatalog and is covered by
|
|
// apicatalog's TestComplete. cmd/schema only adapts catalog.Complete to cobra.
|