Files
goclaw/internal/http/oauth_test.go
T
viettranx 28fab9507a feat(storage): add lazy folder loading, SSE size endpoint, and enhanced file viewer
- Backend: depth-limited WalkDir (max 3 levels default) with on-demand subtree loading
- Backend: new GET /v1/storage/size SSE endpoint with 60min in-memory cache
- Backend: raw binary file serving (?raw=true) with MIME detection and download support
- Frontend: lazy tree expansion with loading spinners for deep folders
- Frontend: streaming size display with cache info tooltip
- Frontend: image viewer (blob URL), unsupported file UI, download button, colored size badges
- Frontend: file-type icons for 13 categories (md, json, yaml, images, video, etc.)
- Fix sidebar connection status text overflow on collapse
- Apply go fix modernization (interface{} → any) across http handlers
2026-03-14 18:13:52 +07:00

241 lines
5.8 KiB
Go

package http
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/google/uuid"
"github.com/nextlevelbuilder/goclaw/internal/store"
)
// --- mock stores for tests ---
type mockProviderStore struct {
providers map[string]*store.LLMProviderData
}
func newMockProviderStore() *mockProviderStore {
return &mockProviderStore{providers: make(map[string]*store.LLMProviderData)}
}
func (m *mockProviderStore) CreateProvider(_ context.Context, p *store.LLMProviderData) error {
if p.ID == uuid.Nil {
p.ID = uuid.New()
}
m.providers[p.Name] = p
return nil
}
func (m *mockProviderStore) GetProvider(_ context.Context, id uuid.UUID) (*store.LLMProviderData, error) {
for _, p := range m.providers {
if p.ID == id {
return p, nil
}
}
return nil, fmt.Errorf("not found")
}
func (m *mockProviderStore) GetProviderByName(_ context.Context, name string) (*store.LLMProviderData, error) {
if p, ok := m.providers[name]; ok {
return p, nil
}
return nil, fmt.Errorf("not found")
}
func (m *mockProviderStore) ListProviders(_ context.Context) ([]store.LLMProviderData, error) {
var out []store.LLMProviderData
for _, p := range m.providers {
out = append(out, *p)
}
return out, nil
}
func (m *mockProviderStore) UpdateProvider(_ context.Context, id uuid.UUID, updates map[string]any) error {
for _, p := range m.providers {
if p.ID == id {
if v, ok := updates["api_key"]; ok {
p.APIKey = v.(string)
}
if v, ok := updates["settings"]; ok {
p.Settings = v.(json.RawMessage)
}
if v, ok := updates["enabled"]; ok {
p.Enabled = v.(bool)
}
return nil
}
}
return fmt.Errorf("not found")
}
func (m *mockProviderStore) DeleteProvider(_ context.Context, id uuid.UUID) error {
for name, p := range m.providers {
if p.ID == id {
delete(m.providers, name)
return nil
}
}
return fmt.Errorf("not found")
}
type mockSecretsStore struct {
data map[string]string
}
func newMockSecretsStore() *mockSecretsStore {
return &mockSecretsStore{data: make(map[string]string)}
}
func (m *mockSecretsStore) Get(_ context.Context, key string) (string, error) {
if v, ok := m.data[key]; ok {
return v, nil
}
return "", fmt.Errorf("not found: %s", key)
}
func (m *mockSecretsStore) Set(_ context.Context, key, value string) error {
m.data[key] = value
return nil
}
func (m *mockSecretsStore) Delete(_ context.Context, key string) error {
delete(m.data, key)
return nil
}
func (m *mockSecretsStore) GetAll(_ context.Context) (map[string]string, error) {
return m.data, nil
}
// --- helper ---
func newTestOAuthHandler(token string) *OAuthHandler {
return NewOAuthHandler(token, newMockProviderStore(), newMockSecretsStore(), nil, nil)
}
// --- tests ---
func TestOAuthHandlerStatusNoToken(t *testing.T) {
h := newTestOAuthHandler("")
mux := http.NewServeMux()
h.RegisterRoutes(mux)
req := httptest.NewRequest("GET", "/v1/auth/openai/status", nil)
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status code = %d, want %d", w.Code, http.StatusOK)
}
var result map[string]any
json.NewDecoder(w.Body).Decode(&result)
if result["authenticated"] != false {
t.Errorf("authenticated = %v, want false", result["authenticated"])
}
}
func TestOAuthHandlerAuth(t *testing.T) {
h := newTestOAuthHandler("secret-token")
mux := http.NewServeMux()
h.RegisterRoutes(mux)
// Without token - should be unauthorized
req := httptest.NewRequest("GET", "/v1/auth/openai/status", nil)
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
if w.Code != http.StatusUnauthorized {
t.Fatalf("status code without token = %d, want %d", w.Code, http.StatusUnauthorized)
}
// With correct token - should work
req2 := httptest.NewRequest("GET", "/v1/auth/openai/status", nil)
req2.Header.Set("Authorization", "Bearer secret-token")
w2 := httptest.NewRecorder()
mux.ServeHTTP(w2, req2)
if w2.Code != http.StatusOK {
t.Fatalf("status code with token = %d, want %d", w2.Code, http.StatusOK)
}
}
func TestOAuthHandlerLogoutNoProvider(t *testing.T) {
h := newTestOAuthHandler("")
mux := http.NewServeMux()
h.RegisterRoutes(mux)
req := httptest.NewRequest("POST", "/v1/auth/openai/logout", nil)
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status code = %d, want %d", w.Code, http.StatusOK)
}
var result map[string]string
json.NewDecoder(w.Body).Decode(&result)
if result["status"] != "logged out" {
t.Errorf("status = %q, want 'logged out'", result["status"])
}
}
func TestOAuthHandlerRouteRegistration(t *testing.T) {
h := newTestOAuthHandler("")
mux := http.NewServeMux()
h.RegisterRoutes(mux)
routes := []struct {
method string
path string
}{
{"GET", "/v1/auth/openai/status"},
{"POST", "/v1/auth/openai/logout"},
{"POST", "/v1/auth/openai/start"},
}
for _, r := range routes {
req := httptest.NewRequest(r.method, r.path, nil)
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
if w.Code == http.StatusNotFound {
t.Errorf("%s %s returned 404", r.method, r.path)
}
}
}
func TestOAuthHandlerStartReturnsAuthURL(t *testing.T) {
h := newTestOAuthHandler("")
mux := http.NewServeMux()
h.RegisterRoutes(mux)
req := httptest.NewRequest("POST", "/v1/auth/openai/start", nil)
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
// Skip if port 1455 is already in use (environment issue, not code bug)
if w.Code == http.StatusInternalServerError {
t.Skip("port 1455 unavailable, skipping")
}
if w.Code != http.StatusOK {
t.Fatalf("status code = %d, want %d; body: %s", w.Code, http.StatusOK, w.Body.String())
}
var result map[string]any
json.NewDecoder(w.Body).Decode(&result)
_, hasURL := result["auth_url"]
_, hasStatus := result["status"]
if !hasURL && !hasStatus {
t.Fatal("response has neither auth_url nor status")
}
}