180 lines
5.0 KiB
TypeScript
180 lines
5.0 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { describe, it } from 'node:test';
|
|
import {
|
|
canDeleteExecutionByIdentity,
|
|
canDeleteTaskByIdentity,
|
|
canEditExecutionByIdentity,
|
|
canEditTaskByIdentity
|
|
} from '../src/views/project/project/execution/composables/task-permission-rules';
|
|
|
|
function createExecution(overrides: Partial<Api.Project.ProjectExecution> = {}): Api.Project.ProjectExecution {
|
|
return {
|
|
id: 'execution-1',
|
|
projectId: 'project-1',
|
|
projectRequirementId: null,
|
|
projectRequirementName: null,
|
|
projectRequirementStatusCode: null,
|
|
executionName: 'execution',
|
|
executionType: null,
|
|
ownerId: 'execution-owner',
|
|
ownerNickname: null,
|
|
statusCode: 'active',
|
|
statusName: null,
|
|
terminal: false,
|
|
allowEdit: true,
|
|
availableActions: [],
|
|
plannedStartDate: null,
|
|
plannedEndDate: null,
|
|
actualStartDate: null,
|
|
actualEndDate: null,
|
|
progressRate: 0,
|
|
priority: '2',
|
|
priorityName: null,
|
|
executionDesc: null,
|
|
lastStatusReason: null,
|
|
createTime: '2026-07-07 10:00:00',
|
|
updateTime: '2026-07-07 10:00:00',
|
|
...overrides
|
|
};
|
|
}
|
|
|
|
function createTask(overrides: Partial<Api.Project.ProjectTask> = {}): Api.Project.ProjectTask {
|
|
return {
|
|
id: 'task-1',
|
|
projectId: 'project-1',
|
|
executionId: 'execution-1',
|
|
executionName: '执行',
|
|
executionStatusCode: 'active',
|
|
parentTaskId: null,
|
|
projectRequirementId: null,
|
|
projectRequirementName: null,
|
|
projectRequirementStatusCode: null,
|
|
taskTitle: '任务',
|
|
type: '',
|
|
ownerId: 'task-owner',
|
|
ownerNickname: null,
|
|
executionOwnerId: 'execution-owner',
|
|
parentTaskOwnerId: null,
|
|
statusCode: 'active',
|
|
statusName: null,
|
|
terminal: false,
|
|
allowEdit: true,
|
|
availableActions: [],
|
|
progressRate: 0,
|
|
plannedStartDate: null,
|
|
plannedEndDate: null,
|
|
actualStartDate: null,
|
|
actualEndDate: null,
|
|
priority: '2',
|
|
priorityName: null,
|
|
taskDesc: null,
|
|
lastStatusReason: null,
|
|
assignees: null,
|
|
attachments: [],
|
|
totalSpentHours: null,
|
|
createTime: '2026-07-07 10:00:00',
|
|
updateTime: '2026-07-07 10:00:00',
|
|
...overrides
|
|
};
|
|
}
|
|
|
|
describe('task mutation permission rules', () => {
|
|
it('allows task owner, execution owner, and project manager to edit mutable tasks', () => {
|
|
const task = createTask();
|
|
|
|
assert.equal(
|
|
canEditTaskByIdentity(task, { currentUserId: 'task-owner', projectManagerUserId: 'project-manager' }),
|
|
true
|
|
);
|
|
assert.equal(
|
|
canEditTaskByIdentity(task, { currentUserId: 'execution-owner', projectManagerUserId: 'project-manager' }),
|
|
true
|
|
);
|
|
assert.equal(
|
|
canEditTaskByIdentity(task, { currentUserId: 'project-manager', projectManagerUserId: 'project-manager' }),
|
|
true
|
|
);
|
|
});
|
|
|
|
it('does not allow unrelated users to edit just because they have a task permission code', () => {
|
|
const task = createTask();
|
|
|
|
assert.equal(
|
|
canEditTaskByIdentity(task, {
|
|
currentUserId: 'developer',
|
|
projectManagerUserId: 'project-manager'
|
|
}),
|
|
false
|
|
);
|
|
});
|
|
|
|
it('uses the same identity rule for delete and only blocks completed tasks by status', () => {
|
|
assert.equal(
|
|
canDeleteTaskByIdentity(createTask({ statusCode: 'active' }), {
|
|
currentUserId: 'execution-owner',
|
|
projectManagerUserId: 'project-manager'
|
|
}),
|
|
true
|
|
);
|
|
assert.equal(
|
|
canDeleteTaskByIdentity(createTask({ statusCode: 'completed' }), {
|
|
currentUserId: 'execution-owner',
|
|
projectManagerUserId: 'project-manager'
|
|
}),
|
|
false
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('execution mutation permission rules', () => {
|
|
it('allows only execution owner and project manager to edit mutable executions', () => {
|
|
const execution = createExecution();
|
|
|
|
assert.equal(
|
|
canEditExecutionByIdentity(execution, {
|
|
currentUserId: 'execution-owner',
|
|
projectManagerUserId: 'project-manager'
|
|
}),
|
|
true
|
|
);
|
|
assert.equal(
|
|
canEditExecutionByIdentity(execution, {
|
|
currentUserId: 'project-manager',
|
|
projectManagerUserId: 'project-manager'
|
|
}),
|
|
true
|
|
);
|
|
assert.equal(
|
|
canEditExecutionByIdentity(execution, {
|
|
currentUserId: 'developer',
|
|
projectManagerUserId: 'project-manager'
|
|
}),
|
|
false
|
|
);
|
|
});
|
|
|
|
it('uses the same identity rule for delete and only blocks completed executions by status', () => {
|
|
assert.equal(
|
|
canDeleteExecutionByIdentity(createExecution({ statusCode: 'active' }), {
|
|
currentUserId: 'execution-owner',
|
|
projectManagerUserId: 'project-manager'
|
|
}),
|
|
true
|
|
);
|
|
assert.equal(
|
|
canDeleteExecutionByIdentity(createExecution({ statusCode: 'active' }), {
|
|
currentUserId: 'developer',
|
|
projectManagerUserId: 'project-manager'
|
|
}),
|
|
false
|
|
);
|
|
assert.equal(
|
|
canDeleteExecutionByIdentity(createExecution({ statusCode: 'completed' }), {
|
|
currentUserId: 'execution-owner',
|
|
projectManagerUserId: 'project-manager'
|
|
}),
|
|
false
|
|
);
|
|
});
|
|
});
|