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(), describeUserPoolClient: vi.fn(), updateUserPoolClientOAuth: 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() } 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('lists the pool\'s Lambda triggers with links to the function and its logs', async () => { api.getCloudResource.mockResolvedValue({...pool('email'), metadata: {...pool('email').metadata, lambdaTriggers: [ {trigger: 'PreTokenGeneration', label: 'Pre token generation', functionArn: 'arn:aws:lambda:us-east-1:000000000000:function:notes-demo-pre-token', functionName: 'notes-demo-pre-token', version: null}, ]}}) renderPanel() expect(await screen.findByText('Pre token generation')).toBeInTheDocument() expect(screen.getByRole('link', {name: 'Open function notes-demo-pre-token'})).toHaveAttribute('href', '/cloud-explorer/aws/serverless?resource=notes-demo-pre-token') expect(screen.getByRole('link', {name: 'Open logs for notes-demo-pre-token'})).toHaveAttribute('href', '/cloud-explorer/aws/logs?resource=%2Faws%2Flambda%2Fnotes-demo-pre-token') }) test('says when a pool has no Lambda triggers', async () => { api.getCloudResource.mockResolvedValue(pool('email')) renderPanel() expect(await screen.findByText('This pool runs no Lambda triggers.')).toBeInTheDocument() }) test('turns on hosted sign-in for an app client', async () => { const user = userEvent.setup() const settings = {clientId: 'a8e5c7d38d3b4a1a97c9d822de', clientName: 'orders-users-client', hasSecret: false, explicitAuthFlows: ['ALLOW_REFRESH_TOKEN_AUTH'], callbackUrls: [], logoutUrls: [], oauthFlows: [], oauthScopes: [], identityProviders: ['COGNITO']} api.getCloudResource.mockResolvedValue(pool('email')) api.describeUserPoolClient.mockResolvedValue(settings) api.updateUserPoolClientOAuth.mockImplementation(async (_cloud, _pool, _client, input) => ({...settings, ...input})) renderPanel() await user.click(await screen.findByRole('button', {name: 'Manage app client orders-users-client'})) const form = await screen.findByRole('form', {name: 'Hosted sign-in settings'}) expect(within(form).getByRole('checkbox', {name: /Client credentials/})).toBeDisabled() await user.click(within(form).getByRole('checkbox', {name: /Authorization code/})) await user.click(within(form).getByRole('checkbox', {name: 'openid'})) await user.click(within(form).getByRole('checkbox', {name: 'email'})) await user.type(within(form).getByLabelText(/Callback URLs/), 'http://localhost:4600/api/auth/callback') await user.type(within(form).getByLabelText(/Sign-out URLs/), 'http://localhost:4600/') await user.click(within(form).getByRole('button', {name: 'Save settings'})) expect(api.updateUserPoolClientOAuth).toHaveBeenCalledWith('aws', summary.id, 'a8e5c7d38d3b4a1a97c9d822de', { callbackUrls: ['http://localhost:4600/api/auth/callback'], logoutUrls: ['http://localhost:4600/'], oauthFlows: ['code'], oauthScopes: ['openid', 'email'], identityProviders: ['COGNITO'], }) expect(await screen.findByText(/can now send users to the hosted login page/)).toBeInTheDocument() }) 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'}) })