Files
floci-ui/packages/frontend/src/components/DataCatalogPanel.test.tsx
T
Zakaria 5eee0b7e9b
CI / checks (pull_request) Successful in 28m25s
feat(api): add Athena workgroups and a query editor to the cloud explorer
Create and delete Athena workgroups (with a result folder, optionally
enforced), and run SQL in one: pick a database from the Glue Data Catalog,
preview a table, run with Ctrl+Enter, cancel a running query, read up to 1,000
rows and download the full result CSV from S3. Recent queries in the workgroup
can be reopened. TheCloud runs the SQL for real over the files a Glue table
points at. The Glue page links to the editor.
2026-10-25 18:10:33 -04:00

93 lines
4.6 KiB
TypeScript

import {QueryClient, QueryClientProvider} from '@tanstack/react-query'
import {render, screen, within} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import {MemoryRouter} from 'react-router-dom'
import {beforeEach, expect, test, vi} from 'vitest'
import {DataCatalogPanel} from './DataCatalogPanel'
import type {CloudResource} from '@/types/resource'
const api = vi.hoisted(() => ({
getCloudResource: vi.fn(),
createCatalogTable: vi.fn(),
deleteCatalogTable: vi.fn(),
}))
vi.mock('@/api/cloudProxyClient', () => api)
vi.mock('@/lib/accountStore', () => ({useAccountId: () => '000000000000'}))
function database(tables: unknown[] = [{
name: 'orders', description: null, format: 'csv', location: 's3://data/orders/', delimiter: ';', headerRow: true,
columns: [{name: 'id', type: 'int'}, {name: 'amount', type: 'double'}], partitionKeys: [], createdAt: null, updatedAt: null,
}]): CloudResource {
return {id: 'shop', name: 'shop', cloud: 'aws', service: 'datacatalog', type: 'glueDatabase', region: null, createdAt: null, metadata: {description: 'Orders', tableCount: tables.length, tables}}
}
function renderPanel(resource: CloudResource) {
api.getCloudResource.mockResolvedValue(resource)
const client = new QueryClient({defaultOptions: {queries: {retry: false}}})
return render(<MemoryRouter><QueryClientProvider client={client}><DataCatalogPanel resource={resource} runtimeReachable/></QueryClientProvider></MemoryRouter>)
}
beforeEach(() => {
vi.clearAllMocks()
api.createCatalogTable.mockResolvedValue(database())
api.deleteCatalogTable.mockResolvedValue(database([]))
})
test('lists tables with their format, location and columns', async () => {
renderPanel(database())
const row = (await screen.findByText('orders')).closest('tr') as HTMLElement
expect(within(row).getByText('s3://data/orders/')).toBeInTheDocument()
expect(within(row).getByText('semicolon-separated, header row')).toBeInTheDocument()
expect(within(row).getByText('amount')).toBeInTheDocument()
expect(screen.getByRole('link', {name: 'Query in Athena'})).toHaveAttribute('href', '/cloud-explorer/aws/query')
})
test('creates a CSV table from the columns entered', async () => {
const user = userEvent.setup()
renderPanel(database([]))
const form = await screen.findByRole('form', {name: 'Create table'})
await user.type(within(form).getByLabelText('Name'), 'events')
const location = within(form).getByLabelText('S3 location')
await user.clear(location)
await user.type(location, 's3://data/events/')
await user.type(within(form).getByLabelText('Column 1 name'), 'id')
await user.selectOptions(within(form).getByLabelText('Column 1 type'), 'int')
await user.click(within(form).getByRole('button', {name: 'Add column'}))
await user.type(within(form).getByLabelText('Column 2 name'), 'kind')
await user.click(within(form).getByRole('button', {name: 'Create table'}))
expect(api.createCatalogTable).toHaveBeenCalledWith('aws', 'shop', {
name: 'events', location: 's3://data/events/', format: 'csv', delimiter: ',', headerRow: true,
columns: [{name: 'id', type: 'int'}, {name: 'kind', type: 'string'}],
})
expect(await screen.findByText('Created table events. Athena can query it now.')).toBeInTheDocument()
})
test('JSON tables need no delimiter or header', async () => {
const user = userEvent.setup()
renderPanel(database([]))
const form = await screen.findByRole('form', {name: 'Create table'})
await user.selectOptions(within(form).getByLabelText('Format'), 'json')
expect(within(form).queryByLabelText('Delimiter')).not.toBeInTheDocument()
await user.type(within(form).getByLabelText('Name'), 'events')
await user.type(within(form).getByLabelText('S3 location'), 'data/events/')
await user.type(within(form).getByLabelText('Column 1 name'), 'kind')
await user.click(within(form).getByRole('button', {name: 'Create table'}))
expect(api.createCatalogTable).toHaveBeenCalledWith('aws', 'shop', {name: 'events', location: 's3://data/events/', format: 'json', columns: [{name: 'kind', type: 'string'}]})
})
test('deletes a table after confirming', async () => {
const user = userEvent.setup()
vi.spyOn(window, 'confirm').mockReturnValue(true)
renderPanel(database())
await user.click(await screen.findByRole('button', {name: 'Delete table orders'}))
expect(api.deleteCatalogTable).toHaveBeenCalledWith('aws', 'shop', 'orders')
expect(await screen.findByText('Deleted table orders. Its files stay in S3.')).toBeInTheDocument()
})