- Add API tests for validatePublicURL, testPublicURL, previewInviteURL - Add UI tests for Public URL validation states and test button - Add invite URL preview display and debouncing tests - Increase frontend coverage from 34.85% to 87.7% Addresses Codecov coverage gaps in PR #450 Closes coverage requirements for beta release Coverage: 87.7% (1174 tests passing)
190 lines
6.7 KiB
TypeScript
190 lines
6.7 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
import client from '../client'
|
|
import {
|
|
listUsers,
|
|
getUser,
|
|
createUser,
|
|
inviteUser,
|
|
updateUser,
|
|
deleteUser,
|
|
updateUserPermissions,
|
|
validateInvite,
|
|
acceptInvite,
|
|
} from '../users'
|
|
|
|
vi.mock('../client', () => ({
|
|
default: {
|
|
get: vi.fn(),
|
|
post: vi.fn(),
|
|
put: vi.fn(),
|
|
delete: vi.fn(),
|
|
},
|
|
}))
|
|
|
|
describe('users api', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('lists, reads, creates, updates, and deletes users', async () => {
|
|
vi.mocked(client.get).mockResolvedValueOnce({ data: [{ id: 1, email: 'a' }] })
|
|
const users = await listUsers()
|
|
expect(users[0].id).toBe(1)
|
|
expect(client.get).toHaveBeenCalledWith('/users')
|
|
|
|
vi.mocked(client.get).mockResolvedValueOnce({ data: { id: 2 } })
|
|
await getUser(2)
|
|
expect(client.get).toHaveBeenCalledWith('/users/2')
|
|
|
|
vi.mocked(client.post).mockResolvedValueOnce({ data: { id: 3 } })
|
|
await createUser({ email: 'e', name: 'n', password: 'p' })
|
|
expect(client.post).toHaveBeenCalledWith('/users', { email: 'e', name: 'n', password: 'p' })
|
|
|
|
vi.mocked(client.put).mockResolvedValueOnce({ data: { message: 'ok' } })
|
|
await updateUser(2, { enabled: false })
|
|
expect(client.put).toHaveBeenCalledWith('/users/2', { enabled: false })
|
|
|
|
vi.mocked(client.delete).mockResolvedValueOnce({ data: { message: 'deleted' } })
|
|
await deleteUser(2)
|
|
expect(client.delete).toHaveBeenCalledWith('/users/2')
|
|
})
|
|
|
|
it('invites users and updates permissions', async () => {
|
|
vi.mocked(client.post).mockResolvedValueOnce({ data: { invite_token: 't' } })
|
|
await inviteUser({ email: 'i', permission_mode: 'allow_all' })
|
|
expect(client.post).toHaveBeenCalledWith('/users/invite', { email: 'i', permission_mode: 'allow_all' })
|
|
|
|
vi.mocked(client.put).mockResolvedValueOnce({ data: { message: 'saved' } })
|
|
await updateUserPermissions(1, { permission_mode: 'deny_all', permitted_hosts: [1, 2] })
|
|
expect(client.put).toHaveBeenCalledWith('/users/1/permissions', { permission_mode: 'deny_all', permitted_hosts: [1, 2] })
|
|
})
|
|
|
|
it('validates and accepts invites with params', async () => {
|
|
vi.mocked(client.get).mockResolvedValueOnce({ data: { valid: true, email: 'a' } })
|
|
await validateInvite('token-1')
|
|
expect(client.get).toHaveBeenCalledWith('/invite/validate', { params: { token: 'token-1' } })
|
|
|
|
vi.mocked(client.post).mockResolvedValueOnce({ data: { message: 'accepted', email: 'a' } })
|
|
await acceptInvite({ token: 't', name: 'n', password: 'p' })
|
|
expect(client.post).toHaveBeenCalledWith('/invite/accept', { token: 't', name: 'n', password: 'p' })
|
|
})
|
|
|
|
describe('previewInviteURL', () => {
|
|
it('should call POST /users/preview-invite-url with email', async () => {
|
|
const mockResponse = {
|
|
preview_url: 'https://example.com/accept-invite?token=SAMPLE_TOKEN_PREVIEW',
|
|
base_url: 'https://example.com',
|
|
is_configured: true,
|
|
email: 'test@example.com',
|
|
warning: false,
|
|
warning_message: ''
|
|
}
|
|
vi.mocked(client.post).mockResolvedValue({ data: mockResponse })
|
|
|
|
const result = await import('../users').then(m => m.previewInviteURL('test@example.com'))
|
|
|
|
expect(client.post).toHaveBeenCalledWith('/users/preview-invite-url', { email: 'test@example.com' })
|
|
expect(result).toEqual(mockResponse)
|
|
})
|
|
|
|
it('should return complete PreviewInviteURLResponse structure', async () => {
|
|
const mockResponse = {
|
|
preview_url: 'https://charon.example.com/accept-invite?token=SAMPLE_TOKEN_PREVIEW',
|
|
base_url: 'https://charon.example.com',
|
|
is_configured: true,
|
|
email: 'user@test.com',
|
|
warning: false,
|
|
warning_message: ''
|
|
}
|
|
vi.mocked(client.post).mockResolvedValue({ data: mockResponse })
|
|
|
|
const result = await import('../users').then(m => m.previewInviteURL('user@test.com'))
|
|
|
|
expect(result.preview_url).toBeDefined()
|
|
expect(result.base_url).toBeDefined()
|
|
expect(result.is_configured).toBeDefined()
|
|
expect(result.email).toBeDefined()
|
|
expect(result.warning).toBeDefined()
|
|
expect(result.warning_message).toBeDefined()
|
|
})
|
|
|
|
it('should return preview_url with sample token', async () => {
|
|
vi.mocked(client.post).mockResolvedValue({
|
|
data: {
|
|
preview_url: 'http://localhost:8080/accept-invite?token=SAMPLE_TOKEN_PREVIEW',
|
|
base_url: 'http://localhost:8080',
|
|
is_configured: false,
|
|
email: 'test@example.com',
|
|
warning: true,
|
|
warning_message: 'Public URL not configured'
|
|
}
|
|
})
|
|
|
|
const result = await import('../users').then(m => m.previewInviteURL('test@example.com'))
|
|
|
|
expect(result.preview_url).toContain('SAMPLE_TOKEN_PREVIEW')
|
|
})
|
|
|
|
it('should return is_configured flag', async () => {
|
|
vi.mocked(client.post).mockResolvedValue({
|
|
data: {
|
|
preview_url: 'https://example.com/accept-invite?token=SAMPLE_TOKEN_PREVIEW',
|
|
base_url: 'https://example.com',
|
|
is_configured: true,
|
|
email: 'test@example.com',
|
|
warning: false,
|
|
warning_message: ''
|
|
}
|
|
})
|
|
|
|
const result = await import('../users').then(m => m.previewInviteURL('test@example.com'))
|
|
|
|
expect(result.is_configured).toBe(true)
|
|
})
|
|
|
|
it('should return warning flag when public URL not configured', async () => {
|
|
vi.mocked(client.post).mockResolvedValue({
|
|
data: {
|
|
preview_url: 'http://localhost:8080/accept-invite?token=SAMPLE_TOKEN_PREVIEW',
|
|
base_url: 'http://localhost:8080',
|
|
is_configured: false,
|
|
email: 'admin@test.com',
|
|
warning: true,
|
|
warning_message: 'Using default localhost URL'
|
|
}
|
|
})
|
|
|
|
const result = await import('../users').then(m => m.previewInviteURL('admin@test.com'))
|
|
|
|
expect(result.warning).toBe(true)
|
|
expect(result.warning_message).toBe('Using default localhost URL')
|
|
})
|
|
|
|
it('should return the provided email in response', async () => {
|
|
const testEmail = 'specific@email.com'
|
|
vi.mocked(client.post).mockResolvedValue({
|
|
data: {
|
|
preview_url: 'https://example.com/accept-invite?token=SAMPLE_TOKEN_PREVIEW',
|
|
base_url: 'https://example.com',
|
|
is_configured: true,
|
|
email: testEmail,
|
|
warning: false,
|
|
warning_message: ''
|
|
}
|
|
})
|
|
|
|
const result = await import('../users').then(m => m.previewInviteURL(testEmail))
|
|
|
|
expect(result.email).toBe(testEmail)
|
|
})
|
|
|
|
it('should handle request errors', async () => {
|
|
vi.mocked(client.post).mockRejectedValue(new Error('Network error'))
|
|
|
|
await expect(
|
|
import('../users').then(m => m.previewInviteURL('test@example.com'))
|
|
).rejects.toThrow('Network error')
|
|
})
|
|
})
|
|
})
|