Adjusting role actions
This commit is contained in:
@@ -3,6 +3,7 @@ import test from 'node:test';
|
||||
|
||||
import {
|
||||
createWorkspace,
|
||||
deleteWorkspaceMember,
|
||||
deleteWorkspaceIfEmpty,
|
||||
ensureDefaultWorkspaceForUser,
|
||||
ensurePersonalWorkspaceForUser,
|
||||
@@ -10,6 +11,7 @@ import {
|
||||
getPlatformAdminSummary,
|
||||
listOwnedWorkspacesByOwnerEmail,
|
||||
updateWorkspace,
|
||||
updateWorkspaceMemberRole,
|
||||
} from './workspaceRepository.js';
|
||||
import { mockDb } from '../test/mockDb.js';
|
||||
import type { UserRow } from '../types.js';
|
||||
@@ -259,6 +261,212 @@ test('listOwnedWorkspacesByOwnerEmail resolves accepted owner flocks by email',
|
||||
assert.match(calls[0].text, /workspaces\.id <> \$2/);
|
||||
});
|
||||
|
||||
test('updateWorkspaceMemberRole changes a non-owner member role', async () => {
|
||||
const { calls } = mockDb({
|
||||
rowCount: 1,
|
||||
rows: [
|
||||
{
|
||||
id: 'member-1',
|
||||
workspace_id: 42,
|
||||
user_id: 'user-2',
|
||||
invite_email: 'helper@example.com',
|
||||
name: 'Helper',
|
||||
role: 'viewer',
|
||||
accepted_at: '2026-04-14T00:00:00.000Z',
|
||||
created_at: '2026-04-14T00:00:00.000Z',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const member = await updateWorkspaceMemberRole({
|
||||
memberId: 'member-1',
|
||||
workspaceId: 42,
|
||||
role: 'viewer',
|
||||
requesterMemberId: 'owner-member',
|
||||
requesterIsBillingOwner: false,
|
||||
requesterRole: 'owner',
|
||||
billingEmail: 'billing@example.com',
|
||||
});
|
||||
|
||||
assert.equal(member?.role, 'viewer');
|
||||
assert.deepEqual(calls[0].params, ['member-1', 42, 'viewer', false, 'owner-member', 'billing@example.com', 'owner']);
|
||||
assert.match(calls[0].text, /UPDATE workspace_members/);
|
||||
assert.match(calls[0].text, /role <> 'owner'/);
|
||||
});
|
||||
|
||||
test('updateWorkspaceMemberRole returns null when no non-owner member matches', async () => {
|
||||
mockDb({
|
||||
rowCount: 0,
|
||||
rows: [],
|
||||
});
|
||||
|
||||
const member = await updateWorkspaceMemberRole({
|
||||
memberId: 'owner-member',
|
||||
workspaceId: 42,
|
||||
role: 'viewer',
|
||||
requesterMemberId: 'owner-member',
|
||||
requesterIsBillingOwner: false,
|
||||
requesterRole: 'owner',
|
||||
billingEmail: 'billing@example.com',
|
||||
});
|
||||
|
||||
assert.equal(member, null);
|
||||
});
|
||||
|
||||
test('updateWorkspaceMemberRole lets the billing owner change another owner role', async () => {
|
||||
const { calls } = mockDb({
|
||||
rowCount: 1,
|
||||
rows: [
|
||||
{
|
||||
id: 'other-owner',
|
||||
workspace_id: 42,
|
||||
user_id: 'user-2',
|
||||
invite_email: 'other@example.com',
|
||||
name: 'Other Owner',
|
||||
role: 'assistant',
|
||||
accepted_at: '2026-04-14T00:00:00.000Z',
|
||||
created_at: '2026-04-14T00:00:00.000Z',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const member = await updateWorkspaceMemberRole({
|
||||
memberId: 'other-owner',
|
||||
workspaceId: 42,
|
||||
role: 'assistant',
|
||||
requesterMemberId: 'billing-owner',
|
||||
requesterIsBillingOwner: true,
|
||||
requesterRole: 'owner',
|
||||
billingEmail: 'billing@example.com',
|
||||
});
|
||||
|
||||
assert.equal(member?.role, 'assistant');
|
||||
assert.deepEqual(calls[0].params, ['other-owner', 42, 'assistant', true, 'billing-owner', 'billing@example.com', 'owner']);
|
||||
assert.match(calls[0].text, /id <> \$5/);
|
||||
});
|
||||
|
||||
test('updateWorkspaceMemberRole does not let the billing owner change their own owner role', async () => {
|
||||
mockDb({
|
||||
rowCount: 0,
|
||||
rows: [],
|
||||
});
|
||||
|
||||
const member = await updateWorkspaceMemberRole({
|
||||
memberId: 'billing-owner',
|
||||
workspaceId: 42,
|
||||
role: 'assistant',
|
||||
requesterMemberId: 'billing-owner',
|
||||
requesterIsBillingOwner: true,
|
||||
requesterRole: 'owner',
|
||||
billingEmail: 'billing@example.com',
|
||||
});
|
||||
|
||||
assert.equal(member, null);
|
||||
});
|
||||
|
||||
test('updateWorkspaceMemberRole lets a non-billing owner change another non-billing owner role', async () => {
|
||||
const { calls } = mockDb({
|
||||
rowCount: 1,
|
||||
rows: [
|
||||
{
|
||||
id: 'other-owner',
|
||||
workspace_id: 42,
|
||||
user_id: 'user-2',
|
||||
invite_email: 'other@example.com',
|
||||
name: 'Other Owner',
|
||||
role: 'assistant',
|
||||
accepted_at: '2026-04-14T00:00:00.000Z',
|
||||
created_at: '2026-04-14T00:00:00.000Z',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const member = await updateWorkspaceMemberRole({
|
||||
memberId: 'other-owner',
|
||||
workspaceId: 42,
|
||||
role: 'assistant',
|
||||
requesterMemberId: 'non-billing-owner',
|
||||
requesterIsBillingOwner: false,
|
||||
requesterRole: 'owner',
|
||||
billingEmail: 'billing@example.com',
|
||||
});
|
||||
|
||||
assert.equal(member?.role, 'assistant');
|
||||
assert.deepEqual(calls[0].params, ['other-owner', 42, 'assistant', false, 'non-billing-owner', 'billing@example.com', 'owner']);
|
||||
assert.match(calls[0].text, /LOWER\(BTRIM\(COALESCE\(invite_email, email\)\)\) <> LOWER\(BTRIM\(\$6\)\)/);
|
||||
});
|
||||
|
||||
test('updateWorkspaceMemberRole does not let a non-billing owner change the billing owner role', async () => {
|
||||
mockDb({
|
||||
rowCount: 0,
|
||||
rows: [],
|
||||
});
|
||||
|
||||
const member = await updateWorkspaceMemberRole({
|
||||
memberId: 'billing-owner',
|
||||
workspaceId: 42,
|
||||
role: 'assistant',
|
||||
requesterMemberId: 'non-billing-owner',
|
||||
requesterIsBillingOwner: false,
|
||||
requesterRole: 'owner',
|
||||
billingEmail: 'billing@example.com',
|
||||
});
|
||||
|
||||
assert.equal(member, null);
|
||||
});
|
||||
|
||||
test('deleteWorkspaceMember removes non-owner members without billing owner access', async () => {
|
||||
const { calls } = mockDb({
|
||||
rowCount: 1,
|
||||
rows: [{ id: 'member-1' }],
|
||||
});
|
||||
|
||||
const deleted = await deleteWorkspaceMember({
|
||||
memberId: 'member-1',
|
||||
workspaceId: 42,
|
||||
requesterMemberId: 'owner-member',
|
||||
requesterIsBillingOwner: false,
|
||||
});
|
||||
|
||||
assert.equal(deleted, true);
|
||||
assert.deepEqual(calls[0].params, ['member-1', 42, false, 'owner-member']);
|
||||
assert.match(calls[0].text, /role <> 'owner'/);
|
||||
});
|
||||
|
||||
test('deleteWorkspaceMember lets the billing owner remove another owner', async () => {
|
||||
const { calls } = mockDb({
|
||||
rowCount: 1,
|
||||
rows: [{ id: 'other-owner' }],
|
||||
});
|
||||
|
||||
const deleted = await deleteWorkspaceMember({
|
||||
memberId: 'other-owner',
|
||||
workspaceId: 42,
|
||||
requesterMemberId: 'billing-owner',
|
||||
requesterIsBillingOwner: true,
|
||||
});
|
||||
|
||||
assert.equal(deleted, true);
|
||||
assert.deepEqual(calls[0].params, ['other-owner', 42, true, 'billing-owner']);
|
||||
assert.match(calls[0].text, /id <> \$4/);
|
||||
});
|
||||
|
||||
test('deleteWorkspaceMember does not let the billing owner remove their own owner membership', async () => {
|
||||
mockDb({
|
||||
rowCount: 0,
|
||||
rows: [],
|
||||
});
|
||||
|
||||
const deleted = await deleteWorkspaceMember({
|
||||
memberId: 'billing-owner',
|
||||
workspaceId: 42,
|
||||
requesterMemberId: 'billing-owner',
|
||||
requesterIsBillingOwner: true,
|
||||
});
|
||||
|
||||
assert.equal(deleted, false);
|
||||
});
|
||||
|
||||
test('getPlatformAdminSummary counts memorialized birds separately', async () => {
|
||||
const { calls } = mockDb({
|
||||
rowCount: 1,
|
||||
|
||||
Reference in New Issue
Block a user