mirror of
https://github.com/supabase/supabase.git
synced 2026-10-07 10:25:06 +03:00
Disables network bans for v3 (`AWS_K8S`) projects and shows a specific unsupported notice. The shared banned-IP query waits for project details and skips unsupported projects, covering both Database Settings and Advisor for v3 and High Availability projects. The hook returns the standard query result and uses `skipToken` to prevent unsupported requests, including manual refetches. Database Settings handles project-detail errors at the call site. Open unban confirmations are cleared when the section becomes disabled, and submission checks eligibility. Addresses [FE-4483](https://linear.app/supabase/issue/FE-4483/disable-network-bans-for-v3-aws-k8s-projects). ## To test - Open Database Settings on a v3 project. Check that Network bans shows the v3 notice, hides the IP list and unban controls, and makes no network-bans retrieval request on initial load or reload, including while Advisor is mounted. - Check that an HA project still shows its existing notice and makes no network-bans retrieval request on initial load or reload. - Navigate from a supported project to a v3 or HA project and check that no banned-IP request is sent for the unsupported project and no banned-IP signals from the previous project appear in Advisor. - If project details fail without cached data, check that Network bans shows an error after retries finish and does not retrieve bans. A successful retry should restore normal behavior. - Open an unban confirmation on a supported project, then navigate to a v3 or HA project. Check that the dialog closes without sending an unban request and stays closed when returning. A newly opened confirmation should still work. - On a supported project, check the empty state and banned IP list. Confirm that users with permission can unban an IP and users without permission see a disabled button with the permissions tooltip. Validation: 17 focused tests passed, covering automatic and manual request suppression, project-detail error display and recovery, and navigation between supported and unsupported projects. Changed-file ESLint, Prettier, and full Studio typecheck (without the incremental cache) passed. Earlier local browser checks on `9912c6c` confirmed no retrieval requests for an HA project on AWS_K8S across reloads and Advisor, and a successful empty state on a supported project. The local failed-project case redirected to the organization after retries, so the inline error remains verified by the component test only. The latest preview, standalone v3 notice, populated bans/unban, and no-permission tooltip still need browser verification. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Banned IP settings now show an unsupported-project notice for AWS Kubernetes projects and hide ban lists and unban actions for AWS Kubernetes and High Availability projects. * Banned IP data loads only after project details are available and only for supported projects; unsupported projects do not display cached ban data. * Project-detail errors are shown separately from ban-list errors. * Unban confirmations close when a project becomes unsupported or an unban succeeds. Unbanning is unavailable when you lack update permission or the project is unsupported. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
135 lines
4.8 KiB
TypeScript
135 lines
4.8 KiB
TypeScript
import { QueryClient } from '@tanstack/react-query'
|
|
import { fireEvent, screen, waitFor } from '@testing-library/react'
|
|
import { mockAnimationsApi } from 'jsdom-testing-mocks'
|
|
import { HttpResponse } from 'msw'
|
|
import { beforeEach, expect, test, vi } from 'vitest'
|
|
|
|
import { BannedIPs } from './BannedIPs'
|
|
import type { deleteBannedIPs } from '@/data/banned-ips/banned-ips-delete-mutations'
|
|
import type { IPData } from '@/data/banned-ips/banned-ips-query'
|
|
import type { ProjectDetail } from '@/data/projects/project-detail-query'
|
|
import { customRender } from '@/tests/lib/custom-render'
|
|
import { addAPIMock, type APIErrorBody } from '@/tests/lib/msw'
|
|
|
|
mockAnimationsApi()
|
|
|
|
const routeParams = vi.hoisted(() => ({ ref: 'default' }))
|
|
beforeEach(() => {
|
|
routeParams.ref = 'default'
|
|
})
|
|
|
|
vi.mock('common', async (importOriginal) => ({
|
|
...(await importOriginal<typeof import('common')>()),
|
|
IS_PLATFORM: true,
|
|
useParams: () => routeParams,
|
|
}))
|
|
|
|
vi.mock('@/lib/constants', async (importOriginal) => ({
|
|
...(await importOriginal<typeof import('@/lib/constants')>()),
|
|
IS_PLATFORM: true,
|
|
}))
|
|
|
|
vi.mock('@/hooks/misc/useCheckPermissions', () => ({
|
|
useAsyncCheckPermissions: () => ({ can: true }),
|
|
}))
|
|
|
|
vi.mock('@/lib/telemetry/track', () => ({ useTrack: () => vi.fn() }))
|
|
|
|
test('shows a project-details error instead of leaving Network bans loading', async () => {
|
|
addAPIMock({
|
|
method: 'get',
|
|
path: '/platform/projects/:ref',
|
|
response: () =>
|
|
HttpResponse.json<APIErrorBody>({ message: 'Project unavailable' }, { status: 500 }),
|
|
})
|
|
|
|
customRender(<BannedIPs />)
|
|
|
|
expect(await screen.findByText('Failed to retrieve project details')).toBeVisible()
|
|
expect(screen.getByText('Error: Project unavailable')).toBeVisible()
|
|
expect(screen.getByRole('link', { name: 'Contact support' })).toHaveAttribute(
|
|
'href',
|
|
expect.stringContaining('projectRef=default')
|
|
)
|
|
expect(screen.queryByRole('button', { name: 'Unban IP' })).not.toBeInTheDocument()
|
|
expect(
|
|
screen.queryByText('There are no banned IP addresses for your project')
|
|
).not.toBeInTheDocument()
|
|
})
|
|
|
|
const PROJECT: ProjectDetail = {
|
|
cloud_provider: 'AWS',
|
|
connectionString: 'postgresql://postgres:password@db.default.supabase.co:5432/postgres',
|
|
db_host: 'db.default.supabase.co',
|
|
dbVersion: 'supabase-postgres-15.1.0',
|
|
high_availability: false,
|
|
id: 1,
|
|
infra_compute_size: 'micro',
|
|
inserted_at: '2026-01-01T00:00:00.000Z',
|
|
integration_source: null,
|
|
is_branch_enabled: false,
|
|
is_physical_backups_enabled: false,
|
|
name: 'Test project',
|
|
organization_id: 1,
|
|
ref: 'default',
|
|
region: 'us-east-1',
|
|
restUrl: 'https://default.supabase.co',
|
|
status: 'ACTIVE_HEALTHY',
|
|
subscription_id: 'subscription-1',
|
|
updated_at: '2026-01-01T00:00:00.000Z',
|
|
}
|
|
|
|
test.each([
|
|
{ name: 'v3', cloud_provider: 'AWS_K8S', high_availability: false },
|
|
{ name: 'HA', cloud_provider: 'AWS', high_availability: true },
|
|
])('clears an open unban confirmation when navigating to $name', async (unsupported) => {
|
|
let bannedIPs: IPData = { banned_ipv4_addresses: ['203.0.113.10'] }
|
|
const unbanRequests: unknown[] = []
|
|
addAPIMock({
|
|
method: 'get',
|
|
path: '/platform/projects/:ref',
|
|
response: ({ params }) =>
|
|
HttpResponse.json<ProjectDetail>(
|
|
params.ref === 'default' ? PROJECT : { ...PROJECT, ...unsupported, ref: String(params.ref) }
|
|
),
|
|
})
|
|
addAPIMock({
|
|
method: 'post',
|
|
path: '/v1/projects/:ref/network-bans/retrieve',
|
|
response: () => HttpResponse.json<IPData>(bannedIPs),
|
|
})
|
|
addAPIMock({
|
|
method: 'delete',
|
|
path: '/v1/projects/:ref/network-bans',
|
|
response: async ({ request }) => {
|
|
unbanRequests.push(await request.json())
|
|
bannedIPs = { banned_ipv4_addresses: [] }
|
|
return HttpResponse.json<Awaited<ReturnType<typeof deleteBannedIPs>>>(null)
|
|
},
|
|
})
|
|
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } })
|
|
const { rerender } = customRender(<BannedIPs />, { queryClient })
|
|
|
|
fireEvent.click(await screen.findByRole('button', { name: 'Unban IP' }))
|
|
expect(await screen.findByRole('dialog', { name: 'Confirm Unban IP' })).toBeVisible()
|
|
|
|
routeParams.ref = 'unsupported'
|
|
rerender(<BannedIPs />)
|
|
|
|
await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument())
|
|
expect(screen.queryByRole('button', { name: 'Unban IP' })).not.toBeInTheDocument()
|
|
expect(unbanRequests).toEqual([])
|
|
|
|
routeParams.ref = 'default'
|
|
rerender(<BannedIPs />)
|
|
|
|
const unbanButton = await screen.findByRole('button', { name: 'Unban IP' })
|
|
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
|
|
expect(unbanRequests).toEqual([])
|
|
|
|
fireEvent.click(unbanButton)
|
|
fireEvent.click(await screen.findByRole('button', { name: 'Confirm Unban' }))
|
|
expect(await screen.findByText('There are no banned IP addresses for your project')).toBeVisible()
|
|
expect(unbanRequests).toEqual([{ ipv4_addresses: ['203.0.113.10'] }])
|
|
})
|