Files
windmill/frontend/src/lib/workspaceRoles.test.ts
T
ca8f0a16c1 feat: per-role fork capability and fork role mapping on rulesets (#12054)
* feat: fork capability per role and fork role mapping on rulesets

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

* fix: keep unset fork for unrestricted roles and apply fork role mapping to fork-owner adds

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

* feat: fork_roles feature usage telemetry

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

* fix: derive an unset fork in the role editor as the server does

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

* chore: update ee-repo-ref to d535972f0b35c6a7f257720816bdaec18465adef

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

Previous ee-repo-ref: 4a8d59cd1563382fcec85b1367f7e15e09bc13c2

New ee-repo-ref: d535972f0b35c6a7f257720816bdaec18465adef

Automated by sync-ee-ref workflow.

---------

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-28 00:43:33 +01:00

82 lines
3.0 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import type { EffectiveRole } from '$lib/gen'
import {
applyCapabilitiesPatch,
baseCapabilitiesFor,
FULL_CAPABILITIES,
resolveCapabilities,
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')
})
})
describe('resolveCapabilities', () => {
it('derives an unset fork from the restriction, as the server does', () => {
const maintainer = resolveCapabilities(
baseCapabilitiesFor('maintainer'),
{ code: true, flows: 'full', apps: 'full' },
false
)
expect(maintainer.fork).toBe(true)
const developer = resolveCapabilities(baseCapabilitiesFor('developer'), { code: false }, false)
expect(developer.fork).toBe(false)
expect(resolveCapabilities(baseCapabilitiesFor('operator'), { fork: true }, false).fork).toBe(
true
)
})
})