Zones, Cache, Blocking, DNS Client, Settings, DHCP and Logs can now be changed on one chosen server through the hub, with that server's admin token. Operators manage cache and blocking; admins also zones, settings and DHCP. Under "All servers", or on a server without an admin token, the controls are disabled with a hint. The hub refuses settings/set changes to a server's web service fields and anything about users, SSO or LDAP; backup, restore, log and stats deletion and admin/* stay refused. Non-admins get settings with secrets blanked. Write params travel in the POST body, downloads pass through with a single-use token, and the audit log names no values. Co-Authored-By: Claude Opus 5.5 <[email protected]>
69 lines
2.9 KiB
TypeScript
69 lines
2.9 KiB
TypeScript
import { describe, expect, it, vi, afterEach } from 'vitest'
|
|
import { render, screen } from '@testing-library/react'
|
|
import userEvent from '@testing-library/user-event'
|
|
import { Dhcp } from './Dhcp'
|
|
import * as api from '../../api/dhcp'
|
|
import { HubMode } from '../../ui/ClusterNodeSelect'
|
|
import { HubWritable } from '../../lib/hub-write'
|
|
|
|
afterEach(() => vi.restoreAllMocks())
|
|
|
|
/* Behind a hub the screen starts on the first server, and changes only where the hub can write. */
|
|
|
|
const NODES = [
|
|
{ name: 'mdns', type: 'Primary' },
|
|
{ name: 'ndns', type: 'Secondary' },
|
|
]
|
|
|
|
function hubDhcp(writable: string[], canModify = true) {
|
|
vi.spyOn(api, 'listScopes').mockResolvedValue({ kind: 'ok', data: [] })
|
|
return render(
|
|
<HubMode.Provider value>
|
|
<HubWritable.Provider value={writable}>
|
|
<Dhcp token="t" sub="Scopes" nodes={NODES} clusterInitialised canModify={canModify} canDelete={canModify} />
|
|
</HubWritable.Provider>
|
|
</HubMode.Provider>,
|
|
)
|
|
}
|
|
|
|
describe('DHCP behind a hub', () => {
|
|
it('starts on the first server, named in the request and shown in the selector', async () => {
|
|
hubDhcp(['mdns', 'ndns'])
|
|
await screen.findByText('No Scope Found')
|
|
expect(api.listScopes).toHaveBeenCalledWith('t', 'mdns')
|
|
expect(screen.getByRole('combobox', { name: 'Server' })).toHaveTextContent('mdns')
|
|
expect(screen.queryByRole('option', { name: 'All servers' })).not.toBeInTheDocument()
|
|
expect(screen.getByRole('button', { name: 'Add Scope' })).toBeInTheDocument()
|
|
expect(screen.queryByRole('status')).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('switches changes off on a server the hub holds no admin token for, and says why', async () => {
|
|
const user = userEvent.setup()
|
|
hubDhcp(['mdns'])
|
|
await screen.findByText('No Scope Found')
|
|
await user.click(screen.getByRole('combobox', { name: 'Server' }))
|
|
await user.click(await screen.findByRole('option', { name: 'ndns' }))
|
|
|
|
expect(await screen.findByText(/No admin token configured on the hub/)).toBeInTheDocument()
|
|
expect(api.listScopes).toHaveBeenLastCalledWith('t', 'ndns')
|
|
expect(screen.queryByRole('button', { name: 'Add Scope' })).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('says nothing about the server when the role is what forbids the change', async () => {
|
|
hubDhcp(['mdns'], false)
|
|
await screen.findByText('No Scope Found')
|
|
expect(screen.queryByRole('button', { name: 'Add Scope' })).not.toBeInTheDocument()
|
|
expect(screen.queryByText(/admin token|Choose a server/)).not.toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
describe('DHCP outside a hub', () => {
|
|
it('keeps the empty node and no hint', async () => {
|
|
vi.spyOn(api, 'listScopes').mockResolvedValue({ kind: 'ok', data: [] })
|
|
render(<Dhcp token="t" sub="Scopes" />)
|
|
await screen.findByText('No Scope Found')
|
|
expect(api.listScopes).toHaveBeenCalledWith('t', '')
|
|
expect(screen.getByRole('button', { name: 'Add Scope' })).toBeInTheDocument()
|
|
})
|
|
})
|