mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 14:02:43 +08:00
95 lines
3.8 KiB
Go
95 lines
3.8 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package credential
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/larksuite/cli/errs"
|
|
)
|
|
|
|
func TestDefaultTokenProvider_Dispatches(t *testing.T) {
|
|
// Just verify the type implements DefaultTokenResolver
|
|
var _ DefaultTokenResolver = &DefaultTokenProvider{}
|
|
}
|
|
|
|
func TestDefaultAccountProvider_Implements(t *testing.T) {
|
|
var _ DefaultAccountResolver = &DefaultAccountProvider{}
|
|
}
|
|
|
|
// TestClassifyTATResponseCode_InvalidClient_MapsToInvalidClient pins that the
|
|
// unified Token Endpoint's OAuth2 invalid_client error surfaces as
|
|
// CategoryConfig/InvalidClient — the configured app_id/app_secret cannot mint a
|
|
// tenant access token, the same actionable failure the legacy 10003/10014 codes
|
|
// produced. The numeric code is intentionally not asserted: the v3 endpoint may
|
|
// return invalid_client with no Lark code (code defaults to 0).
|
|
func TestClassifyTATResponseCode_InvalidClient_MapsToInvalidClient(t *testing.T) {
|
|
err := classifyTATResponseCode(0, "invalid_client", "client authentication failed", "feishu", "cli_app_x")
|
|
if err == nil {
|
|
t.Fatal("expected non-nil error for invalid_client")
|
|
}
|
|
var cfgErr *errs.ConfigError
|
|
if !errors.As(err, &cfgErr) {
|
|
t.Fatalf("expected *errs.ConfigError, got %T: %v", err, err)
|
|
}
|
|
if cfgErr.Category != errs.CategoryConfig {
|
|
t.Errorf("Category = %q, want %q", cfgErr.Category, errs.CategoryConfig)
|
|
}
|
|
if cfgErr.Subtype != errs.SubtypeInvalidClient {
|
|
t.Errorf("Subtype = %q, want %q", cfgErr.Subtype, errs.SubtypeInvalidClient)
|
|
}
|
|
if cfgErr.Hint == "" {
|
|
t.Error("Hint must be non-empty so the user gets a recovery action")
|
|
}
|
|
}
|
|
|
|
// TestClassifyTATResponseCode_UnauthorizedClient_MapsToInvalidClient pins that
|
|
// unauthorized_client is treated as the same credential failure as
|
|
// invalid_client.
|
|
func TestClassifyTATResponseCode_UnauthorizedClient_MapsToInvalidClient(t *testing.T) {
|
|
err := classifyTATResponseCode(0, "unauthorized_client", "client not authorized", "feishu", "cli_app_x")
|
|
var cfgErr *errs.ConfigError
|
|
if !errors.As(err, &cfgErr) {
|
|
t.Fatalf("expected *errs.ConfigError, got %T: %v", err, err)
|
|
}
|
|
if cfgErr.Subtype != errs.SubtypeInvalidClient {
|
|
t.Errorf("Subtype = %q, want %q", cfgErr.Subtype, errs.SubtypeInvalidClient)
|
|
}
|
|
}
|
|
|
|
// TestClassifyTATResponseCode_OtherErrorFallsThrough pins that OAuth errors
|
|
// outside the credential set fall through to the generic BuildAPIError fallback
|
|
// — still typed, but not a ConfigError. The mapping is narrow and intentional.
|
|
func TestClassifyTATResponseCode_OtherErrorFallsThrough(t *testing.T) {
|
|
err := classifyTATResponseCode(20068, "invalid_scope", "unauthorized scope", "feishu", "cli_app_x")
|
|
if err == nil {
|
|
t.Fatal("expected non-nil error for invalid_scope")
|
|
}
|
|
var cfgErr *errs.ConfigError
|
|
if errors.As(err, &cfgErr) {
|
|
t.Fatalf("invalid_scope must not be classified as ConfigError, got %T", err)
|
|
}
|
|
}
|
|
|
|
// TestClassifyTATResponseCode_CodeZeroOtherError_StillTyped pins the code-0
|
|
// backstop: a non-credential OAuth error (e.g. invalid_scope) that arrives with no
|
|
// numeric code (code 0) must still produce a non-nil typed error. BuildAPIError
|
|
// returns nil for code 0 (Feishu's success convention); without the backstop,
|
|
// FetchTAT would surface this deterministic rejection as ("", nil) — an empty token
|
|
// with no error.
|
|
func TestClassifyTATResponseCode_CodeZeroOtherError_StillTyped(t *testing.T) {
|
|
err := classifyTATResponseCode(0, "invalid_scope", "the requested scope is not granted", "feishu", "cli_app_x")
|
|
if err == nil {
|
|
t.Fatal("expected non-nil error for code-0 invalid_scope (must not be swallowed as success)")
|
|
}
|
|
if !errs.IsTyped(err) {
|
|
t.Fatalf("expected a typed errs.* error, got %T %v", err, err)
|
|
}
|
|
var cfgErr *errs.ConfigError
|
|
if errors.As(err, &cfgErr) {
|
|
t.Fatalf("code-0 invalid_scope must not be a ConfigError, got %T", err)
|
|
}
|
|
}
|