mirror of
https://github.com/larksuite/cli.git
synced 2026-07-06 16:18:05 +08:00
Input pre-check failures shared by every shortcut — @file/stdin input resolution, enum validation, and unsupported --dry-run — now leave the CLI as typed validation envelopes naming the offending flag, so scripts and AI agents can branch on `param` instead of parsing prose. Wire type, exit code, and message text are unchanged; the new fields are additive. The shared layer also gains typed replacements for its legacy error-producing helpers, so each business domain can migrate to typed errors without rebuilding common plumbing, and a path-scoped lint guard keeps migrated domains from sliding back. Changes: - Shared pre-check failures (input flags, enum values, dry-run support) return typed validation errors carrying the offending flag as `param`. - Every legacy error-producing helper in shortcuts/common has a typed replacement that preserves the existing message text: validation and flag-group checks, chat/user ID validation (callers name the flag so `param` is ground truth), "me" open-id resolution, safe-path checks, input-stat and save-error wrapping. Legacy helpers stay for not-yet-migrated domains, marked deprecated — including the legacy API-result classifier, whose typed route is runtime.CallAPITyped. - A new errscontract rule rejects legacy common-helper calls on migrated paths, so a migrated domain cannot silently reintroduce legacy envelopes; drive is the first locked path and its last legacy ID-helper calls are replaced.
99 lines
3.1 KiB
Go
99 lines
3.1 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package common
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/larksuite/cli/internal/core"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func resolveOpenIDsTestRuntime(userOpenID string) *RuntimeContext {
|
|
cmd := &cobra.Command{Use: "test"}
|
|
cfg := &core.CliConfig{UserOpenId: userOpenID}
|
|
return TestNewRuntimeContext(cmd, cfg)
|
|
}
|
|
|
|
func TestResolveOpenIDs_Empty(t *testing.T) {
|
|
rt := resolveOpenIDsTestRuntime("ou_self")
|
|
out, err := ResolveOpenIDs("--user-ids", nil, rt)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(out) != 0 {
|
|
t.Fatalf("expected empty, got %v", out)
|
|
}
|
|
}
|
|
|
|
func TestResolveOpenIDs_ExpandsMeAndDedups(t *testing.T) {
|
|
rt := resolveOpenIDsTestRuntime("ou_self")
|
|
out, err := ResolveOpenIDs("--user-ids", []string{"me", "ou_a", "me", "ou_a"}, rt)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
want := []string{"ou_self", "ou_a"}
|
|
if len(out) != len(want) || out[0] != want[0] || out[1] != want[1] {
|
|
t.Fatalf("got %v, want %v", out, want)
|
|
}
|
|
}
|
|
|
|
func TestResolveOpenIDs_MeIsCaseInsensitive(t *testing.T) {
|
|
rt := resolveOpenIDsTestRuntime("ou_self")
|
|
out, err := ResolveOpenIDs("--user-ids", []string{"ou_other", "me", "Me", "ME"}, rt)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
want := []string{"ou_other", "ou_self"}
|
|
if len(out) != len(want) || out[0] != want[0] || out[1] != want[1] {
|
|
t.Fatalf("got %v, want %v", out, want)
|
|
}
|
|
}
|
|
|
|
func TestResolveOpenIDs_MeWithoutLogin(t *testing.T) {
|
|
rt := resolveOpenIDsTestRuntime("")
|
|
_, err := ResolveOpenIDs("--user-ids", []string{"me"}, rt)
|
|
if err == nil {
|
|
t.Fatal("expected validation error")
|
|
}
|
|
if !strings.Contains(err.Error(), "--user-ids") {
|
|
t.Fatalf("error should mention the offending flag name; got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestResolveOpenIDs_DedupIsCaseInsensitive(t *testing.T) {
|
|
rt := resolveOpenIDsTestRuntime("ou_self")
|
|
// Same underlying open_id with three case variants — should collapse to
|
|
// one entry, preserving the first-occurrence form.
|
|
out, err := ResolveOpenIDs("--user-ids", []string{"ou_abc123", "OU_ABC123", "Ou_Abc123"}, rt)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(out) != 1 || out[0] != "ou_abc123" {
|
|
t.Fatalf("case-insensitive dedup failed: got %v, want [ou_abc123]", out)
|
|
}
|
|
}
|
|
|
|
func TestResolveOpenIDsTyped_MeWithoutLogin_ReturnsTypedValidation(t *testing.T) {
|
|
rt := resolveOpenIDsTestRuntime("")
|
|
_, err := ResolveOpenIDsTyped("--user-ids", []string{"me"}, rt)
|
|
validationErr := assertValidationParam(t, err, "--user-ids")
|
|
if !strings.Contains(validationErr.Message, "--user-ids") {
|
|
t.Fatalf("error should mention the offending flag name; got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestResolveOpenIDsTyped_ExpandsMeAndDedups(t *testing.T) {
|
|
rt := resolveOpenIDsTestRuntime("ou_self")
|
|
out, err := ResolveOpenIDsTyped("--user-ids", []string{"me", "ou_a", "me", "ou_a"}, rt)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
want := []string{"ou_self", "ou_a"}
|
|
if len(out) != len(want) || out[0] != want[0] || out[1] != want[1] {
|
|
t.Fatalf("got %v, want %v", out, want)
|
|
}
|
|
}
|