mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-28 01:32:17 +00:00
fix(ui): hold router and fallback write controls until the source map loads
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
fbf0efa3ca
commit
d943d556c4
4 changed files with 44 additions and 6 deletions
|
|
@ -417,6 +417,25 @@ describe("Fallbacks", () => {
|
|||
expect(screen.queryByTestId("delete-modal")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("hides write actions until the source map has loaded", async () => {
|
||||
let resolveSources: (value: { fields: never[]; source: Record<string, string> }) => void = () => {};
|
||||
vi.mocked(networkingModule.getRouterSettingsCall).mockReturnValue(
|
||||
new Promise((resolve) => {
|
||||
resolveSources = resolve;
|
||||
}),
|
||||
);
|
||||
renderWithQueryClient(<Fallbacks {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getAllByText("gpt-4").length).toBeGreaterThan(0);
|
||||
});
|
||||
expect(screen.queryByTestId("add-fallbacks-button")).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId("edit-fallback-button")).not.toBeInTheDocument();
|
||||
|
||||
resolveSources({ fields: [], source: { fallbacks: "config" } });
|
||||
expect(await screen.findByTestId("add-fallbacks-button")).toBeDisabled();
|
||||
});
|
||||
|
||||
it.each(["env", "default", "db"])("keeps fallbacks editable when source is %s", async (source) => {
|
||||
const user = userEvent.setup();
|
||||
vi.mocked(networkingModule.getRouterSettingsCall).mockResolvedValue({
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ async function testFallbackModelResponse(selectedModel: string, accessToken: str
|
|||
|
||||
const Fallbacks: React.FC<FallbacksProps> = ({ accessToken, userRole, userID }) => {
|
||||
const [routerSettings, setRouterSettings] = useState<{ [key: string]: any }>({});
|
||||
const [routerSources, setRouterSources] = useState<FieldSourceMap>({});
|
||||
const [routerSources, setRouterSources] = useState<FieldSourceMap | null>(null);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [fallbackToDelete, setFallbackToDelete] = useState<FallbackEntry | null>(null);
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
|
|
@ -254,7 +254,7 @@ const Fallbacks: React.FC<FallbacksProps> = ({ accessToken, userRole, userID })
|
|||
|
||||
const hasFallbacks = Array.isArray(routerSettings.fallbacks) && routerSettings.fallbacks.length > 0;
|
||||
// Admin Viewer follows the read-parity rule: see fallbacks, no writes.
|
||||
const canModify = isProxyAdminRole(userRole ?? "");
|
||||
const canModify = isProxyAdminRole(userRole ?? "") && routerSources !== null;
|
||||
const fallbacksFrozen = isConfigOwned(routerSources, "fallbacks");
|
||||
const frozenActionClass = `${iconWrapperClass} cursor-not-allowed opacity-50`;
|
||||
|
||||
|
|
|
|||
|
|
@ -67,9 +67,9 @@ describe("RouterSettings", () => {
|
|||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it("should render the Save Changes and Reset buttons when authenticated", () => {
|
||||
it("should render the Save Changes and Reset buttons when authenticated", async () => {
|
||||
renderWithProviders(<RouterSettings {...defaultProps} />);
|
||||
expect(screen.getByRole("button", { name: /save changes/i })).toBeInTheDocument();
|
||||
expect(await screen.findByRole("button", { name: /save changes/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: /reset/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
|
@ -203,6 +203,25 @@ describe("RouterSettings", () => {
|
|||
expect(await screen.findByText("Set in config.yaml and cannot be changed here")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("holds the form until the source map has loaded so config owned fields never render editable", async () => {
|
||||
let resolveSources: (
|
||||
value: typeof mockRouterSettingsResponse & { source: Record<string, string> },
|
||||
) => void = () => {};
|
||||
vi.mocked(getRouterSettingsCall).mockReturnValue(
|
||||
new Promise((resolve) => {
|
||||
resolveSources = resolve;
|
||||
}),
|
||||
);
|
||||
renderWithProviders(<RouterSettings {...defaultProps} />);
|
||||
|
||||
await waitFor(() => expect(getCallbacksCall).toHaveBeenCalled());
|
||||
expect(screen.queryByRole("textbox", { name: /num_retries/i })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: /save changes/i })).not.toBeInTheDocument();
|
||||
|
||||
resolveSources({ ...mockRouterSettingsResponse, source: { num_retries: "config" } });
|
||||
expect(await screen.findByRole("textbox", { name: /num_retries/i })).toBeDisabled();
|
||||
});
|
||||
|
||||
it.each(["env", "default", "db"])("keeps fields editable when source is %s", async (source) => {
|
||||
const user = userEvent.setup();
|
||||
vi.mocked(getRouterSettingsCall).mockResolvedValue({
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const RouterSettings: React.FC<RouterSettingsProps> = ({ accessToken, userRole,
|
|||
const [availableRoutingStrategies, setAvailableRoutingStrategies] = useState<string[]>([]);
|
||||
const [routerFieldsMetadata, setRouterFieldsMetadata] = useState<{ [key: string]: any }>({});
|
||||
const [routingStrategyDescriptions, setRoutingStrategyDescriptions] = useState<{ [key: string]: string }>({});
|
||||
const [routerSources, setRouterSources] = useState<FieldSourceMap>({});
|
||||
const [routerSources, setRouterSources] = useState<FieldSourceMap | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!accessToken || !userRole || !userID) {
|
||||
|
|
@ -177,7 +177,7 @@ const RouterSettings: React.FC<RouterSettingsProps> = ({ accessToken, userRole,
|
|||
}
|
||||
};
|
||||
|
||||
if (!accessToken) {
|
||||
if (!accessToken || routerSources === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue