import {QueryClient, QueryClientProvider} from '@tanstack/react-query' import {render, screen, within} from '@testing-library/react' import userEvent from '@testing-library/user-event' import {beforeEach, expect, test, vi} from 'vitest' import {CertificatePanel} from './CertificatePanel' import type {CloudResource} from '@/types/resource' const api = vi.hoisted(() => ({ getCloudResource: vi.fn(), getCertificatePem: vi.fn(), exportCertificate: vi.fn(), revokeCertificate: vi.fn(), })) vi.mock('@/api/cloudProxyClient', () => api) vi.mock('@/lib/accountStore', () => ({useAccountId: () => '000000000000'})) const ID = '5ed9dc37-d9fb-456e-911e-e87f091480ba' function certificate(metadata: Record = {}, status = 'ISSUED'): CloudResource { return { id: ID, name: 'notes.thecloud.localhost', cloud: 'aws', service: 'certificates', type: 'certificate', region: 'us-east-1', createdAt: null, status, metadata: { certificateType: 'Amazon issued', exportable: false, inUseBy: [], domainValidations: [{domainName: 'notes.thecloud.localhost', status: 'SUCCESS', method: 'DNS', record: {name: '_a1.notes.thecloud.localhost.', type: 'CNAME', value: '_e8.acm-validations.aws.'}}], ...metadata, }, } } function renderPanel(resource: CloudResource) { api.getCloudResource.mockResolvedValue(resource) const client = new QueryClient({defaultOptions: {queries: {retry: false}}}) return render() } beforeEach(() => { vi.clearAllMocks() api.getCertificatePem.mockResolvedValue({certificate: '-----BEGIN CERTIFICATE-----\nLEAF', certificateChain: '-----BEGIN CERTIFICATE-----\nCA'}) api.exportCertificate.mockResolvedValue({certificate: 'LEAF', certificateChain: null, privateKey: '-----BEGIN ENCRYPTED PRIVATE KEY-----'}) api.revokeCertificate.mockResolvedValue(certificate({exportable: true}, 'REVOKED')) }) test('shows the validation records and that nothing uses the certificate', async () => { renderPanel(certificate()) expect(await screen.findByText('_a1.notes.thecloud.localhost.')).toBeInTheDocument() expect(screen.getByText('_e8.acm-validations.aws.')).toBeInTheDocument() expect(screen.getByText(/Nothing uses this certificate/)).toBeInTheDocument() // Export and revoke exist only for certificates requested as exportable, as on AWS. expect(screen.queryByRole('form', {name: 'Export certificate'})).not.toBeInTheDocument() expect(screen.queryByRole('form', {name: 'Revoke certificate'})).not.toBeInTheDocument() }) test('lists what uses the certificate, since ACM will not delete it then', async () => { renderPanel(certificate({inUseBy: ['arn:aws:elasticloadbalancing:us-east-1:000000000000:loadbalancer/app/web/1']})) expect(await screen.findByText('arn:aws:elasticloadbalancing:us-east-1:000000000000:loadbalancer/app/web/1')).toBeInTheDocument() expect(screen.getByText(/will not delete the certificate/)).toBeInTheDocument() }) test('gets the certificate and chain as downloadable PEM files', async () => { const user = userEvent.setup() renderPanel(certificate()) await user.click(await screen.findByRole('button', {name: 'Get certificate'})) expect(await screen.findByRole('textbox', {name: 'Certificate'})).toHaveValue('-----BEGIN CERTIFICATE-----\nLEAF') expect(screen.getByRole('textbox', {name: 'Certificate chain'})).toHaveValue('-----BEGIN CERTIFICATE-----\nCA') expect(screen.getByRole('link', {name: /notes.thecloud.localhost.pem/})).toHaveAttribute('download', 'notes.thecloud.localhost.pem') }) test('exports an exportable certificate with the passphrase, then revokes it', async () => { const user = userEvent.setup() vi.spyOn(window, 'confirm').mockReturnValue(true) renderPanel(certificate({exportable: true})) const exportForm = await screen.findByRole('form', {name: 'Export certificate'}) await user.type(within(exportForm).getByLabelText(/Passphrase/), 'open-sesame') await user.click(within(exportForm).getByRole('button', {name: 'Export'})) expect(api.exportCertificate).toHaveBeenCalledWith('aws', ID, 'open-sesame') expect(await screen.findByRole('textbox', {name: 'Private key (encrypted)'})).toHaveValue('-----BEGIN ENCRYPTED PRIVATE KEY-----') const revokeForm = screen.getByRole('form', {name: 'Revoke certificate'}) await user.selectOptions(within(revokeForm).getByLabelText('Reason'), 'SUPERSEDED') await user.click(within(revokeForm).getByRole('button', {name: 'Revoke'})) expect(api.revokeCertificate).toHaveBeenCalledWith('aws', ID, 'SUPERSEDED') expect(await screen.findByText(/Certificate revoked/)).toBeInTheDocument() })