mirror of
https://github.com/mark3labs/kit.git
synced 2026-06-14 03:30:26 +00:00
a05da5f3ab
Remove the early ValidateEnvironment gate from CreateProvider that only checked env vars and --provider-api-key, blocking stored OAuth credentials from working. Each provider creation function already handles its own auth resolution with clear error messages. Update ValidateEnvironment to also check stored Anthropic credentials so the model selector UI correctly shows Anthropic models for OAuth users. Add automatic token refresh in oauthTransport so long-lived ACP sessions survive token renewals. Surface actionable auth error messages in ACP session creation. Fix pre-existing staticcheck SA5011 warnings in test files.
255 lines
6.5 KiB
Go
255 lines
6.5 KiB
Go
package auth
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestCredentialManager(t *testing.T) {
|
|
// Create a temporary directory for testing
|
|
tempDir, err := os.MkdirTemp("", "kit-auth-test")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer func() { _ = os.RemoveAll(tempDir) }()
|
|
|
|
// Create a credential manager with a test path
|
|
cm := &CredentialManager{
|
|
credentialsPath: filepath.Join(tempDir, "credentials.json"),
|
|
}
|
|
|
|
// Test initial state - no credentials
|
|
hasAuth, err := cm.HasAnthropicCredentials()
|
|
if err != nil {
|
|
t.Fatalf("HasAnthropicCredentials failed: %v", err)
|
|
}
|
|
if hasAuth {
|
|
t.Error("Expected no credentials initially")
|
|
}
|
|
|
|
// Test setting credentials
|
|
testAPIKey := "sk-ant-test-key-12345678901234567890"
|
|
err = cm.SetAnthropicCredentials(testAPIKey)
|
|
if err != nil {
|
|
t.Fatalf("SetAnthropicCredentials failed: %v", err)
|
|
}
|
|
|
|
// Test that credentials are now present
|
|
hasAuth, err = cm.HasAnthropicCredentials()
|
|
if err != nil {
|
|
t.Fatalf("HasAnthropicCredentials failed: %v", err)
|
|
}
|
|
if !hasAuth {
|
|
t.Error("Expected credentials to be present")
|
|
}
|
|
|
|
// Test retrieving credentials
|
|
creds, err := cm.GetAnthropicCredentials()
|
|
if err != nil {
|
|
t.Fatalf("GetAnthropicCredentials failed: %v", err)
|
|
}
|
|
if creds == nil {
|
|
t.Fatal("Expected credentials to be returned")
|
|
return
|
|
}
|
|
if creds.APIKey != testAPIKey {
|
|
t.Errorf("Expected API key %s, got %s", testAPIKey, creds.APIKey)
|
|
}
|
|
if creds.CreatedAt.IsZero() {
|
|
t.Error("Expected CreatedAt to be set")
|
|
}
|
|
|
|
// Test removing credentials
|
|
err = cm.RemoveAnthropicCredentials()
|
|
if err != nil {
|
|
t.Fatalf("RemoveAnthropicCredentials failed: %v", err)
|
|
}
|
|
|
|
// Test that credentials are gone
|
|
hasAuth, err = cm.HasAnthropicCredentials()
|
|
if err != nil {
|
|
t.Fatalf("HasAnthropicCredentials failed: %v", err)
|
|
}
|
|
if hasAuth {
|
|
t.Error("Expected no credentials after removal")
|
|
}
|
|
|
|
// Test that file is removed when empty
|
|
if _, err := os.Stat(cm.credentialsPath); !os.IsNotExist(err) {
|
|
t.Error("Expected credentials file to be removed when empty")
|
|
}
|
|
}
|
|
|
|
func TestValidateAnthropicAPIKey(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
apiKey string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "valid key",
|
|
apiKey: "sk-ant-test-key-12345678901234567890",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "empty key",
|
|
apiKey: "",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "wrong prefix",
|
|
apiKey: "sk-test-key-12345678901234567890",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "too short",
|
|
apiKey: "sk-ant-short",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "with whitespace",
|
|
apiKey: " sk-ant-test-key-12345678901234567890 ",
|
|
wantErr: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := validateAnthropicAPIKey(tt.apiKey)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("validateAnthropicAPIKey() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetAnthropicAPIKey(t *testing.T) {
|
|
// Create a temporary directory for testing
|
|
tempDir, err := os.MkdirTemp("", "kit-auth-test")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer func() { _ = os.RemoveAll(tempDir) }()
|
|
|
|
// Save original environment
|
|
originalAPIKey := os.Getenv("ANTHROPIC_API_KEY")
|
|
originalXDGConfig := os.Getenv("XDG_CONFIG_HOME")
|
|
defer func() {
|
|
_ = os.Setenv("ANTHROPIC_API_KEY", originalAPIKey)
|
|
_ = os.Setenv("XDG_CONFIG_HOME", originalXDGConfig)
|
|
}()
|
|
|
|
// Set up test environment
|
|
_ = os.Setenv("XDG_CONFIG_HOME", tempDir)
|
|
_ = os.Unsetenv("ANTHROPIC_API_KEY")
|
|
|
|
// Test 1: Flag value takes precedence
|
|
flagKey := "sk-ant-flag-key-12345678901234567890"
|
|
apiKey, source, err := GetAnthropicAPIKey(flagKey)
|
|
if err != nil {
|
|
t.Fatalf("GetAnthropicAPIKey failed: %v", err)
|
|
}
|
|
if apiKey != flagKey {
|
|
t.Errorf("Expected flag key %s, got %s", flagKey, apiKey)
|
|
}
|
|
if source != "command-line flag" {
|
|
t.Errorf("Expected source 'command-line flag', got %s", source)
|
|
}
|
|
|
|
// Test 2: Stored credentials when no flag
|
|
cm, err := NewCredentialManager()
|
|
if err != nil {
|
|
t.Fatalf("NewCredentialManager failed: %v", err)
|
|
}
|
|
|
|
storedKey := "sk-ant-stored-key-12345678901234567890"
|
|
err = cm.SetAnthropicCredentials(storedKey)
|
|
if err != nil {
|
|
t.Fatalf("SetAnthropicCredentials failed: %v", err)
|
|
}
|
|
|
|
apiKey, source, err = GetAnthropicAPIKey("")
|
|
if err != nil {
|
|
t.Fatalf("GetAnthropicAPIKey failed: %v", err)
|
|
}
|
|
if apiKey != storedKey {
|
|
t.Errorf("Expected stored key %s, got %s", storedKey, apiKey)
|
|
}
|
|
if source != "stored API key" {
|
|
t.Errorf("Expected source 'stored API key', got %s", source)
|
|
}
|
|
|
|
// Test 3: Environment variable when no flag or stored credentials
|
|
err = cm.RemoveAnthropicCredentials()
|
|
if err != nil {
|
|
t.Fatalf("RemoveAnthropicCredentials failed: %v", err)
|
|
}
|
|
|
|
envKey := "sk-ant-env-key-12345678901234567890"
|
|
_ = os.Setenv("ANTHROPIC_API_KEY", envKey)
|
|
|
|
apiKey, source, err = GetAnthropicAPIKey("")
|
|
if err != nil {
|
|
t.Fatalf("GetAnthropicAPIKey failed: %v", err)
|
|
}
|
|
if apiKey != envKey {
|
|
t.Errorf("Expected env key %s, got %s", envKey, apiKey)
|
|
}
|
|
if source != "ANTHROPIC_API_KEY environment variable" {
|
|
t.Errorf("Expected source 'ANTHROPIC_API_KEY environment variable', got %s", source)
|
|
}
|
|
|
|
// Test 4: No credentials available
|
|
_ = os.Unsetenv("ANTHROPIC_API_KEY")
|
|
|
|
_, _, err = GetAnthropicAPIKey("")
|
|
if err == nil {
|
|
t.Error("Expected error when no credentials available")
|
|
}
|
|
}
|
|
|
|
func TestCredentialStorePersistence(t *testing.T) {
|
|
// Create a temporary directory for testing
|
|
tempDir, err := os.MkdirTemp("", "kit-auth-test")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer func() { _ = os.RemoveAll(tempDir) }()
|
|
|
|
credentialsPath := filepath.Join(tempDir, "credentials.json")
|
|
|
|
// Create first manager and store credentials
|
|
cm1 := &CredentialManager{credentialsPath: credentialsPath}
|
|
testAPIKey := "sk-ant-test-key-12345678901234567890"
|
|
|
|
err = cm1.SetAnthropicCredentials(testAPIKey)
|
|
if err != nil {
|
|
t.Fatalf("SetAnthropicCredentials failed: %v", err)
|
|
}
|
|
|
|
// Create second manager and verify credentials persist
|
|
cm2 := &CredentialManager{credentialsPath: credentialsPath}
|
|
|
|
creds, err := cm2.GetAnthropicCredentials()
|
|
if err != nil {
|
|
t.Fatalf("GetAnthropicCredentials failed: %v", err)
|
|
}
|
|
if creds == nil {
|
|
t.Fatal("Expected credentials to persist")
|
|
return
|
|
}
|
|
if creds.APIKey != testAPIKey {
|
|
t.Errorf("Expected API key %s, got %s", testAPIKey, creds.APIKey)
|
|
}
|
|
|
|
// Verify file permissions
|
|
info, err := os.Stat(credentialsPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to stat credentials file: %v", err)
|
|
}
|
|
if info.Mode().Perm() != 0600 {
|
|
t.Errorf("Expected file permissions 0600, got %v", info.Mode().Perm())
|
|
}
|
|
}
|