diff --git a/frontend/src/components/FleetView/__tests__/NodeCard.test.tsx b/frontend/src/components/FleetView/__tests__/NodeCard.test.tsx index 2f811daa..c0d1b726 100644 --- a/frontend/src/components/FleetView/__tests__/NodeCard.test.tsx +++ b/frontend/src/components/FleetView/__tests__/NodeCard.test.tsx @@ -55,23 +55,24 @@ describe('NodeCard', () => { expect(screen.queryByText('Running')).not.toBeInTheDocument(); }); - it('hides the actions menu when the user lacks node:manage and no edit/delete affordances', () => { - useAuthMock.mockReturnValue({ isAdmin: false, can: vi.fn(() => false) }); + it('hides the actions menu for a free-tier user', () => { useLicenseMock.mockReturnValue({ isPaid: false }); render(); expect(screen.queryByRole('button', { name: 'Node actions' })).not.toBeInTheDocument(); }); - it('exposes the actions menu (cordon entry point) for a Community admin with node:manage', () => { - useLicenseMock.mockReturnValue({ isPaid: false }); + it('exposes the actions menu (cordon entry point) for a paid admin', () => { + useLicenseMock.mockReturnValue({ isPaid: true }); render(); + // With no edit/delete affordances wired, the menu renders iff cordon is + // allowed: isPaid && can('node:manage'). The admin's can() returns true. expect(screen.getByRole('button', { name: 'Node actions' })).toBeInTheDocument(); }); it('exposes the cordon control for a node-admin via the node:manage permission', async () => { const can = vi.fn((action: string) => action === 'node:manage'); useAuthMock.mockReturnValue({ isAdmin: false, can }); - useLicenseMock.mockReturnValue({ isPaid: false }); + useLicenseMock.mockReturnValue({ isPaid: true }); render(); await userEvent.click(screen.getByRole('button', { name: 'Node actions' })); @@ -79,18 +80,18 @@ describe('NodeCard', () => { expect(can).toHaveBeenCalledWith('node:manage', 'node', '2'); }); - it('hides the cordon control from a user lacking node:manage', () => { + it('hides the cordon control from a paid user lacking node:manage', () => { useAuthMock.mockReturnValue({ isAdmin: false, can: vi.fn(() => false) }); - useLicenseMock.mockReturnValue({ isPaid: false }); + useLicenseMock.mockReturnValue({ isPaid: true }); render(); - // node:manage alone must not surface cordon when false to a deployer/viewer/auditor. + // The paid tier alone must not surface cordon to a deployer/viewer/auditor. expect(screen.queryByRole('button', { name: 'Node actions' })).not.toBeInTheDocument(); }); it('shows Uncordon when the node is already cordoned', async () => { const can = vi.fn((action: string) => action === 'node:manage'); useAuthMock.mockReturnValue({ isAdmin: false, can }); - useLicenseMock.mockReturnValue({ isPaid: false }); + useLicenseMock.mockReturnValue({ isPaid: true }); render(); await userEvent.click(screen.getByRole('button', { name: 'Node actions' })); @@ -103,13 +104,13 @@ describe('NodeCard', () => { }; it('renders the update button for an admin when an update is available', () => { - useAuthMock.mockReturnValue({ isAdmin: true, can: vi.fn(() => true) }); + useAuthMock.mockReturnValue({ isAdmin: true }); render(); expect(screen.getByRole('button', { name: /Update/ })).toBeInTheDocument(); }); it('hides the update button for a non-admin but still shows the read-only badge', () => { - useAuthMock.mockReturnValue({ isAdmin: false, can: vi.fn(() => false) }); + useAuthMock.mockReturnValue({ isAdmin: false }); render(); expect(screen.queryByRole('button', { name: /Update/ })).not.toBeInTheDocument(); expect(screen.getByText('Update available')).toBeInTheDocument();