Files
floci-ui/packages/frontend/src/components/CognitoUsersPanel.test.tsx
T
Zakaria bafe799d00
CI / checks (pull_request) Failing after 12m24s
feat(frontend): manage Cognito users and groups, and test sign-in
The Cognito panel gains a test sign-in that shows the decoded ID and
access token claims, a groups section, and a per-user panel to enable or
disable, confirm, sign out everywhere, reset or set a password and change
group membership.

Delete confirmations (row and bulk) now say what else goes, driven by
the schema's deleteImpact, and the inspector follows list refreshes
instead of the click-time snapshot.
2026-10-23 17:54:15 -04:00

154 lines
7.5 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 {beforeEach, expect, test, vi} from 'vitest'
import {CognitoUsersPanel} from './CognitoUsersPanel'
import type {CloudResource} from '@/types/resource'
const api = vi.hoisted(() => ({
deleteFlatItem: vi.fn(),
getCloudResource: vi.fn(),
listFlatItems: vi.fn(),
listUserPoolGroups: vi.fn(),
putFlatItem: vi.fn(),
getFlatItem: vi.fn(),
updateUserPoolUser: vi.fn(),
setUserPoolUserGroups: vi.fn(),
createUserPoolGroup: vi.fn(),
deleteUserPoolGroup: vi.fn(),
testUserPoolSignIn: vi.fn(),
}))
vi.mock('@/api/cloudProxyClient', () => api)
vi.mock('@/lib/accountStore', () => ({useAccountId: () => '000000000000'}))
const summary: CloudResource = {
id: 'us-east-1_8e65d229a', name: 'orders-users', cloud: 'aws', service: 'userpools', type: 'user-pool',
region: 'us-east-1', createdAt: null, metadata: {},
}
function pool(signInWith: 'email' | 'username'): CloudResource {
return {...summary, metadata: {signInWith, appClients: [{clientId: 'a8e5c7d38d3b4a1a97c9d822de', clientName: 'orders-users-client'}]}}
}
const alice = {
id: 'aa36ecc1-ed8a-43b5-84e9-988398c17436', collectionId: null, timestamp: '2026-10-23T16:01:22.000Z', metadata: {},
body: {username: 'aa36ecc1-ed8a-43b5-84e9-988398c17436', email: 'alice@example.com', status: 'CONFIRMED', enabled: true},
}
function renderPanel() {
const client = new QueryClient({defaultOptions: {queries: {retry: false}}})
return render(<QueryClientProvider client={client}><CognitoUsersPanel resource={summary} runtimeReachable/></QueryClientProvider>)
}
beforeEach(() => {
vi.clearAllMocks()
api.listFlatItems.mockResolvedValue({items: [alice], nextCursor: null})
api.putFlatItem.mockResolvedValue(alice)
api.deleteFlatItem.mockResolvedValue(undefined)
api.listUserPoolGroups.mockResolvedValue([{name: 'admins', description: 'Full access', precedence: 0, createdAt: null}])
api.getFlatItem.mockResolvedValue({...alice, body: {...alice.body, emailVerified: true, groups: [], attributes: {sub: alice.id, email: 'alice@example.com'}}})
api.updateUserPoolUser.mockResolvedValue(undefined)
api.setUserPoolUserGroups.mockResolvedValue(undefined)
api.createUserPoolGroup.mockResolvedValue({name: 'editors', description: null, precedence: null, createdAt: null})
})
test('shows the app client id and the pool users', async () => {
api.getCloudResource.mockResolvedValue(pool('email'))
renderPanel()
expect(await screen.findByText('a8e5c7d38d3b4a1a97c9d822de')).toBeInTheDocument()
expect(screen.getByRole('button', {name: 'Copy Client ID'})).toBeInTheDocument()
expect(await screen.findByText('alice@example.com')).toBeInTheDocument()
expect(screen.getByText('CONFIRMED')).toBeInTheDocument()
expect(api.getCloudResource).toHaveBeenCalledWith('aws', 'userpools', summary.id, expect.anything())
})
test('creates an email user who can sign in immediately', async () => {
const user = userEvent.setup()
api.getCloudResource.mockResolvedValue(pool('email'))
renderPanel()
await screen.findByText('a8e5c7d38d3b4a1a97c9d822de')
const form = screen.getByRole('form', {name: 'Create user'})
await user.type(within(form).getByLabelText('Email'), 'bob@example.com')
await user.type(within(form).getByLabelText('Password'), 'Passw0rd!')
await user.click(within(form).getByRole('button', {name: 'Create user'}))
expect(api.putFlatItem).toHaveBeenCalledWith('aws', 'userpools', summary.id, {email: 'bob@example.com', password: 'Passw0rd!', permanent: true})
})
test('creates a username user with a temporary password when asked', async () => {
const user = userEvent.setup()
api.getCloudResource.mockResolvedValue(pool('username'))
renderPanel()
await screen.findByText('a8e5c7d38d3b4a1a97c9d822de')
const form = screen.getByRole('form', {name: 'Create user'})
await user.type(within(form).getByLabelText('Username'), 'bob')
await user.type(within(form).getByLabelText('Password'), 'Passw0rd!')
await user.click(within(form).getByLabelText(/User can sign in now/))
await user.click(within(form).getByRole('button', {name: 'Create user'}))
expect(api.putFlatItem).toHaveBeenCalledWith('aws', 'userpools', summary.id, {username: 'bob', password: 'Passw0rd!', permanent: false})
})
test('deletes a user by its stored username', async () => {
const user = userEvent.setup()
api.getCloudResource.mockResolvedValue(pool('email'))
renderPanel()
await user.click(await screen.findByRole('button', {name: 'Delete user alice@example.com'}))
expect(api.deleteFlatItem).toHaveBeenCalledWith('aws', 'userpools', summary.id, alice.id)
})
test('manages a user: disables it and puts it in a group', async () => {
const user = userEvent.setup()
api.getCloudResource.mockResolvedValue(pool('email'))
renderPanel()
await user.click(await screen.findByRole('button', {name: 'Manage user alice@example.com'}))
await user.click(await screen.findByRole('button', {name: 'Disable'}))
expect(api.updateUserPoolUser).toHaveBeenCalledWith('aws', summary.id, alice.id, {action: 'disable'})
expect(await screen.findByText(/User disabled/)).toBeInTheDocument()
const groupBox = screen.getByRole('checkbox', {name: 'admins'})
await user.click(groupBox)
await user.click(screen.getByRole('button', {name: 'Save groups'}))
expect(api.setUserPoolUserGroups).toHaveBeenCalledWith('aws', summary.id, alice.id, ['admins'])
})
test('creates a group with its precedence', async () => {
const user = userEvent.setup()
api.getCloudResource.mockResolvedValue(pool('email'))
renderPanel()
await user.type(await screen.findByLabelText('Group name'), 'editors')
await user.type(screen.getByLabelText(/^Precedence/), '5')
await user.click(screen.getByRole('button', {name: 'Create group'}))
expect(api.createUserPoolGroup).toHaveBeenCalledWith('aws', summary.id, {name: 'editors', precedence: 5})
})
test('test sign-in shows the decoded claims, or why Cognito refused', async () => {
const user = userEvent.setup()
api.getCloudResource.mockResolvedValue(pool('email'))
api.testUserPoolSignIn
.mockResolvedValueOnce({outcome: 'rejected', code: 'NotAuthorizedException', message: 'Incorrect username or password.'})
.mockResolvedValueOnce({outcome: 'signed-in', expiresIn: 3600, idToken: {sub: alice.id, email: 'alice@example.com', 'cognito:groups': ['admins']}, accessToken: {sub: alice.id, client_id: 'a8e5c7d38d3b4a1a97c9d822de'}})
renderPanel()
await screen.findByText('a8e5c7d38d3b4a1a97c9d822de')
const form = screen.getByRole('form', {name: 'Test sign-in'})
await user.type(within(form).getByLabelText('Email'), 'alice@example.com')
await user.type(within(form).getByLabelText('Password'), 'wrong')
await user.click(within(form).getByRole('button', {name: 'Try sign-in'}))
expect(await screen.findByText('Incorrect username or password.')).toBeInTheDocument()
await user.click(within(form).getByRole('button', {name: 'Try sign-in'}))
expect(await screen.findByText(/valid for 60 minutes/)).toBeInTheDocument()
expect(screen.getByText('cognito:groups', {selector: 'th code'})).toBeInTheDocument()
expect(api.testUserPoolSignIn).toHaveBeenLastCalledWith('aws', summary.id, {clientId: 'a8e5c7d38d3b4a1a97c9d822de', username: 'alice@example.com', password: 'wrong'})
})