diff --git a/ui/src/api/queries/auth.test.tsx b/ui/src/api/queries/auth.test.tsx new file mode 100644 index 00000000..fe87a1c5 --- /dev/null +++ b/ui/src/api/queries/auth.test.tsx @@ -0,0 +1,46 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { renderHook, waitFor } from '@testing-library/react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import type { ReactNode } from 'react'; + +vi.mock('../client', () => ({ api: { GET: vi.fn() } })); + +import { api as apiClient } from '../client'; +import { useAuthCapabilities } from './auth'; + +function wrapper({ children }: { children: ReactNode }) { + const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } }); + return {children}; +} + +describe('useAuthCapabilities', () => { + beforeEach(() => vi.clearAllMocks()); + + it('returns the capabilities body on success', async () => { + (apiClient.GET as any).mockResolvedValue({ + data: { + oidc: { enabled: true, providerName: 'Logto', primary: true }, + localAccounts: { enabled: true, adminRecoveryOnly: true }, + }, + error: null, + }); + + const { result } = renderHook(() => useAuthCapabilities(), { wrapper }); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + expect(result.current.data?.oidc?.enabled).toBe(true); + expect(result.current.data?.oidc?.providerName).toBe('Logto'); + expect(result.current.data?.localAccounts?.adminRecoveryOnly).toBe(true); + }); + + it('exposes error state when the request fails', async () => { + (apiClient.GET as any).mockResolvedValue({ + data: undefined, + error: { message: 'boom' }, + }); + + const { result } = renderHook(() => useAuthCapabilities(), { wrapper }); + + await waitFor(() => expect(result.current.isError).toBe(true)); + }); +}); diff --git a/ui/src/api/queries/auth.ts b/ui/src/api/queries/auth.ts index 343f462b..b728e642 100644 --- a/ui/src/api/queries/auth.ts +++ b/ui/src/api/queries/auth.ts @@ -1,6 +1,8 @@ import { useQuery } from '@tanstack/react-query'; import { config } from '../../config'; import { useAuthStore } from '../../auth/auth-store'; +import { api } from '../client'; +import type { components } from '../schema'; export interface RoleSummary { id: string; @@ -46,3 +48,18 @@ export function useMe(enabled = false) { staleTime: 30_000, }); } + +export type AuthCapabilities = components['schemas']['AuthCapabilitiesResponse']; + +export function useAuthCapabilities() { + return useQuery({ + queryKey: ['auth', 'capabilities'], + queryFn: async () => { + const { data, error } = await api.GET('/auth/capabilities'); + if (error || !data) throw new Error('Failed to load auth capabilities'); + return data as AuthCapabilities; + }, + staleTime: Infinity, + retry: false, + }); +}