import { render, screen } from '@testing-library/react' import type { InputHTMLAttributes, ReactNode } from 'react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { ServiceVersionsSection } from './ServiceVersionsSection' const { mockUseFeatures, mockUseIsOrioleDb, mockUseProject, mockUseReadReplicas, mockUseServiceVersions, mockUseUpgradeEligibility, mockIsAwsK8sCloudProvider, } = vi.hoisted(() => ({ mockUseFeatures: vi.fn(), mockUseIsOrioleDb: vi.fn(), mockUseProject: vi.fn(), mockUseReadReplicas: vi.fn(), mockUseServiceVersions: vi.fn(), mockUseUpgradeEligibility: vi.fn(), mockIsAwsK8sCloudProvider: vi.fn(), })) vi.mock('common', () => ({ useParams: () => ({ ref: 'project-ref' }), })) vi.mock('ui', () => ({ Badge: ({ children }: { children: ReactNode }) => {children}, Card: ({ children }: { children: ReactNode }) =>
{children}
, CardContent: ({ children }: { children: ReactNode }) =>
{children}
, Input: (props: InputHTMLAttributes) => , InputGroup: ({ children }: { children: ReactNode }) =>
{children}
, InputGroupAddon: ({ children }: { children: ReactNode }) =>
{children}
, InputGroupInput: (props: InputHTMLAttributes) => , Tooltip: ({ children }: { children: ReactNode }) => <>{children}, TooltipContent: ({ children }: { children: ReactNode }) =>
{children}
, TooltipTrigger: ({ children }: { children: ReactNode }) => <>{children}, })) vi.mock('ui-patterns/Admonition', () => ({ Admonition: ({ title, description, children, }: { title?: ReactNode description?: ReactNode children?: ReactNode }) => (
{title}
{description}
{children}
), })) vi.mock('ui-patterns/form/FormItemLayout/FormItemLayout', () => ({ FormItemLayout: ({ label, children }: { label: ReactNode; children: ReactNode }) => ( ), })) vi.mock('ui-patterns/PageSection', () => ({ PageSection: ({ children, id }: { children: ReactNode; id?: string }) => (
{children}
), PageSectionContent: ({ children }: { children: ReactNode }) =>
{children}
, PageSectionDescription: ({ children }: { children: ReactNode }) =>

{children}

, PageSectionMeta: ({ children }: { children: ReactNode }) =>
{children}
, PageSectionSummary: ({ children }: { children: ReactNode }) =>
{children}
, PageSectionTitle: ({ children }: { children: ReactNode }) =>

{children}

, })) vi.mock('ui-patterns/ShimmeringLoader', () => ({ GenericSkeletonLoader: () =>
Loading service versions
, })) vi.mock('../../Infrastructure/UpgradeWarnings', () => ({ ReadReplicasWarning: ({ latestPgVersion }: { latestPgVersion?: string }) => (
Read replicas block upgrade to {latestPgVersion}
), ValidationErrorsWarning: ({ validationErrors }: { validationErrors?: string[] }) => (
Validation errors: {validationErrors?.join(', ')}
), ValidationWarningsAdmonition: ({ warnings }: { warnings?: string[] }) => (
Validation warnings: {warnings?.join(', ')}
), })) vi.mock('../Infrastructure/ProjectUpgradeAlert', () => ({ ProjectUpgradeAlert: () =>
Project upgrade available
, })) vi.mock('@/components/ui/AlertError', () => ({ AlertError: ({ subject }: { subject: string }) =>
{subject}
, })) vi.mock('@/data/config/project-upgrade-eligibility-query', () => ({ useProjectUpgradeEligibilityQuery: mockUseUpgradeEligibility, })) vi.mock('@/data/projects/project-service-versions', () => ({ useProjectServiceVersionsQuery: mockUseServiceVersions, })) vi.mock('@/data/read-replicas/replicas-query', () => ({ useReadReplicasQuery: mockUseReadReplicas, })) vi.mock('@/hooks/misc/useIsFeatureEnabled', () => ({ useIsFeatureEnabled: mockUseFeatures, })) vi.mock('@/hooks/misc/useSelectedProject', () => ({ useIsOrioleDb: mockUseIsOrioleDb, useSelectedProjectQuery: mockUseProject, useIsAwsK8sCloudProvider: mockIsAwsK8sCloudProvider, })) describe('ServiceVersionsSection', () => { beforeEach(() => { mockUseFeatures.mockReturnValue({ projectAuthAll: true, projectSettingsDatabaseUpgrades: true, }) mockUseIsOrioleDb.mockReturnValue(false) mockUseProject.mockReturnValue({ data: { status: 'ACTIVE_HEALTHY' } }) mockIsAwsK8sCloudProvider.mockReturnValue(false) mockUseReadReplicas.mockReturnValue({ data: [{ identifier: 'project-ref' }] }) mockUseUpgradeEligibility.mockReturnValue({ data: { current_app_version: 'supabase-postgres-15.8.1.060', current_app_version_release_channel: 'ga', latest_app_version: 'supabase-postgres-17.4.1.060', eligible: true, validation_errors: [], warnings: [], }, error: undefined, isPending: false, isError: false, isSuccess: true, }) mockUseServiceVersions.mockReturnValue({ data: { gotrue: '2.180.0', postgrest: '12.2.8', 'supabase-postgres': '15.8.1.060', }, error: undefined, isPending: false, isError: false, isSuccess: true, }) }) it('preserves the paused-project state without showing stale version values', () => { mockUseProject.mockReturnValue({ data: { status: 'INACTIVE' } }) const { container } = render() expect( screen.getByText('Service versions cannot be retrieved while project is paused') ).toBeInTheDocument() expect(screen.queryByDisplayValue('12.2.8')).not.toBeInTheDocument() expect(container.querySelector('#service-versions')).toBeInTheDocument() }) it('renders Auth, PostgREST, and Postgres versions with release metadata', () => { mockUseIsOrioleDb.mockReturnValue(true) mockUseUpgradeEligibility.mockReturnValue({ data: { current_app_version: 'supabase-postgres-17.4.1.060-orioledb', current_app_version_release_channel: 'beta', latest_app_version: 'supabase-postgres-17.4.1.060-orioledb', eligible: true, validation_errors: [], warnings: [], }, error: undefined, isPending: false, isError: false, isSuccess: true, }) render() expect(screen.getByDisplayValue('2.180.0')).toBeInTheDocument() expect(screen.getByDisplayValue('12.2.8')).toBeInTheDocument() expect(screen.getByDisplayValue('17.4.1.060')).toBeInTheDocument() expect(screen.getAllByText('beta').length).toBeGreaterThan(0) expect(screen.getAllByText('OrioleDB').length).toBeGreaterThan(0) expect(screen.getAllByText('Latest').length).toBeGreaterThan(0) expect(screen.getByText('Project upgrade available')).toBeInTheDocument() }) it('shows the read-replica upgrade block instead of the upgrade action', () => { mockUseReadReplicas.mockReturnValue({ data: [{ identifier: 'project-ref' }, { identifier: 'replica-1' }], }) render() expect(screen.getByText('Read replicas block upgrade to 17.4.1.060')).toBeInTheDocument() expect(screen.queryByText('Project upgrade available')).not.toBeInTheDocument() }) it('renders upgrade validation errors and warnings for an ineligible project', () => { mockUseUpgradeEligibility.mockReturnValue({ data: { current_app_version: 'supabase-postgres-15.8.1.060', latest_app_version: 'supabase-postgres-17.4.1.060', eligible: false, validation_errors: ['Remove read replicas'], warnings: ['Review extension compatibility'], }, error: undefined, isPending: false, isError: false, isSuccess: true, }) render() expect(screen.getByText('Validation errors: Remove read replicas')).toBeInTheDocument() expect( screen.getByText('Validation warnings: Review extension compatibility') ).toBeInTheDocument() expect(screen.queryByText('Project upgrade available')).not.toBeInTheDocument() }) it('preserves eligibility and service-version loading and error states', () => { mockUseUpgradeEligibility .mockReturnValueOnce({ data: undefined, error: undefined, isPending: true, isError: false, isSuccess: false, }) .mockReturnValueOnce({ data: undefined, error: new Error('eligibility failed'), isPending: false, isError: true, isSuccess: false, }) const { rerender } = render() expect(screen.getByText('Loading service versions')).toBeInTheDocument() rerender() expect(screen.getByText('Failed to retrieve Postgres version')).toBeInTheDocument() mockUseUpgradeEligibility.mockReturnValue({ data: { current_app_version: 'supabase-postgres-15.8.1.060', latest_app_version: 'supabase-postgres-17.4.1.060', eligible: true, }, error: undefined, isPending: false, isError: false, isSuccess: true, }) mockUseServiceVersions.mockReturnValue({ data: undefined, error: new Error('versions failed'), isPending: false, isError: true, isSuccess: false, }) rerender() expect(screen.getByText('Failed to retrieve versions')).toBeInTheDocument() }) it('does not offer upgrade for v3 projects', () => { mockIsAwsK8sCloudProvider.mockReturnValue(true) render() expect(screen.queryByText('Project upgrade available')).not.toBeInTheDocument() }) })