mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 22:24:31 +08:00
Every failure on the authentication, authorization, and configuration
path now surfaces as a typed structured error instead of an ad-hoc
envelope. Users and scripts that consume CLI output get:
- a fixed nine-category taxonomy on the wire, each mapped to a
stable shell exit code (authentication/authorization/config = 3,
network = 4, internal = 5, policy = 6, confirmation = 10)
- identity-aware detail fields (missing_scopes, requested_scopes,
granted_scopes, console_url, log_id, retryable, hint) carried
uniformly on the envelope
- a single canonical policy envelope at exit 6; the legacy
auth_error carve-out is retired
- per-subtype canonical message + hint that preserves Lark's
diagnostic phrasing and routes recovery to the right actor:
app developer (app_scope_not_applied), user (missing_scope,
token_scope_insufficient, user_unauthorized), or tenant admin
(app_unavailable, app_disabled)
- wrong app credentials classify as config/invalid_client whether
surfaced by the Open API endpoint (99991543) or the tenant
access-token mint endpoint (10003 / 10014), instead of
collapsing to a transport error or api/unknown
- local shortcut scope preflight emits the same
authorization/missing_scope envelope (identity + deterministic
missing-scope set) used by the post-call permission path, so AI
consumers read the same structured shape from precheck and from
server-returned permission denial
- streaming download/upload failures keep the same network subtype
split (timeout / TLS / DNS / transport) as the non-stream path
instead of collapsing every cause to a generic transport failure
- console_url is carried only on the bot-perspective
app_scope_not_applied envelope (where the recovery action is
"developer applies the scope at the developer console"); the
user-perspective missing_scope envelope drops the field, since
the only actionable user recovery is `lark-cli auth login --scope`
and pointing an end user at a console they cannot modify is
misleading
- bind workflows (Hermes / OpenClaw / lark-channel) flatten dynamic
Type tags to wire 'config' with the original module name kept
as a metric label
All 10 typed errors are cause-bearing, nil-safe on .Error() and
.Unwrap(), and defensively clone slice setter inputs. Four lint
rules (CheckNilSafeError / CheckBuilderImmutable / CheckUnwrapSymmetry
/ CheckBuildAPIErrorArms) lock these invariants on migrated paths.
473 lines
15 KiB
Go
473 lines
15 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package cmdutil
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/larksuite/cli/errs"
|
|
extcred "github.com/larksuite/cli/extension/credential"
|
|
"github.com/larksuite/cli/internal/core"
|
|
"github.com/larksuite/cli/internal/credential"
|
|
"github.com/larksuite/cli/internal/envvars"
|
|
"github.com/larksuite/cli/internal/output"
|
|
)
|
|
|
|
// newCmdWithAsFlag creates a cobra.Command with a --as string flag for testing.
|
|
func newCmdWithAsFlag(asValue string, changed bool) *cobra.Command {
|
|
cmd := &cobra.Command{Use: "test"}
|
|
cmd.Flags().String("as", "auto", "identity")
|
|
if changed {
|
|
_ = cmd.Flags().Set("as", asValue)
|
|
}
|
|
return cmd
|
|
}
|
|
|
|
// --- ResolveAs tests ---
|
|
|
|
func TestResolveAs_ExplicitAs(t *testing.T) {
|
|
f, _, _, _ := TestFactory(t, &core.CliConfig{AppID: "a", AppSecret: "s"})
|
|
cmd := newCmdWithAsFlag("bot", true)
|
|
|
|
got := f.ResolveAs(context.Background(), cmd, core.AsBot)
|
|
if got != core.AsBot {
|
|
t.Errorf("want bot, got %s", got)
|
|
}
|
|
if f.IdentityAutoDetected {
|
|
t.Error("IdentityAutoDetected should be false for explicit --as")
|
|
}
|
|
if f.ResolvedIdentity != core.AsBot {
|
|
t.Errorf("ResolvedIdentity want bot, got %s", f.ResolvedIdentity)
|
|
}
|
|
}
|
|
|
|
func TestResolveAs_ExplicitAsUser(t *testing.T) {
|
|
f, _, _, _ := TestFactory(t, &core.CliConfig{AppID: "a", AppSecret: "s"})
|
|
cmd := newCmdWithAsFlag("user", true)
|
|
|
|
got := f.ResolveAs(context.Background(), cmd, core.AsUser)
|
|
if got != core.AsUser {
|
|
t.Errorf("want user, got %s", got)
|
|
}
|
|
if f.ResolvedIdentity != core.AsUser {
|
|
t.Errorf("ResolvedIdentity want user, got %s", f.ResolvedIdentity)
|
|
}
|
|
}
|
|
|
|
func TestResolveAs_ExplicitAuto_FallsToAutoDetect(t *testing.T) {
|
|
// --as auto explicitly: should fall through to auto-detect
|
|
// Config has no UserOpenId → auto-detect returns bot
|
|
f, _, _, _ := TestFactory(t, &core.CliConfig{AppID: "a", AppSecret: "s"})
|
|
cmd := newCmdWithAsFlag("auto", true)
|
|
|
|
got := f.ResolveAs(context.Background(), cmd, "auto")
|
|
if got != core.AsBot {
|
|
t.Errorf("want bot (auto-detect, no login), got %s", got)
|
|
}
|
|
if !f.IdentityAutoDetected {
|
|
t.Error("IdentityAutoDetected should be true for auto-detect path")
|
|
}
|
|
}
|
|
|
|
func TestResolveAs_DefaultAs_FromConfig(t *testing.T) {
|
|
f, _, _, _ := TestFactory(t, &core.CliConfig{
|
|
AppID: "a", AppSecret: "s",
|
|
DefaultAs: "bot",
|
|
})
|
|
cmd := newCmdWithAsFlag("auto", false) // --as not changed
|
|
|
|
got := f.ResolveAs(context.Background(), cmd, "auto")
|
|
if got != core.AsBot {
|
|
t.Errorf("want bot (from default-as config), got %s", got)
|
|
}
|
|
if f.IdentityAutoDetected {
|
|
t.Error("IdentityAutoDetected should be false for default-as path")
|
|
}
|
|
}
|
|
|
|
func TestResolveAs_DefaultAs_EnvDoesNotBypassConfigSource(t *testing.T) {
|
|
t.Setenv(envvars.CliDefaultAs, "user")
|
|
|
|
f, _, _, _ := TestFactory(t, &core.CliConfig{AppID: "a", AppSecret: "s"})
|
|
cmd := newCmdWithAsFlag("auto", false)
|
|
|
|
got := f.ResolveAs(context.Background(), cmd, "auto")
|
|
if got != core.AsBot {
|
|
t.Errorf("want bot (env default-as should not bypass config source), got %s", got)
|
|
}
|
|
if !f.IdentityAutoDetected {
|
|
t.Error("IdentityAutoDetected should be true when no account default-as is set")
|
|
}
|
|
}
|
|
|
|
func TestResolveAs_DefaultAs_AutoValue_FallsToAutoDetect(t *testing.T) {
|
|
// default-as = "auto" should fall through to auto-detect
|
|
f, _, _, _ := TestFactory(t, &core.CliConfig{
|
|
AppID: "a", AppSecret: "s",
|
|
DefaultAs: "auto",
|
|
})
|
|
cmd := newCmdWithAsFlag("auto", false)
|
|
|
|
got := f.ResolveAs(context.Background(), cmd, "auto")
|
|
// No UserOpenId → auto-detect returns bot
|
|
if got != core.AsBot {
|
|
t.Errorf("want bot (auto-detect), got %s", got)
|
|
}
|
|
if !f.IdentityAutoDetected {
|
|
t.Error("IdentityAutoDetected should be true")
|
|
}
|
|
}
|
|
|
|
func TestResolveAs_NilCmd_AutoDetect(t *testing.T) {
|
|
f, _, _, _ := TestFactory(t, &core.CliConfig{AppID: "a", AppSecret: "s"})
|
|
|
|
got := f.ResolveAs(context.Background(), nil, "auto")
|
|
if got != core.AsBot {
|
|
t.Errorf("want bot, got %s", got)
|
|
}
|
|
}
|
|
|
|
// --- CheckIdentity tests ---
|
|
|
|
func TestCheckIdentity_Supported(t *testing.T) {
|
|
f, _, _, _ := TestFactory(t, &core.CliConfig{AppID: "a", AppSecret: "s"})
|
|
|
|
err := f.CheckIdentity(core.AsBot, []string{"bot", "user"})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if f.ResolvedIdentity != core.AsBot {
|
|
t.Errorf("ResolvedIdentity want bot, got %s", f.ResolvedIdentity)
|
|
}
|
|
}
|
|
|
|
func TestCheckIdentity_Supported_UserOnly(t *testing.T) {
|
|
f, _, _, _ := TestFactory(t, &core.CliConfig{AppID: "a", AppSecret: "s"})
|
|
|
|
err := f.CheckIdentity(core.AsUser, []string{"user"})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if f.ResolvedIdentity != core.AsUser {
|
|
t.Errorf("ResolvedIdentity want user, got %s", f.ResolvedIdentity)
|
|
}
|
|
}
|
|
|
|
func TestCheckIdentity_Unsupported_Explicit(t *testing.T) {
|
|
f, _, _, _ := TestFactory(t, &core.CliConfig{AppID: "a", AppSecret: "s"})
|
|
f.IdentityAutoDetected = false // explicit --as
|
|
|
|
err := f.CheckIdentity(core.AsUser, []string{"bot"})
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
if !strings.Contains(err.Error(), "--as user is not supported") {
|
|
t.Errorf("unexpected error message: %v", err)
|
|
}
|
|
if !strings.Contains(err.Error(), "bot") {
|
|
t.Errorf("error should mention supported identity: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCheckIdentity_Unsupported_AutoDetected(t *testing.T) {
|
|
f, _, _, _ := TestFactory(t, &core.CliConfig{AppID: "a", AppSecret: "s"})
|
|
f.IdentityAutoDetected = true
|
|
|
|
err := f.CheckIdentity(core.AsUser, []string{"bot"})
|
|
var ve *errs.ValidationError
|
|
if !errors.As(err, &ve) {
|
|
t.Fatalf("expected *errs.ValidationError, got %T: %v", err, err)
|
|
}
|
|
if !strings.Contains(ve.Message, "resolved identity") {
|
|
t.Errorf("expected 'resolved identity' in message, got: %v", ve.Message)
|
|
}
|
|
if !strings.Contains(ve.Hint, "use --as bot") {
|
|
t.Errorf("expected hint to suggest --as bot, got: %v", ve.Hint)
|
|
}
|
|
}
|
|
|
|
// --- NewAPIClient / NewAPIClientWithConfig tests ---
|
|
|
|
func TestNewAPIClient(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", Brand: core.BrandLark}
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
|
|
ac, err := f.NewAPIClient()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if ac.Config.AppID != "a" {
|
|
t.Errorf("want AppID a, got %s", ac.Config.AppID)
|
|
}
|
|
}
|
|
|
|
func TestNewAPIClientWithConfig(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", Brand: core.BrandLark}
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
|
|
ac, err := f.NewAPIClientWithConfig(cfg)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if ac.Config.AppID != "a" {
|
|
t.Errorf("want AppID a, got %s", ac.Config.AppID)
|
|
}
|
|
if ac.SDK == nil {
|
|
t.Error("SDK should not be nil")
|
|
}
|
|
if ac.HTTP == nil {
|
|
t.Error("HTTP should not be nil")
|
|
}
|
|
}
|
|
|
|
func TestNewAPIClientWithConfig_NilIOStreams(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", Brand: core.BrandLark}
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
f.IOStreams = nil
|
|
|
|
ac, err := f.NewAPIClientWithConfig(cfg)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if ac == nil {
|
|
t.Fatal("expected non-nil APIClient")
|
|
}
|
|
}
|
|
|
|
// --- ResolveStrictMode tests ---
|
|
|
|
func TestResolveStrictMode_Off(t *testing.T) {
|
|
f, _, _, _ := TestFactory(t, &core.CliConfig{AppID: "a", AppSecret: "s"})
|
|
if got := f.ResolveStrictMode(context.Background()); got != core.StrictModeOff {
|
|
t.Errorf("expected off, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestResolveStrictMode_BotFromAccount(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", SupportedIdentities: 2} // SupportsBot = 2
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
if got := f.ResolveStrictMode(context.Background()); got != core.StrictModeBot {
|
|
t.Errorf("expected bot, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestResolveStrictMode_UserFromAccount(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", SupportedIdentities: 1} // SupportsUser = 1
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
if got := f.ResolveStrictMode(context.Background()); got != core.StrictModeUser {
|
|
t.Errorf("expected user, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestResolveStrictMode_BothIdentities(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", SupportedIdentities: 3} // SupportsAll = 3
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
if got := f.ResolveStrictMode(context.Background()); got != core.StrictModeOff {
|
|
t.Errorf("expected off when both supported, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestResolveStrictMode_NilCredential(t *testing.T) {
|
|
f, _, _, _ := TestFactory(t, &core.CliConfig{AppID: "a", AppSecret: "s"})
|
|
f.Credential = nil
|
|
if got := f.ResolveStrictMode(context.Background()); got != core.StrictModeOff {
|
|
t.Errorf("expected off with nil credential, got %q", got)
|
|
}
|
|
}
|
|
|
|
// --- CheckStrictMode tests ---
|
|
|
|
func TestCheckStrictMode_BotMode_BotAllowed(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", SupportedIdentities: 2}
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
if err := f.CheckStrictMode(context.Background(), core.AsBot); err != nil {
|
|
t.Errorf("bot should be allowed in bot mode, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCheckStrictMode_BotMode_UserBlocked(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", SupportedIdentities: 2}
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
err := f.CheckStrictMode(context.Background(), core.AsUser)
|
|
if err == nil {
|
|
t.Fatal("expected error for user in bot mode")
|
|
}
|
|
if !strings.Contains(err.Error(), "strict mode") {
|
|
t.Errorf("error should mention strict mode, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCheckStrictMode_UserMode_UserAllowed(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", SupportedIdentities: 1}
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
if err := f.CheckStrictMode(context.Background(), core.AsUser); err != nil {
|
|
t.Errorf("user should be allowed in user mode, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCheckStrictMode_UserMode_BotBlocked(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", SupportedIdentities: 1}
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
err := f.CheckStrictMode(context.Background(), core.AsBot)
|
|
if err == nil {
|
|
t.Fatal("expected error for bot in user mode")
|
|
}
|
|
}
|
|
|
|
func TestCheckStrictMode_Off_BothAllowed(t *testing.T) {
|
|
f, _, _, _ := TestFactory(t, &core.CliConfig{AppID: "a", AppSecret: "s"})
|
|
if err := f.CheckStrictMode(context.Background(), core.AsUser); err != nil {
|
|
t.Errorf("user should be allowed when off: %v", err)
|
|
}
|
|
if err := f.CheckStrictMode(context.Background(), core.AsBot); err != nil {
|
|
t.Errorf("bot should be allowed when off: %v", err)
|
|
}
|
|
}
|
|
|
|
// --- ResolveAs strict mode tests ---
|
|
|
|
func TestResolveAs_StrictModeBot_ForceBot(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", SupportedIdentities: 2}
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
cmd := newCmdWithAsFlag("auto", false)
|
|
got := f.ResolveAs(context.Background(), cmd, "auto")
|
|
if got != core.AsBot {
|
|
t.Errorf("bot mode should force bot, got %s", got)
|
|
}
|
|
}
|
|
|
|
func TestResolveAs_StrictModeUser_ForceUser(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", SupportedIdentities: 1}
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
cmd := newCmdWithAsFlag("auto", false)
|
|
got := f.ResolveAs(context.Background(), cmd, "auto")
|
|
if got != core.AsUser {
|
|
t.Errorf("user mode should force user, got %s", got)
|
|
}
|
|
}
|
|
|
|
func TestResolveAs_StrictModeUser_PreservesExplicitBot(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", SupportedIdentities: 1}
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
cmd := newCmdWithAsFlag("bot", true)
|
|
got := f.ResolveAs(context.Background(), cmd, core.AsBot)
|
|
if got != core.AsBot {
|
|
t.Errorf("explicit bot should be preserved for strict-mode validation, got %s", got)
|
|
}
|
|
if err := f.CheckStrictMode(context.Background(), got); err == nil {
|
|
t.Fatal("expected strict-mode error for explicit bot in user mode")
|
|
}
|
|
}
|
|
|
|
func TestResolveAs_StrictModeBot_PreservesExplicitUser(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", SupportedIdentities: 2}
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
cmd := newCmdWithAsFlag("user", true)
|
|
got := f.ResolveAs(context.Background(), cmd, core.AsUser)
|
|
if got != core.AsUser {
|
|
t.Errorf("explicit user should be preserved for strict-mode validation, got %s", got)
|
|
}
|
|
if err := f.CheckStrictMode(context.Background(), got); err == nil {
|
|
t.Fatal("expected strict-mode error for explicit user in bot mode")
|
|
}
|
|
}
|
|
|
|
func TestResolveAs_StrictModeUser_ExplicitAutoForcesUser(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", SupportedIdentities: 1}
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
cmd := newCmdWithAsFlag("auto", true)
|
|
got := f.ResolveAs(context.Background(), cmd, core.AsAuto)
|
|
if got != core.AsUser {
|
|
t.Errorf("--as auto should use strict-mode user identity, got %s", got)
|
|
}
|
|
}
|
|
|
|
func TestResolveAs_StrictModeBot_IgnoresDefaultAsUser(t *testing.T) {
|
|
cfg := &core.CliConfig{AppID: "a", AppSecret: "s", DefaultAs: "user", SupportedIdentities: 2}
|
|
f, _, _, _ := TestFactory(t, cfg)
|
|
cmd := newCmdWithAsFlag("auto", false)
|
|
got := f.ResolveAs(context.Background(), cmd, "auto")
|
|
if got != core.AsBot {
|
|
t.Errorf("bot mode should override default-as user, got %s", got)
|
|
}
|
|
}
|
|
|
|
// stubExtProvider is a minimal extcred.Provider for testing external-provider guards.
|
|
type stubExtProvider struct {
|
|
name string
|
|
acct *extcred.Account
|
|
err error
|
|
}
|
|
|
|
func (s *stubExtProvider) Name() string { return s.name }
|
|
func (s *stubExtProvider) ResolveAccount(_ context.Context) (*extcred.Account, error) {
|
|
return s.acct, s.err
|
|
}
|
|
func (s *stubExtProvider) ResolveToken(_ context.Context, _ extcred.TokenSpec) (*extcred.Token, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func TestRequireBuiltinCredentialProvider_BlocksExternalProvider(t *testing.T) {
|
|
stub := &stubExtProvider{name: "env", acct: &extcred.Account{AppID: "app"}}
|
|
cred := credential.NewCredentialProvider([]extcred.Provider{stub}, nil, nil, nil)
|
|
f, _, _, _ := TestFactory(t, nil)
|
|
f.Credential = cred
|
|
|
|
err := f.RequireBuiltinCredentialProvider(context.Background(), "auth")
|
|
if err == nil {
|
|
t.Fatal("expected error, got nil")
|
|
}
|
|
|
|
var ve *errs.ValidationError
|
|
if !errors.As(err, &ve) {
|
|
t.Fatalf("error type = %T, want *errs.ValidationError", err)
|
|
}
|
|
if got := output.ExitCodeOf(err); got != output.ExitValidation {
|
|
t.Errorf("exit code = %d, want %d", got, output.ExitValidation)
|
|
}
|
|
if ve.Message == "" {
|
|
t.Error("expected non-empty message")
|
|
}
|
|
if ve.Hint == "" {
|
|
t.Error("expected non-empty hint")
|
|
}
|
|
}
|
|
|
|
func TestRequireBuiltinCredentialProvider_AllowsBuiltinProvider(t *testing.T) {
|
|
// No extension providers → built-in path → no error
|
|
f, _, _, _ := TestFactory(t, nil)
|
|
err := f.RequireBuiltinCredentialProvider(context.Background(), "auth")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRequireBuiltinCredentialProvider_NilCredential(t *testing.T) {
|
|
f, _, _, _ := TestFactory(t, nil)
|
|
f.Credential = nil
|
|
err := f.RequireBuiltinCredentialProvider(context.Background(), "auth")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error with nil Credential: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRequireBuiltinCredentialProvider_PropagatesProviderError(t *testing.T) {
|
|
sentinel := errors.New("provider unavailable")
|
|
stub := &stubExtProvider{name: "env", err: sentinel}
|
|
cred := credential.NewCredentialProvider([]extcred.Provider{stub}, nil, nil, nil)
|
|
|
|
f, _, _, _ := TestFactory(t, nil)
|
|
f.Credential = cred
|
|
|
|
err := f.RequireBuiltinCredentialProvider(context.Background(), "auth")
|
|
if !errors.Is(err, sentinel) {
|
|
t.Fatalf("error = %v, want sentinel", err)
|
|
}
|
|
}
|