From aad2d59c14e9c87c446f0b64e55ff95cb368c6aa Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Tue, 24 Mar 2026 09:05:42 -0700 Subject: [PATCH] address greptile review feedback (greploop iteration 2) - Fix P2: add useEffect cleanup to prevent stale response race condition - Fix P2: guard against unparseable expiration_date values (e.g. "N/A") - Fix P2: replace fragile .ant-alert CSS selectors with queryByRole("alert") - Add test for unparseable expiration_date Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/components/LicenseExpiryBanner.test.tsx | 16 ++++++++++++++-- .../src/components/LicenseExpiryBanner.tsx | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/ui/litellm-dashboard/src/components/LicenseExpiryBanner.test.tsx b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.test.tsx index c22cffa4e17..81345dac09f 100644 --- a/ui/litellm-dashboard/src/components/LicenseExpiryBanner.test.tsx +++ b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.test.tsx @@ -90,7 +90,7 @@ describe("LicenseExpiryBanner", () => { expect(mockGetLicenseInfo).toHaveBeenCalled(); }); await waitFor(() => { - expect(container.querySelector(".ant-alert")).toBeNull(); + expect(screen.queryByRole("alert")).toBeNull(); }); }); @@ -169,7 +169,19 @@ describe("LicenseExpiryBanner", () => { expect(mockGetLicenseInfo).toHaveBeenCalled(); }); await waitFor(() => { - expect(container.querySelector(".ant-alert")).toBeNull(); + expect(screen.queryByRole("alert")).toBeNull(); + }); + }); + + it("should render nothing when expiration_date is unparseable", async () => { + mockGetLicenseInfo.mockResolvedValue(makeLicense("N/A")); + + const { container } = render(); + await waitFor(() => { + expect(mockGetLicenseInfo).toHaveBeenCalled(); + }); + await waitFor(() => { + expect(screen.queryByRole("alert")).toBeNull(); }); }); }); diff --git a/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx index a265a8018d3..b327c571991 100644 --- a/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx +++ b/ui/litellm-dashboard/src/components/LicenseExpiryBanner.tsx @@ -14,9 +14,16 @@ export const LicenseExpiryBanner: React.FC = () => { useEffect(() => { if (!accessToken) return; + let cancelled = false; getLicenseInfo(accessToken) - .then(setLicenseInfo) + .then((info) => { + if (!cancelled) setLicenseInfo(info); + }) .catch(() => null); + + return () => { + cancelled = true; + }; }, [accessToken]); if (!licenseInfo?.has_license || !licenseInfo.expiration_date) { @@ -27,6 +34,12 @@ export const LicenseExpiryBanner: React.FC = () => { // then append end-of-day in UTC to avoid timezone-dependent parsing. const dateOnly = licenseInfo.expiration_date.split("T")[0]; const expDate = new Date(dateOnly + "T23:59:59Z"); + + // Guard against unparseable expiration_date values (e.g. "N/A", malformed strings) + if (isNaN(expDate.getTime())) { + return null; + } + const now = new Date(); const diffMs = expDate.getTime() - now.getTime(); const daysRemaining = Math.ceil(diffMs / (1000 * 60 * 60 * 24));