mirror of
https://github.com/jeffusion/gitea-ai-assistant.git
synced 2026-03-27 10:05:50 +00:00
Remove the per-provider listModels API (GET /providers/:id/models) and all four provider implementations (OpenAI Compatible, OpenAI Responses, Anthropic, Gemini). ModelCombobox now only shows tokenlens suggestions (tagged '推荐') plus free-form custom input — no more unfiltered 'API' models from provider SDKs. Fixes: switching provider type in ProviderDialog no longer shows stale models from the original provider's API. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
100 lines
3.2 KiB
TypeScript
100 lines
3.2 KiB
TypeScript
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
import { render, screen, waitFor } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import type { ReactNode } from 'react';
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
import { RoleAssignment } from '../RoleAssignment';
|
|
import { fetchProviders, fetchRoles, setRole } from '@/services/llmProviderService';
|
|
|
|
vi.mock('sonner', () => ({
|
|
toast: {
|
|
success: vi.fn(),
|
|
error: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
vi.mock('@/services/llmProviderService', async () => {
|
|
const actual = await vi.importActual<typeof import('@/services/llmProviderService')>('@/services/llmProviderService');
|
|
return {
|
|
...actual,
|
|
fetchProviders: vi.fn(),
|
|
fetchRoles: vi.fn(),
|
|
setRole: vi.fn(),
|
|
fetchModelSuggestions: vi.fn().mockResolvedValue({
|
|
openai_compatible: ['gpt-4o', 'gpt-4o-mini'],
|
|
openai_responses: ['gpt-4o', 'gpt-4o-mini'],
|
|
anthropic: ['claude-sonnet-4-20250514'],
|
|
gemini: ['gemini-2.5-pro'],
|
|
}),
|
|
};
|
|
});
|
|
|
|
function renderWithQuery(ui: ReactNode) {
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: { retry: false },
|
|
mutations: { retry: false },
|
|
},
|
|
});
|
|
return render(<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>);
|
|
}
|
|
|
|
describe('RoleAssignment', () => {
|
|
it('renders role cards and supports provider/model editing', async () => {
|
|
vi.mocked(fetchProviders).mockResolvedValueOnce([
|
|
{
|
|
id: 'p1',
|
|
name: 'OpenAI',
|
|
type: 'openai_responses',
|
|
baseUrl: null,
|
|
defaultModel: 'gpt-4o-mini',
|
|
isEnabled: true,
|
|
hasKey: true,
|
|
extraConfig: {},
|
|
createdAt: '2026-01-01',
|
|
},
|
|
]);
|
|
|
|
vi.mocked(fetchRoles).mockResolvedValueOnce([
|
|
{
|
|
role: 'legacy',
|
|
providerId: 'p1',
|
|
providerName: 'OpenAI',
|
|
providerType: 'openai_responses',
|
|
model: 'gpt-4o',
|
|
},
|
|
]);
|
|
|
|
vi.mocked(setRole).mockResolvedValue({
|
|
role: 'planner',
|
|
providerId: 'p1',
|
|
providerName: 'OpenAI',
|
|
providerType: 'openai_responses',
|
|
model: 'custom-planner-model',
|
|
});
|
|
|
|
const user = userEvent.setup();
|
|
renderWithQuery(<RoleAssignment />);
|
|
|
|
expect(await screen.findByText('角色分配')).toBeInTheDocument();
|
|
expect(await screen.findByText('Legacy 审查')).toBeInTheDocument();
|
|
expect(await screen.findByText('规划器 Planner')).toBeInTheDocument();
|
|
|
|
// Radix Select renders placeholder in a span with pointer-events: none.
|
|
// Click the trigger button (parent) instead of the placeholder text.
|
|
const providerPlaceholders = screen.getAllByText('选择提供商');
|
|
const triggerButton = providerPlaceholders[0].closest('button')!;
|
|
await user.click(triggerButton);
|
|
await user.click(await screen.findByRole('option', { name: /OpenAI/ }));
|
|
|
|
const modelInputs = screen.getAllByPlaceholderText('选择或输入模型...') as HTMLInputElement[];
|
|
await waitFor(() => {
|
|
expect(modelInputs[1].value).toBe('gpt-4o-mini');
|
|
});
|
|
|
|
await user.clear(modelInputs[1]);
|
|
await user.type(modelInputs[1], 'custom-planner-model');
|
|
expect(modelInputs[1].value).toBe('custom-planner-model');
|
|
});
|
|
});
|