mirror of
https://github.com/router-for-me/CLIProxyAPIPlus.git
synced 2026-03-20 07:51:52 +00:00
165 lines
5.1 KiB
Go
165 lines
5.1 KiB
Go
package management
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
|
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
|
)
|
|
|
|
func TestRequestGitLabPATToken_SavesAuthRecord(t *testing.T) {
|
|
t.Setenv("MANAGEMENT_PASSWORD", "")
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if got := r.Header.Get("Authorization"); got != "Bearer glpat-test-token" {
|
|
t.Fatalf("authorization header = %q, want Bearer glpat-test-token", got)
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
switch r.URL.Path {
|
|
case "/api/v4/user":
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
|
"id": 42,
|
|
"username": "gitlab-user",
|
|
"name": "GitLab User",
|
|
"email": "gitlab@example.com",
|
|
})
|
|
case "/api/v4/personal_access_tokens/self":
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
|
"id": 7,
|
|
"name": "management-center",
|
|
"scopes": []string{"api", "read_user"},
|
|
"user_id": 42,
|
|
})
|
|
case "/api/v4/code_suggestions/direct_access":
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
|
"base_url": "https://cloud.gitlab.example.com",
|
|
"token": "gateway-token",
|
|
"expires_at": 1893456000,
|
|
"headers": map[string]string{
|
|
"X-Gitlab-Realm": "saas",
|
|
},
|
|
"model_details": map[string]any{
|
|
"model_provider": "anthropic",
|
|
"model_name": "claude-sonnet-4-5",
|
|
},
|
|
})
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}))
|
|
defer upstream.Close()
|
|
|
|
store := &memoryAuthStore{}
|
|
h := NewHandlerWithoutConfigFilePath(&config.Config{AuthDir: t.TempDir()}, coreauth.NewManager(nil, nil, nil))
|
|
h.tokenStore = store
|
|
|
|
rec := httptest.NewRecorder()
|
|
ctx, _ := gin.CreateTestContext(rec)
|
|
ctx.Request = httptest.NewRequest(http.MethodPost, "/v0/management/gitlab-auth-url", strings.NewReader(`{"base_url":"`+upstream.URL+`","personal_access_token":"glpat-test-token"}`))
|
|
ctx.Request.Header.Set("Content-Type", "application/json")
|
|
|
|
h.RequestGitLabPATToken(ctx)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected status %d, got %d with body %s", http.StatusOK, rec.Code, rec.Body.String())
|
|
}
|
|
|
|
var resp map[string]any
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
|
t.Fatalf("decode response: %v", err)
|
|
}
|
|
if got := resp["status"]; got != "ok" {
|
|
t.Fatalf("status = %#v, want ok", got)
|
|
}
|
|
if got := resp["model_provider"]; got != "anthropic" {
|
|
t.Fatalf("model_provider = %#v, want anthropic", got)
|
|
}
|
|
if got := resp["model_name"]; got != "claude-sonnet-4-5" {
|
|
t.Fatalf("model_name = %#v, want claude-sonnet-4-5", got)
|
|
}
|
|
|
|
store.mu.Lock()
|
|
defer store.mu.Unlock()
|
|
if len(store.items) != 1 {
|
|
t.Fatalf("expected 1 saved auth record, got %d", len(store.items))
|
|
}
|
|
var saved *coreauth.Auth
|
|
for _, item := range store.items {
|
|
saved = item
|
|
}
|
|
if saved == nil {
|
|
t.Fatal("expected saved auth record")
|
|
}
|
|
if saved.Provider != "gitlab" {
|
|
t.Fatalf("provider = %q, want gitlab", saved.Provider)
|
|
}
|
|
if got := saved.Metadata["auth_kind"]; got != "personal_access_token" {
|
|
t.Fatalf("auth_kind = %#v, want personal_access_token", got)
|
|
}
|
|
if got := saved.Metadata["model_provider"]; got != "anthropic" {
|
|
t.Fatalf("saved model_provider = %#v, want anthropic", got)
|
|
}
|
|
if got := saved.Metadata["duo_gateway_token"]; got != "gateway-token" {
|
|
t.Fatalf("saved duo_gateway_token = %#v, want gateway-token", got)
|
|
}
|
|
}
|
|
|
|
func TestPostOAuthCallback_GitLabWritesPendingCallbackFile(t *testing.T) {
|
|
t.Setenv("MANAGEMENT_PASSWORD", "")
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
authDir := t.TempDir()
|
|
state := "gitlab-state-123"
|
|
RegisterOAuthSession(state, "gitlab")
|
|
t.Cleanup(func() { CompleteOAuthSession(state) })
|
|
|
|
h := NewHandlerWithoutConfigFilePath(&config.Config{AuthDir: authDir}, coreauth.NewManager(nil, nil, nil))
|
|
|
|
rec := httptest.NewRecorder()
|
|
ctx, _ := gin.CreateTestContext(rec)
|
|
ctx.Request = httptest.NewRequest(http.MethodPost, "/v0/management/oauth-callback", strings.NewReader(`{"provider":"gitlab","redirect_url":"http://localhost:17171/auth/callback?code=test-code&state=`+state+`"}`))
|
|
ctx.Request.Header.Set("Content-Type", "application/json")
|
|
|
|
h.PostOAuthCallback(ctx)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected status %d, got %d with body %s", http.StatusOK, rec.Code, rec.Body.String())
|
|
}
|
|
|
|
filePath := filepath.Join(authDir, ".oauth-gitlab-"+state+".oauth")
|
|
data, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
t.Fatalf("read callback file: %v", err)
|
|
}
|
|
|
|
var payload map[string]string
|
|
if err := json.Unmarshal(data, &payload); err != nil {
|
|
t.Fatalf("decode callback payload: %v", err)
|
|
}
|
|
if got := payload["code"]; got != "test-code" {
|
|
t.Fatalf("callback code = %q, want test-code", got)
|
|
}
|
|
if got := payload["state"]; got != state {
|
|
t.Fatalf("callback state = %q, want %q", got, state)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeOAuthProvider_GitLab(t *testing.T) {
|
|
provider, err := NormalizeOAuthProvider("gitlab")
|
|
if err != nil {
|
|
t.Fatalf("NormalizeOAuthProvider returned error: %v", err)
|
|
}
|
|
if provider != "gitlab" {
|
|
t.Fatalf("provider = %q, want gitlab", provider)
|
|
}
|
|
}
|