mirror of
https://github.com/supabase/supabase.git
synced 2026-10-19 16:19:10 +03:00
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Feature ## Summary - Add a `useProjectHealthLintsQuery` that runs the live health checks (database down, unreachable, connection limit, service error rate, infrastructure alerts) - Surface those results on the project home advisor row alongside security and performance errors - Register health lint metadata (titles, docs links, entity icon) so homepage cards can render them Bottom of the stack. The advisor sidebar still uses tabs; health items show under All until #49662. ## To test 1. Open any project home in Studio. 2. Find the Advisor row (the cards under “Advisor found N issues”). 3. If the project has a real health problem, you should see a **HEALTH** card (for example “Database process is down” or “Database connection limit reached”), not only SECURITY / PERFORMANCE. 4. If the project is healthy, you should **not** see a HEALTH card. Existing security and performance cards should still appear as before. 5. Click a HEALTH card (or any advisor card). Advisor Center should open on that item. 6. In Advisor Center on this PR, health items only show under the **All** tab — Health is not its own tab yet. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added a Health category to Advisor, with a dedicated tab and activity icon. - Added health checks for database availability, connection limits, service errors, and infrastructure alerts. - Health issues now appear alongside security and performance recommendations with relevant troubleshooting links. - **Bug Fixes** - Health-related advisor findings are now correctly categorized and displayed. - **Tests** - Added coverage for health checks, categorization, filtering, and project health query behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
82 lines
2.8 KiB
TypeScript
82 lines
2.8 KiB
TypeScript
import { HttpResponse } from 'msw'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
import { getProjectHealthLints, HEALTH_LINT_NAMES } from './health-lints-query'
|
|
import type { components } from '@/data/api'
|
|
import { addAPIMock } from '@/tests/lib/msw'
|
|
|
|
type AdvisorsResponse = components['schemas']['V2ProjectAdvisorsResponse_Output']
|
|
type AdvisorsRequestBody = components['schemas']['V2RunProjectAdvisorsBody']
|
|
type AdvisorLint = AdvisorsResponse['data']['attributes']['lints'][number]
|
|
|
|
const createLint = (overrides: Partial<AdvisorLint> = {}): AdvisorLint =>
|
|
({
|
|
cache_key: 'instance_db_down',
|
|
name: 'instance_db_down',
|
|
title: 'Database process is down',
|
|
level: 'ERROR',
|
|
facing: 'EXTERNAL',
|
|
categories: ['HEALTH'],
|
|
description: 'The instance is running but Postgres is not accepting connections locally.',
|
|
detail: 'The on-host check of Postgres is failing.',
|
|
remediation: 'https://supabase.com/docs/guides/platform/troubleshooting',
|
|
metadata: { type: 'health', entity: 'Database' },
|
|
...overrides,
|
|
}) as AdvisorLint
|
|
|
|
const mockAdvisorsRun = (lints: AdvisorLint[], onRequest?: (body: AdvisorsRequestBody) => void) => {
|
|
addAPIMock({
|
|
method: 'post',
|
|
path: '/v2/projects/:ref/advisors/run',
|
|
response: async ({ request }) => {
|
|
onRequest?.((await request.json()) as AdvisorsRequestBody)
|
|
return HttpResponse.json<AdvisorsResponse>({
|
|
data: { type: 'project_advisors', attributes: { lints } },
|
|
})
|
|
},
|
|
})
|
|
}
|
|
|
|
describe('getProjectHealthLints', () => {
|
|
it('asks the endpoint for the health lints Studio surfaces', async () => {
|
|
let requestBody: AdvisorsRequestBody | undefined
|
|
mockAdvisorsRun([], (body) => {
|
|
requestBody = body
|
|
})
|
|
|
|
await getProjectHealthLints({ projectRef: 'default' })
|
|
|
|
expect(requestBody?.data.attributes.lints.map((lint) => lint.name)).toEqual([
|
|
...HEALTH_LINT_NAMES,
|
|
])
|
|
})
|
|
|
|
it('returns the health issues the project actually has', async () => {
|
|
mockAdvisorsRun([createLint()])
|
|
|
|
const result = await getProjectHealthLints({ projectRef: 'default' })
|
|
|
|
expect(result.map((lint) => lint.name)).toEqual(['instance_db_down'])
|
|
})
|
|
|
|
it('drops results that report on the check rather than on the project', async () => {
|
|
mockAdvisorsRun([
|
|
createLint(),
|
|
createLint({
|
|
cache_key: 'advisor_check_unavailable:metrics_unavailable',
|
|
name: 'advisor_check_unavailable',
|
|
level: 'INFO',
|
|
}),
|
|
createLint({ cache_key: 'project_not_active', name: 'project_not_active' }),
|
|
])
|
|
|
|
const result = await getProjectHealthLints({ projectRef: 'default' })
|
|
|
|
expect(result.map((lint) => lint.name)).toEqual(['instance_db_down'])
|
|
})
|
|
|
|
it('throws when no project ref is given', async () => {
|
|
await expect(getProjectHealthLints({})).rejects.toThrow('Project ref is required')
|
|
})
|
|
})
|