Files
windmill/frontend/src/lib/workspaceRoles.test.ts
T
8dbdfa1949 feat: workspace roles settings UI and capability-based gating (#11824)
* feat: configurable workspace roles with per-workspace overrides

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: close role review gaps in pickers, declarative sync and removal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: keep resolve_role_name doc on its function

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: validate overrides on instance role changes and bill admins fully

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: allow pipeline drafts and keep role checks on one connection

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: list data pipelines in operator_can_draft

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: expect a 403 role refusal when an operator resolves jobs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: require a valid license for custom workspace roles

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: freeze workspace roles when the license lapses

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: pin the ee companion for workspace roles

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor: broadcast role override changes from the write path, not a trigger

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: update ee-repo-ref to b5294d38db082bdd104d5358a47695390694c05d

This commit updates the EE repository reference after PR #891 was merged in windmill-ee-private.

Previous ee-repo-ref: 688e00c5982865b14098f530d54280e2311994c0

New ee-repo-ref: b5294d38db082bdd104d5358a47695390694c05d

Automated by sync-ee-ref workflow.

* feat: add workspace roles settings UI and role pickers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: show the server's reason when a settings save fails

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: default role pickers to a defined role and confirm resets of unknown capabilities

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: treat null role capabilities as unset and key role drafts by map

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: gate the frontend on workspace role capabilities

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: gate deploys, bulk actions and chat composition per role kind

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: treat a superadmin as full in deploy permission checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: leave page visibility out of isRestricted, as the server does

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: let a member discard their own draft without the authoring right

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: show custom roles as inactive without a valid license

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep the user store import on the agent page

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: show custom roles as frozen without a valid license

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: reorder instance roles with a drag handle

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: let an admin row that kept the operator flag hold full rights everywhere

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: treat superadmin members as admins and return role names from get user

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: move the instance role label into the expanded panel

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-10-21 16:31:04 +02:00

65 lines
2.4 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import type { EffectiveRole } from '$lib/gen'
import {
applyCapabilitiesPatch,
baseCapabilitiesFor,
FULL_CAPABILITIES,
validRoleOr,
workspaceRolesErrors
} from './workspaceRoles'
// These mirror `windmill_common::roles`; the server refuses what they let through.
describe('role capability layering', () => {
it('falls back to full for developer and to the operator rights for any other name', () => {
expect(baseCapabilitiesFor('developer')).toEqual(FULL_CAPABILITIES)
const other = baseCapabilitiesFor('reviewer')
expect([other.code, other.flows, other.manage_schedules]).toEqual([false, 'none', true])
})
it('applies only the keys a patch sets', () => {
const caps = applyCapabilitiesPatch(baseCapabilitiesFor('x'), {
flows: 'compose',
visibility: { workers: false }
})
expect(caps.flows).toBe('compose')
expect(caps.apps).toBe('none')
expect(caps.visibility.workers).toBe(false)
expect(caps.visibility.runs).toBe(true)
})
it('treats a stored null as unset, as serde reads it', () => {
const caps = applyCapabilitiesPatch(baseCapabilitiesFor('developer'), {
code: null as any,
visibility: { runs: null as any }
})
expect(caps).toEqual(FULL_CAPABILITIES)
expect(
workspaceRolesErrors([{ name: 'developer', capabilities: { code: null as any } }])
).toEqual({})
})
it('refuses what parse_instance_roles refuses', () => {
expect(workspaceRolesErrors(undefined)).toEqual({})
expect(workspaceRolesErrors([])).toHaveProperty('list')
expect(workspaceRolesErrors([{ name: 'admin' }])).toHaveProperty('0.name')
expect(workspaceRolesErrors([{ name: 'a' }, { name: 'a' }])).toHaveProperty('1.name')
expect(workspaceRolesErrors([{ name: 'x', capabilities: { flows: 'full' } }])).toHaveProperty(
'0.capabilities'
)
expect(
workspaceRolesErrors([{ name: 'developer', capabilities: { apps: 'compose' } }])
).toEqual({})
})
})
describe('validRoleOr', () => {
const role = (name: string) => ({ name }) as EffectiveRole
it('falls back to developer, or the first role when the instance dropped it', () => {
expect(validRoleOr([role('operator'), role('developer')], undefined)).toBe('developer')
expect(validRoleOr([role('builder'), role('viewer')], undefined)).toBe('builder')
expect(validRoleOr([role('builder'), role('viewer')], 'developer')).toBe('builder')
expect(validRoleOr([role('builder')], 'admin')).toBe('admin')
})
})