mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
23abbb67ea
* feat(auth): add Anthropic OAuth setup-token login flow Add support for Anthropic's OAuth-based setup tokens (sk-ant-oat01-*) as an alternative to API keys. This includes: - New `--setup-token` flag on `auth login` command - Interactive login menu for Anthropic (setup token vs API key) - Setup token validation and credential storage with oauth auth method - Usage endpoint integration to show 5h/7d utilization in `auth status` - Streaming support for OAuth tokens (required by Anthropic API) - Model ID normalization (dots to hyphens) for API compatibility - Remove .env.example (secrets should not be templated) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(auth): update related functionality * refactor(auth): organize constants and improve header casing in requests fo CI * fix(auth): fix golint again * fix(auth): handle nil arguments in tool calls for buildParams function --------- Co-authored-by: Baller <sharonms3377@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
99 lines
2.9 KiB
Go
99 lines
2.9 KiB
Go
package auth
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestFetchAnthropicUsage_Success(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if got := r.Header.Get("Authorization"); got != "Bearer test-token" {
|
|
t.Errorf("Authorization = %q, want %q", got, "Bearer test-token")
|
|
}
|
|
if got := r.Header.Get("Anthropic-Beta"); got != anthropicBetaHeader {
|
|
t.Errorf("Anthropic-Beta = %q, want %q", got, anthropicBetaHeader)
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(`{"five_hour":{"utilization":0.42},"seven_day":{"utilization":0.85}}`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
// Temporarily override the URL by using the test server
|
|
origURL := anthropicUsageURL
|
|
defer func() { setAnthropicUsageURL(origURL) }()
|
|
setAnthropicUsageURL(srv.URL)
|
|
|
|
usage, err := FetchAnthropicUsage("test-token")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if usage.FiveHourUtilization != 0.42 {
|
|
t.Errorf("FiveHourUtilization = %v, want 0.42", usage.FiveHourUtilization)
|
|
}
|
|
if usage.SevenDayUtilization != 0.85 {
|
|
t.Errorf("SevenDayUtilization = %v, want 0.85", usage.SevenDayUtilization)
|
|
}
|
|
}
|
|
|
|
func TestFetchAnthropicUsage_Forbidden(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusForbidden)
|
|
w.Write([]byte(`{"error":"forbidden"}`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
origURL := anthropicUsageURL
|
|
defer func() { setAnthropicUsageURL(origURL) }()
|
|
setAnthropicUsageURL(srv.URL)
|
|
|
|
_, err := FetchAnthropicUsage("test-token")
|
|
if err == nil {
|
|
t.Fatal("expected error for 403, got nil")
|
|
}
|
|
if !strings.Contains(err.Error(), "insufficient scope") {
|
|
t.Errorf("expected 'insufficient scope' error, got %q", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestFetchAnthropicUsage_ServerError(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
w.Write([]byte(`internal error`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
origURL := anthropicUsageURL
|
|
defer func() { setAnthropicUsageURL(origURL) }()
|
|
setAnthropicUsageURL(srv.URL)
|
|
|
|
_, err := FetchAnthropicUsage("test-token")
|
|
if err == nil {
|
|
t.Fatal("expected error for 500, got nil")
|
|
}
|
|
if !strings.Contains(err.Error(), "500") {
|
|
t.Errorf("expected error containing '500', got %q", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestFetchAnthropicUsage_MalformedJSON(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(`not json`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
origURL := anthropicUsageURL
|
|
defer func() { setAnthropicUsageURL(origURL) }()
|
|
setAnthropicUsageURL(srv.URL)
|
|
|
|
_, err := FetchAnthropicUsage("test-token")
|
|
if err == nil {
|
|
t.Fatal("expected error for malformed JSON, got nil")
|
|
}
|
|
if !strings.Contains(err.Error(), "parsing usage response") {
|
|
t.Errorf("expected 'parsing usage response' error, got %q", err.Error())
|
|
}
|
|
}
|