diff --git a/src/models/action-center/tasks.types.ts b/src/models/action-center/tasks.types.ts index dfa2c205..cdb1cf21 100644 --- a/src/models/action-center/tasks.types.ts +++ b/src/models/action-center/tasks.types.ts @@ -94,7 +94,7 @@ export interface TaskActivity { activityType: TaskActivityType; creatorUserId: number; targetUserId: number | null; - creationTime: string; + createdTime: string; } export interface TaskSlaDetail { @@ -121,20 +121,21 @@ export interface TaskBaseResponse { folderId: number; key: string; isDeleted: boolean; - creationTime: string; + createdTime: string; id: number; action: string | null; externalTag: string | null; lastAssignedTime: string | null; - completionTime: string | null; + completedTime: string | null; parentOperationId: string | null; deleterUserId: number | null; - deletionTime: string | null; - lastModificationTime: string | null; + deletedTime: string | null; + lastModifiedTime: string | null; } export interface TaskCreateOptions { title: string; + data?: Record; priority?: TaskPriority; } diff --git a/tests/unit/models/action-center/index.ts b/tests/unit/models/action-center/index.ts new file mode 100644 index 00000000..96bac153 --- /dev/null +++ b/tests/unit/models/action-center/index.ts @@ -0,0 +1,5 @@ +/** + * Action Center models test exports + */ + +export * from './tasks.test'; \ No newline at end of file diff --git a/tests/unit/models/action-center/tasks.test.ts b/tests/unit/models/action-center/tasks.test.ts new file mode 100644 index 00000000..29002549 --- /dev/null +++ b/tests/unit/models/action-center/tasks.test.ts @@ -0,0 +1,304 @@ +// ===== IMPORTS ===== +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import { createTaskWithMethods } from '../../../../src/models/action-center/tasks.models'; +import type { TaskServiceModel } from '../../../../src/models/action-center/tasks.models'; +import { TaskType, TaskPriority} from '../../../../src/models/action-center/tasks.types'; +import { createBasicTask } from '../../../utils/mocks/tasks'; +import { createMockOperationResponse } from '../../../utils/mocks/core'; +import { TASK_TEST_CONSTANTS } from '../../../utils/constants/tasks'; +import { TEST_CONSTANTS } from '../../../utils/constants/common'; + +// ===== TEST SUITE ===== +describe('Task Models', () => { + let mockService: TaskServiceModel; + + beforeEach(() => { + // Create a mock service + mockService = { + assign: vi.fn(), + reassign: vi.fn(), + unassign: vi.fn(), + complete: vi.fn(), + create: vi.fn(), + getAll: vi.fn(), + getById: vi.fn(), + getUsers: vi.fn(), + } as any; + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + describe('bound methods on task', () => { + describe('task.assign()', () => { + it('should call task.assign with userId', async () => { + const taskData = createBasicTask(); + const task = createTaskWithMethods(taskData, mockService); + + const mockResponse = createMockOperationResponse([ + { taskId: TASK_TEST_CONSTANTS.TASK_ID, userId: TASK_TEST_CONSTANTS.USER_ID } + ]); + mockService.assign = vi.fn().mockResolvedValue(mockResponse); + + + const result = await task.assign({ userId: TASK_TEST_CONSTANTS.USER_ID }); + + + expect(mockService.assign).toHaveBeenCalledWith({ + taskId: TASK_TEST_CONSTANTS.TASK_ID, + userId: TASK_TEST_CONSTANTS.USER_ID + }); + expect(result).toEqual(mockResponse); + }); + + it('should call task.assign with userNameOrEmail', async () => { + const taskData = createBasicTask(); + const task = createTaskWithMethods(taskData, mockService); + + const mockResponse = createMockOperationResponse([ + { taskId: TASK_TEST_CONSTANTS.TASK_ID, userNameOrEmail: TASK_TEST_CONSTANTS.USER_EMAIL } + ]); + mockService.assign = vi.fn().mockResolvedValue(mockResponse); + + + const result = await task.assign({ userNameOrEmail: TASK_TEST_CONSTANTS.USER_EMAIL }); + + + expect(mockService.assign).toHaveBeenCalledWith({ + taskId: TASK_TEST_CONSTANTS.TASK_ID, + userNameOrEmail: TASK_TEST_CONSTANTS.USER_EMAIL + }); + expect(result).toEqual(mockResponse); + }); + + it('should throw error if taskId is undefined', async () => { + const taskData = createBasicTask({ id: undefined }); + const task = createTaskWithMethods(taskData, mockService); + + + await expect(task.assign({ userId: TASK_TEST_CONSTANTS.USER_ID })).rejects.toThrow('Task ID is undefined'); + }); + }); + + describe('task.reassign()', () => { + it('should call task.reassign with userId', async () => { + const taskData = createBasicTask(); + const task = createTaskWithMethods(taskData, mockService); + + const mockResponse = createMockOperationResponse([ + { taskId: TASK_TEST_CONSTANTS.TASK_ID, userId: TASK_TEST_CONSTANTS.USER_ID } + ]); + mockService.reassign = vi.fn().mockResolvedValue(mockResponse); + + + const result = await task.reassign({ userId: TASK_TEST_CONSTANTS.USER_ID }); + + + expect(mockService.reassign).toHaveBeenCalledWith({ + taskId: TASK_TEST_CONSTANTS.TASK_ID, + userId: TASK_TEST_CONSTANTS.USER_ID + }); + expect(result).toEqual(mockResponse); + }); + + it('should call task.reassign with userNameOrEmail', async () => { + const taskData = createBasicTask(); + const task = createTaskWithMethods(taskData, mockService); + + const mockResponse = createMockOperationResponse([ + { taskId: TASK_TEST_CONSTANTS.TASK_ID, userNameOrEmail: TASK_TEST_CONSTANTS.USER_EMAIL } + ]); + mockService.reassign = vi.fn().mockResolvedValue(mockResponse); + + + const result = await task.reassign({ userNameOrEmail: TASK_TEST_CONSTANTS.USER_EMAIL }); + + + expect(mockService.reassign).toHaveBeenCalledWith({ + taskId: TASK_TEST_CONSTANTS.TASK_ID, + userNameOrEmail: TASK_TEST_CONSTANTS.USER_EMAIL + }); + expect(result).toEqual(mockResponse); + }); + + it('should throw error if taskId is undefined', async () => { + const taskData = createBasicTask({ id: undefined }); + const task = createTaskWithMethods(taskData, mockService); + + + await expect(task.reassign({ userId: TASK_TEST_CONSTANTS.USER_ID })).rejects.toThrow('Task ID is undefined'); + }); + }); + + describe('task.unassign()', () => { + it('should call task.unassign', async () => { + const taskData = createBasicTask(); + const task = createTaskWithMethods(taskData, mockService); + + const mockResponse = createMockOperationResponse([ + { taskId: TASK_TEST_CONSTANTS.TASK_ID } + ]); + mockService.unassign = vi.fn().mockResolvedValue(mockResponse); + + + const result = await task.unassign(); + + + expect(mockService.unassign).toHaveBeenCalledWith(TASK_TEST_CONSTANTS.TASK_ID); + expect(result).toEqual(mockResponse); + }); + + it('should throw error if taskId is undefined', async () => { + const taskData = createBasicTask({ id: undefined }); + const task = createTaskWithMethods(taskData, mockService); + + + await expect(task.unassign()).rejects.toThrow('Task ID is undefined'); + }); + }); + + describe('task.complete()', () => { + it('should call task.complete for external task', async () => { + const taskData = createBasicTask({ folderId: TEST_CONSTANTS.FOLDER_ID, type: TaskType.External }); + const task = createTaskWithMethods(taskData, mockService); + + const mockResponse = createMockOperationResponse({ + type: TaskType.External, + taskId: TASK_TEST_CONSTANTS.TASK_ID + }); + mockService.complete = vi.fn().mockResolvedValue(mockResponse); + + + const result = await task.complete({ + type: TaskType.External + }); + + + expect(mockService.complete).toHaveBeenCalledWith( + { + type: TaskType.External, + taskId: TASK_TEST_CONSTANTS.TASK_ID, + data: undefined, + action: undefined + }, + TEST_CONSTANTS.FOLDER_ID + ); + expect(result).toEqual(mockResponse); + }); + + it('should call task.complete for app task', async () => { + const taskData = createBasicTask({ folderId: TEST_CONSTANTS.FOLDER_ID, type: TaskType.App }); + const task = createTaskWithMethods(taskData, mockService); + + const mockResponse = createMockOperationResponse({ + type: TaskType.App, + taskId: TASK_TEST_CONSTANTS.TASK_ID, + data: {}, + action: TASK_TEST_CONSTANTS.ACTION_APPROVE + }); + mockService.complete = vi.fn().mockResolvedValue(mockResponse); + + + const result = await task.complete({ + type: TaskType.App, + data: TASK_TEST_CONSTANTS.APP_TASK_DATA, + action: TASK_TEST_CONSTANTS.ACTION_APPROVE + }); + + + expect(mockService.complete).toHaveBeenCalledWith( + { + type: TaskType.App, + taskId: TASK_TEST_CONSTANTS.TASK_ID, + data: TASK_TEST_CONSTANTS.APP_TASK_DATA, + action: TASK_TEST_CONSTANTS.ACTION_APPROVE + }, + TEST_CONSTANTS.FOLDER_ID + ); + expect(result).toEqual(mockResponse); + }); + + it('should call task.complete for form task', async () => { + const taskData = createBasicTask({ folderId: TEST_CONSTANTS.FOLDER_ID, type: TaskType.Form }); + const task = createTaskWithMethods(taskData, mockService); + + const mockResponse = createMockOperationResponse({ + type: TaskType.Form, + taskId: TASK_TEST_CONSTANTS.TASK_ID, + data: TASK_TEST_CONSTANTS.FORM_DATA, + action: TASK_TEST_CONSTANTS.ACTION_SUBMIT + }); + mockService.complete = vi.fn().mockResolvedValue(mockResponse); + + + const result = await task.complete({ + type: TaskType.Form, + data: TASK_TEST_CONSTANTS.FORM_DATA, + action: TASK_TEST_CONSTANTS.ACTION_SUBMIT + }); + + + expect(mockService.complete).toHaveBeenCalledWith( + { + type: TaskType.Form, + taskId: TASK_TEST_CONSTANTS.TASK_ID, + data: TASK_TEST_CONSTANTS.FORM_DATA, + action: TASK_TEST_CONSTANTS.ACTION_SUBMIT + }, + TEST_CONSTANTS.FOLDER_ID + ); + expect(result).toEqual(mockResponse); + }); + + it('should throw error if taskId is undefined', async () => { + const taskData = createBasicTask({ id: undefined }); + const task = createTaskWithMethods(taskData, mockService); + + + await expect(task.complete({ + type: TaskType.External, + data: {}, + action: TASK_TEST_CONSTANTS.ACTION_SUBMIT + })).rejects.toThrow('Task ID is undefined'); + }); + + it('should throw error if folderId is undefined', async () => { + const taskData = createBasicTask({ folderId: undefined }); + const task = createTaskWithMethods(taskData, mockService); + + + await expect(task.complete({ + type: TaskType.External, + data: {}, + action: TASK_TEST_CONSTANTS.ACTION_SUBMIT + })).rejects.toThrow('Folder ID is required'); + }); + }); + }); + + describe('Task data and methods are combined correctly', () => { + it('should preserve all task properties', () => { + const taskData = createBasicTask(); + const task = createTaskWithMethods(taskData, mockService); + + expect(task.id).toBe(TASK_TEST_CONSTANTS.TASK_ID); + expect(task.title).toBe(TASK_TEST_CONSTANTS.TASK_TITLE); + expect(task.type).toBe(TaskType.External); + expect(task.priority).toBe(TaskPriority.Medium); + expect(task.folderId).toBe(TEST_CONSTANTS.FOLDER_ID); + expect(task.key).toBe(TASK_TEST_CONSTANTS.TASK_KEY); + }); + + it('should have all methods available', () => { + const taskData = createBasicTask(); + const task = createTaskWithMethods(taskData, mockService); + + expect(typeof task.assign).toBe('function'); + expect(typeof task.reassign).toBe('function'); + expect(typeof task.unassign).toBe('function'); + expect(typeof task.complete).toBe('function'); + }); + }); +}); + diff --git a/tests/unit/services/action-center/index.ts b/tests/unit/services/action-center/index.ts new file mode 100644 index 00000000..3d7e898e --- /dev/null +++ b/tests/unit/services/action-center/index.ts @@ -0,0 +1,5 @@ +/** + * Action Center services test exports + */ + +export * from './tasks.test'; \ No newline at end of file diff --git a/tests/unit/services/action-center/tasks.test.ts b/tests/unit/services/action-center/tasks.test.ts new file mode 100644 index 00000000..b3d2c885 --- /dev/null +++ b/tests/unit/services/action-center/tasks.test.ts @@ -0,0 +1,885 @@ +// ===== IMPORTS ===== +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import { TaskService } from '../../../../src/services/action-center/tasks'; +import { + TaskType, + TaskPriority, + TaskAssignmentOptions, + TaskCompletionOptions, + TaskCreateOptions, + TaskGetAllOptions, + TaskGetUsersOptions +} from '../../../../src/models/action-center/tasks.types'; +import { PaginationHelpers } from '../../../../src/utils/pagination/helpers'; +import { ApiClient } from '../../../../src/core/http/api-client'; +import { createServiceTestDependencies, createMockApiClient } from '../../../utils/setup'; +import { + createMockTaskResponse, + createMockTaskGetResponse, + createMockTasks, + createMockUsers +} from '../../../utils/mocks/tasks'; +import { createMockError } from '../../../utils/mocks/core'; +import { DEFAULT_TASK_EXPAND } from '../../../../src/models/action-center/tasks.constants'; +import { TASK_TEST_CONSTANTS } from '../../../utils/constants/tasks'; +import { TEST_CONSTANTS } from '../../../utils/constants/common'; +import { TASK_ENDPOINTS } from '../../../../src/utils/constants/endpoints'; +import { FOLDER_ID } from '../../../../src/utils/constants/headers'; + +// ===== MOCKING ===== +// Mock the dependencies +vi.mock('../../../../src/core/http/api-client'); + +// Import mock objects using vi.hoisted() - this ensures they're available before vi.mock() calls +const mocks = vi.hoisted(() => { + // Import/re-export the mock utilities from core + return import('../../../utils/mocks/core'); +}); + +// Setup all mocks at module level +vi.mock('../../../../src/utils/transform', async () => (await mocks).mockTransformUtils); +vi.mock('../../../../src/utils/pagination/helpers', async () => (await mocks).mockPaginationHelpers); + +// ===== TEST SUITE ===== +describe('TaskService Unit Tests', () => { + let taskService: TaskService; + let mockApiClient: any; + + beforeEach(() => { + // Create mock instances using centralized setup + const { config, executionContext, tokenManager } = createServiceTestDependencies(); + mockApiClient = createMockApiClient(); + + // Mock the ApiClient constructor + vi.mocked(ApiClient).mockImplementation(() => mockApiClient); + + // Reset pagination helpers mock before each test + vi.mocked(PaginationHelpers.getAll).mockReset(); + + taskService = new TaskService(config, executionContext, tokenManager); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + describe('create', () => { + it('should create a task successfully with all fields mapped correctly', async () => { + const taskInput = { + title: TASK_TEST_CONSTANTS.TASK_TITLE, + priority: TaskPriority.High + } as TaskCreateOptions; + + const mockResponse = createMockTaskResponse({ + title: TASK_TEST_CONSTANTS.TASK_TITLE, + priority: TaskPriority.High + }); + + mockApiClient.post.mockResolvedValue(mockResponse); + + const result = await taskService.create(taskInput, TEST_CONSTANTS.FOLDER_ID); + + // Verify the result + expect(result).toBeDefined(); + expect(result.title).toBe(TASK_TEST_CONSTANTS.TASK_TITLE); + expect(result.priority).toBe(TaskPriority.High); + + // Verify the API call has correct endpoint, body, and headers + expect(mockApiClient.post).toHaveBeenCalledWith( + TASK_ENDPOINTS.CREATE_GENERIC_TASK, + expect.objectContaining({ + title: TASK_TEST_CONSTANTS.TASK_TITLE, + priority: TaskPriority.High, + type: TaskType.External // SDK adds this automatically + }), + expect.objectContaining({ + headers: expect.objectContaining({ + [FOLDER_ID]: TEST_CONSTANTS.FOLDER_ID.toString() + }) + }) + ); + }); + + it('should handle optional data field with nested objects', async () => { + const taskInput = { + title: TASK_TEST_CONSTANTS.TASK_TITLE_COMPLEX, + priority: TaskPriority.Critical, + data: TASK_TEST_CONSTANTS.CUSTOM_DATA + } as TaskCreateOptions; + + const mockResponse = createMockTaskResponse({ + priority: TaskPriority.Critical, + data: taskInput.data + }); + + mockApiClient.post.mockResolvedValue(mockResponse); + + await taskService.create(taskInput, TEST_CONSTANTS.FOLDER_ID); + + // Verify complex data structures are passed through + expect(mockApiClient.post).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ + data: TASK_TEST_CONSTANTS.CUSTOM_DATA + }), + expect.any(Object) + ); + }); + + it('should handle API errors', async () => { + const taskInput = { + title: TASK_TEST_CONSTANTS.TASK_TITLE, + priority: TaskPriority.High + } as TaskCreateOptions; + + const error = createMockError(TEST_CONSTANTS.ERROR_MESSAGE); + mockApiClient.post.mockRejectedValue(error); + + await expect(taskService.create(taskInput, TEST_CONSTANTS.FOLDER_ID)).rejects.toThrow(TEST_CONSTANTS.ERROR_MESSAGE); + }); + }); + + describe('assign', () => { + it('should assign a single task successfully', async () => { + const assignment = { + taskId: TASK_TEST_CONSTANTS.TASK_ID, + userId: TASK_TEST_CONSTANTS.USER_ID + } as TaskAssignmentOptions; + + const mockResponse = { + value: [] // Empty array means success + }; + + mockApiClient.post.mockResolvedValue(mockResponse); + + const result = await taskService.assign(assignment); + + expect(result.success).toBe(true); + expect(result.data).toEqual([assignment]); + + expect(mockApiClient.post).toHaveBeenCalledWith( + TASK_ENDPOINTS.ASSIGN_TASKS, + expect.objectContaining({ + taskAssignments: expect.arrayContaining([ + expect.objectContaining({ + taskId: assignment.taskId, + userId: assignment.userId + }) + ]) + }), + expect.any(Object) + ); + }); + + it('should assign multiple tasks successfully', async () => { + const assignments = [ + { taskId: TASK_TEST_CONSTANTS.TASK_ID, userId: TASK_TEST_CONSTANTS.USER_ID }, + { taskId: TASK_TEST_CONSTANTS.TASK_ID_2, userId: TASK_TEST_CONSTANTS.USER_ID_2 } + ] as TaskAssignmentOptions[]; + + const mockResponse = { + value: [] + }; + + mockApiClient.post.mockResolvedValue(mockResponse); + + const result = await taskService.assign(assignments); + + expect(result.success).toBe(true); + expect(result.data).toEqual(assignments); + + expect(mockApiClient.post).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ + taskAssignments: expect.arrayContaining([ + expect.objectContaining({ taskId: TASK_TEST_CONSTANTS.TASK_ID, userId: TASK_TEST_CONSTANTS.USER_ID }), + expect.objectContaining({ taskId: TASK_TEST_CONSTANTS.TASK_ID_2, userId: TASK_TEST_CONSTANTS.USER_ID_2 }) + ]) + }), + expect.any(Object) + ); + }); + + it('should support assignment with email', async () => { + const assignment = { + taskId: TASK_TEST_CONSTANTS.TASK_ID, + userNameOrEmail: TASK_TEST_CONSTANTS.USER_EMAIL + } as TaskAssignmentOptions; + + const mockResponse = { + value: [] + }; + + mockApiClient.post.mockResolvedValue(mockResponse); + + const result = await taskService.assign(assignment); + + expect(result.success).toBe(true); + expect(result.data).toEqual([assignment]); + + expect(mockApiClient.post).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ + taskAssignments: expect.arrayContaining([ + expect.objectContaining({ + taskId: TASK_TEST_CONSTANTS.TASK_ID, + userNameOrEmail: TASK_TEST_CONSTANTS.USER_EMAIL + }) + ]) + }), + expect.any(Object) + ); + }); + }); + + describe('reassign', () => { + it('should reassign a single task successfully', async () => { + const assignment = { + taskId: TASK_TEST_CONSTANTS.TASK_ID, + userId: TASK_TEST_CONSTANTS.USER_ID + } as TaskAssignmentOptions; + + const mockResponse = { + value: [] + }; + + mockApiClient.post.mockResolvedValue(mockResponse); + + const result = await taskService.reassign(assignment); + + expect(result.success).toBe(true); + expect(result.data).toEqual([assignment]); + + expect(mockApiClient.post).toHaveBeenCalledWith( + TASK_ENDPOINTS.REASSIGN_TASKS, + expect.objectContaining({ + taskAssignments: expect.arrayContaining([ + expect.objectContaining({ + taskId: assignment.taskId, + userId: assignment.userId + }) + ]) + }), + expect.any(Object) + ); + }); + + it('should reassign multiple tasks successfully', async () => { + const assignments = [ + { taskId: TASK_TEST_CONSTANTS.TASK_ID, userId: TASK_TEST_CONSTANTS.USER_ID }, + { taskId: TASK_TEST_CONSTANTS.TASK_ID_2, userId: TASK_TEST_CONSTANTS.USER_ID_2 } + ] as TaskAssignmentOptions[]; + + const mockResponse = { + value: [] + }; + + mockApiClient.post.mockResolvedValue(mockResponse); + + const result = await taskService.reassign(assignments); + + // Verify complete OperationResponse structure + expect(result.success).toBe(true); + expect(result.data).toEqual(assignments); + + // Verify API call + expect(mockApiClient.post).toHaveBeenCalledWith( + TASK_ENDPOINTS.REASSIGN_TASKS, + expect.objectContaining({ + taskAssignments: expect.arrayContaining([ + expect.objectContaining({ taskId: TASK_TEST_CONSTANTS.TASK_ID, userId: TASK_TEST_CONSTANTS.USER_ID }), + expect.objectContaining({ taskId: TASK_TEST_CONSTANTS.TASK_ID_2, userId: TASK_TEST_CONSTANTS.USER_ID_2 }) + ]) + }), + expect.any(Object) + ); + }); + + it('should reassign task with email address', async () => { + const assignment = { + taskId: TASK_TEST_CONSTANTS.TASK_ID, + userNameOrEmail: TASK_TEST_CONSTANTS.USER_EMAIL + } as TaskAssignmentOptions; + + const mockResponse = { + value: [] + }; + + mockApiClient.post.mockResolvedValue(mockResponse); + + const result = await taskService.reassign(assignment); + + expect(result.success).toBe(true); + expect(result.data).toEqual([assignment]); + + // Verify email is passed to API + expect(mockApiClient.post).toHaveBeenCalledWith( + TASK_ENDPOINTS.REASSIGN_TASKS, + expect.objectContaining({ + taskAssignments: expect.arrayContaining([ + expect.objectContaining({ + taskId: TASK_TEST_CONSTANTS.TASK_ID, + userNameOrEmail: TASK_TEST_CONSTANTS.USER_EMAIL + }) + ]) + }), + expect.any(Object) + ); + }); + }); + + describe('unassign', () => { + it('should unassign a single task successfully', async () => { + const taskId = TASK_TEST_CONSTANTS.TASK_ID; + + const mockResponse = { + value: [] + }; + + mockApiClient.post.mockResolvedValue(mockResponse); + + const result = await taskService.unassign(taskId); + + expect(result.success).toBe(true); + expect(result.data).toEqual([{ taskId: taskId }]); + + expect(mockApiClient.post).toHaveBeenCalledWith( + TASK_ENDPOINTS.UNASSIGN_TASKS, + expect.objectContaining({ + taskIds: [taskId] + }), + expect.any(Object) + ); + }); + + it('should unassign multiple tasks successfully', async () => { + const taskIds = [TASK_TEST_CONSTANTS.TASK_ID, TASK_TEST_CONSTANTS.TASK_ID_2, TASK_TEST_CONSTANTS.TASK_ID_3]; + + const mockResponse = { + value: [] + }; + + mockApiClient.post.mockResolvedValue(mockResponse); + + const result = await taskService.unassign(taskIds); + + expect(result.success).toBe(true); + expect(result.data).toEqual(taskIds.map(taskId => ({ taskId }))); + + expect(mockApiClient.post).toHaveBeenCalledWith( + TASK_ENDPOINTS.UNASSIGN_TASKS, + expect.objectContaining({ + taskIds + }), + expect.any(Object) + ); + }); + + it('should handle unassignment failure for invalid task ID', async () => { + const invalidTaskId = 9999; + + const mockErrorResponse = { + value: [{ + taskId: invalidTaskId, + userId: null, + errorCode: 1002, + errorMessage: 'Action does not exist.', + userNameOrEmail: null + }] + }; + + mockApiClient.post.mockResolvedValue(mockErrorResponse); + + const result = await taskService.unassign(invalidTaskId); + + expect(result.success).toBe(false); + expect(result.data).toEqual(mockErrorResponse.value); + expect(result.data[0]).toHaveProperty('taskId', invalidTaskId); + expect(result.data[0]).toHaveProperty('errorCode', 1002); + expect(result.data[0]).toHaveProperty('errorMessage', 'Action does not exist.'); + }); + }); + + describe('complete', () => { + it('should complete a generic task successfully', async () => { + const completionOptions = { + type: TaskType.External, + taskId: TASK_TEST_CONSTANTS.TASK_ID + } as TaskCompletionOptions; + + const folderId = TEST_CONSTANTS.FOLDER_ID; + + mockApiClient.post.mockResolvedValue(undefined); + + const result = await taskService.complete(completionOptions, folderId); + + expect(result.success).toBe(true); + expect(result.data).toEqual(completionOptions); + expect(mockApiClient.post).toHaveBeenCalledWith( + TASK_ENDPOINTS.COMPLETE_GENERIC_TASK, + completionOptions, + expect.objectContaining({ + headers: expect.any(Object) + }) + ); + }); + + it('should complete a form task successfully', async () => { + const completionOptions = { + type: TaskType.Form, + taskId: TASK_TEST_CONSTANTS.TASK_ID, + data: TASK_TEST_CONSTANTS.FORM_DATA, + action: TASK_TEST_CONSTANTS.ACTION_SUBMIT + } as TaskCompletionOptions; + + const folderId = TEST_CONSTANTS.FOLDER_ID; + + mockApiClient.post.mockResolvedValue(undefined); + + const result = await taskService.complete(completionOptions, folderId); + + expect(result.success).toBe(true); + expect(result.data).toEqual(completionOptions); + + expect(mockApiClient.post).toHaveBeenCalledWith( + TASK_ENDPOINTS.COMPLETE_FORM_TASK, + completionOptions, + expect.any(Object) + ); + }); + + it('should complete an app task successfully', async () => { + const completionOptions = { + type: TaskType.App, + taskId: TASK_TEST_CONSTANTS.TASK_ID, + action: TASK_TEST_CONSTANTS.ACTION_APPROVE, + data: TASK_TEST_CONSTANTS.APP_TASK_DATA + } as TaskCompletionOptions; + + const folderId = TEST_CONSTANTS.FOLDER_ID; + + mockApiClient.post.mockResolvedValue(undefined); + + const result = await taskService.complete(completionOptions, folderId); + + expect(result.success).toBe(true); + expect(result.data).toEqual(completionOptions); + + expect(mockApiClient.post).toHaveBeenCalledWith( + TASK_ENDPOINTS.COMPLETE_APP_TASK, + completionOptions, + expect.any(Object) + ); + }); + + it('should include folderId in headers', async () => { + const completionOptions = { + type: TaskType.External, + taskId: TASK_TEST_CONSTANTS.TASK_ID + } as TaskCompletionOptions; + + const folderId = TEST_CONSTANTS.FOLDER_ID; + + mockApiClient.post.mockResolvedValue(undefined); + + await taskService.complete(completionOptions, folderId); + + expect(mockApiClient.post).toHaveBeenCalledWith( + expect.any(String), + expect.any(Object), + expect.objectContaining({ + headers: expect.objectContaining({ + [FOLDER_ID]: folderId.toString() + }) + }) + ); + }); + }); + + describe('getById', () => { + it('should get a task by ID successfully', async () => { + const taskId = TASK_TEST_CONSTANTS.TASK_ID; + const mockResponse = createMockTaskGetResponse({ + id: taskId, + title: TASK_TEST_CONSTANTS.TASK_TITLE + }); + + mockApiClient.get.mockResolvedValue(mockResponse); + + const result = await taskService.getById(taskId); + + expect(result).toBeDefined(); + expect(result.id).toBe(taskId); + expect(result.title).toBe(TASK_TEST_CONSTANTS.TASK_TITLE); + expect(mockApiClient.get).toHaveBeenCalledWith( + expect.stringContaining(taskId.toString()), + expect.any(Object) + ); + }); + + it('should include folderId in headers when provided', async () => { + const taskId = TASK_TEST_CONSTANTS.TASK_ID; + const folderId = TEST_CONSTANTS.FOLDER_ID; + const mockResponse = createMockTaskGetResponse({ + id: taskId, + folderId: folderId + }); + + mockApiClient.get.mockResolvedValue(mockResponse); + + await taskService.getById(taskId, {}, folderId); + + expect(mockApiClient.get).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ + headers: expect.objectContaining({ + [FOLDER_ID]: folderId.toString() + }) + }) + ); + }); + + it('should handle form tasks by calling getFormTaskById with provided folderId', async () => { + const taskId = TASK_TEST_CONSTANTS.TASK_ID; + const folderId = TEST_CONSTANTS.FOLDER_ID; + + const mockTaskResponse = createMockTaskGetResponse({ + id: taskId, + title: TASK_TEST_CONSTANTS.TASK_TITLE_FORM, + type: TaskType.Form, + folderId: folderId + }); + + const mockFormTaskResponse = createMockTaskGetResponse({ + id: taskId, + title: TASK_TEST_CONSTANTS.TASK_TITLE_FORM, + type: TaskType.Form, + folderId: folderId, + formLayout: { /* form-specific data */ }, + actionLabel: TASK_TEST_CONSTANTS.ACTION_SUBMIT + }); + + mockApiClient.get + .mockResolvedValueOnce(mockTaskResponse) + .mockResolvedValueOnce(mockFormTaskResponse); + + await taskService.getById(taskId, {}, folderId); + + expect(mockApiClient.get).toHaveBeenCalledTimes(2); + expect(mockApiClient.get).toHaveBeenNthCalledWith( + 2, + TASK_ENDPOINTS.GET_TASK_FORM_BY_ID, + expect.any(Object) + ); + }); + + it('should handle form tasks without folderId by using task folderId', async () => { + const taskId = TASK_TEST_CONSTANTS.TASK_ID; + const taskFolderId = TEST_CONSTANTS.FOLDER_ID; + + const mockTaskResponse = createMockTaskGetResponse({ + id: taskId, + title: TASK_TEST_CONSTANTS.TASK_TITLE_FORM, + type: TaskType.Form, + folderId: taskFolderId + }); + + const mockFormTaskResponse = createMockTaskGetResponse({ + id: taskId, + title: TASK_TEST_CONSTANTS.TASK_TITLE_FORM, + type: TaskType.Form, + folderId: taskFolderId, + formLayout: { /* form-specific data */ }, + actionLabel: TASK_TEST_CONSTANTS.ACTION_SUBMIT + }); + + mockApiClient.get + .mockResolvedValueOnce(mockTaskResponse) + .mockResolvedValueOnce(mockFormTaskResponse); + + // Call without providing folderId parameter + await taskService.getById(taskId); + + expect(mockApiClient.get).toHaveBeenCalledTimes(2); + expect(mockApiClient.get).toHaveBeenNthCalledWith( + 2, + TASK_ENDPOINTS.GET_TASK_FORM_BY_ID, + expect.any(Object) + ); + }); + + it('should merge custom expand with default expand parameters', async () => { + const taskId = TASK_TEST_CONSTANTS.TASK_ID; + const mockResponse = createMockTaskGetResponse({ + id: taskId, + title: TASK_TEST_CONSTANTS.TASK_TITLE + }); + + mockApiClient.get.mockResolvedValue(mockResponse); + + // Test with custom expand parameter + await taskService.getById(taskId, { expand: 'CustomField' }); + + expect(mockApiClient.get).toHaveBeenCalledWith( + expect.stringContaining(taskId.toString()), + expect.objectContaining({ + params: expect.objectContaining({ + expand: `${DEFAULT_TASK_EXPAND},CustomField` + }) + }) + ); + }); + }); + + describe('getAll', () => { + beforeEach(() => { + // Reset the mock before each test + vi.mocked(PaginationHelpers.getAll).mockReset(); + }); + + it('should return all tasks without pagination', async () => { + // Mock the pagination helper to return our test data + const mockTasks = createMockTasks(3); + const mockResponse = { + items: mockTasks, + totalCount: 3 + }; + + vi.mocked(PaginationHelpers.getAll).mockResolvedValue(mockResponse); + + const result = await taskService.getAll(); + + // Verify PaginationHelpers.getAll was called with correct parameters + expect(PaginationHelpers.getAll).toHaveBeenCalledWith( + expect.objectContaining({ + serviceAccess: expect.any(Object), + getEndpoint: expect.any(Function), + transformFn: expect.any(Function), + pagination: expect.any(Object) + }), + undefined + ); + + expect(result).toEqual(mockResponse); + }); + + it('should return paginated tasks when pagination options provided', async () => { + // Mock the pagination helper to return our test data + const mockTasks = createMockTasks(10); + const mockResponse = { + items: mockTasks, + totalCount: 100, + hasNextPage: true, + nextCursor: TASK_TEST_CONSTANTS.CURSOR_NEXT, + previousCursor: null, + currentPage: 1, + totalPages: 10 + }; + + vi.mocked(PaginationHelpers.getAll).mockResolvedValue(mockResponse); + + const options = { + pageSize: TEST_CONSTANTS.PAGE_SIZE, + jumpToPage: 1 + } as TaskGetAllOptions; + + const result = await taskService.getAll(options); + + // Verify PaginationHelpers.getAll was called with correct parameters + expect(PaginationHelpers.getAll).toHaveBeenCalledWith( + expect.objectContaining({ + serviceAccess: expect.any(Object), + getEndpoint: expect.any(Function), + transformFn: expect.any(Function), + pagination: expect.any(Object) + }), + expect.objectContaining({ + pageSize: TEST_CONSTANTS.PAGE_SIZE, + jumpToPage: 1 + }) + ); + + expect(result).toEqual(mockResponse); + }); + + it('should handle filtering options', async () => { + // Mock the pagination helper to return our test data + const mockTasks = createMockTasks(2); + const mockResponse = { + items: mockTasks, + totalCount: 2 + }; + + vi.mocked(PaginationHelpers.getAll).mockResolvedValue(mockResponse); + + const options = { + filter: "status eq 'Pending'" + } as TaskGetAllOptions; + + await taskService.getAll(options); + + // Verify PaginationHelpers.getAll was called with correct parameters + expect(PaginationHelpers.getAll).toHaveBeenCalledWith( + expect.objectContaining({ + serviceAccess: expect.any(Object), + getEndpoint: expect.any(Function), + transformFn: expect.any(Function), + pagination: expect.any(Object) + }), + expect.objectContaining({ + filter: "status eq 'Pending'" + }) + ); + }); + + it('should call processParametersFn with folderId when provided', async () => { + const mockTasks = createMockTasks(1); + const mockResponse = { + items: mockTasks, + totalCount: 1 + }; + + // Mock PaginationHelpers.getAll and capture the processParametersFn + let capturedProcessParametersFn: ((options: any, folderId?: number) => any) | undefined; + vi.mocked(PaginationHelpers.getAll).mockImplementation(async (config: any) => { + capturedProcessParametersFn = config.processParametersFn; + return mockResponse; + }); + + await taskService.getAll({ folderId: TEST_CONSTANTS.FOLDER_ID }); + + // Verify the process parameters function was captured + expect(capturedProcessParametersFn).toBeDefined(); + + // Test processParametersFn with folderId and no existing filter + const optionsWithoutFilter = { select: 'id,title' }; + const processedWithoutFilter = capturedProcessParametersFn!(optionsWithoutFilter, TEST_CONSTANTS.FOLDER_ID); + expect(processedWithoutFilter).toHaveProperty('filter', `organizationUnitId eq ${TEST_CONSTANTS.FOLDER_ID}`); + expect(processedWithoutFilter).toHaveProperty('expand'); + + // Test processParametersFn with folderId and existing filter + const optionsWithFilter = { filter: 'status eq "Pending"' }; + const processedWithFilter = capturedProcessParametersFn!(optionsWithFilter, TEST_CONSTANTS.FOLDER_ID); + expect(processedWithFilter.filter).toBe(`status eq "Pending" and organizationUnitId eq ${TEST_CONSTANTS.FOLDER_ID}`); + + // Test processParametersFn without folderId + const optionsNoFolder = { select: 'id' }; + const processedNoFolder = capturedProcessParametersFn!(optionsNoFolder); + expect(processedNoFolder.filter).toBeUndefined(); + }); + }); + + describe('getUsers', () => { + beforeEach(() => { + // Reset the mock before each test + vi.mocked(PaginationHelpers.getAll).mockReset(); + }); + + it('should return all users without pagination', async () => { + // Mock the pagination helper to return our test data + const folderId = TEST_CONSTANTS.FOLDER_ID; + const mockUsers = createMockUsers(3); + const mockResponse = { + items: mockUsers, + totalCount: 3 + }; + + vi.mocked(PaginationHelpers.getAll).mockResolvedValue(mockResponse); + + const result = await taskService.getUsers(folderId); + + // Verify PaginationHelpers.getAll was called with correct parameters + expect(PaginationHelpers.getAll).toHaveBeenCalledWith( + expect.objectContaining({ + serviceAccess: expect.any(Object), + getEndpoint: expect.any(Function), + transformFn: expect.any(Function), + pagination: expect.any(Object) + }), + expect.objectContaining({ + folderId: folderId + }) + ); + + expect(result).toEqual(mockResponse); + }); + + it('should return paginated users when pagination options provided', async () => { + // Mock the pagination helper to return our test data + const folderId = TEST_CONSTANTS.FOLDER_ID; + const mockUsers = createMockUsers(10); + const mockResponse = { + items: mockUsers, + totalCount: 50, + hasNextPage: true, + nextCursor: TASK_TEST_CONSTANTS.CURSOR_NEXT, + previousCursor: null, + currentPage: 1, + totalPages: 5 + }; + + vi.mocked(PaginationHelpers.getAll).mockResolvedValue(mockResponse); + + const options = { + pageSize: TEST_CONSTANTS.PAGE_SIZE + } as TaskGetUsersOptions; + + const result = await taskService.getUsers(folderId, options); + + // Verify PaginationHelpers.getAll was called with correct parameters + expect(PaginationHelpers.getAll).toHaveBeenCalledWith( + expect.objectContaining({ + serviceAccess: expect.any(Object), + getEndpoint: expect.any(Function), + transformFn: expect.any(Function), + pagination: expect.any(Object) + }), + expect.objectContaining({ + folderId: folderId, + pageSize: TEST_CONSTANTS.PAGE_SIZE + }) + ); + + expect(result).toEqual(mockResponse); + }); + + it('should handle filtering options', async () => { + // Mock the pagination helper to return our test data + const folderId = TEST_CONSTANTS.FOLDER_ID; + const mockUsers = createMockUsers(1); + const mockResponse = { + items: mockUsers, + totalCount: 1 + }; + + vi.mocked(PaginationHelpers.getAll).mockResolvedValue(mockResponse); + + const options = { + filter: "name eq 'abc'" + } as TaskGetUsersOptions; + + await taskService.getUsers(folderId, options); + + // Verify PaginationHelpers.getAll was called with correct parameters + expect(PaginationHelpers.getAll).toHaveBeenCalledWith( + expect.objectContaining({ + serviceAccess: expect.any(Object), + getEndpoint: expect.any(Function), + transformFn: expect.any(Function), + pagination: expect.any(Object) + }), + expect.objectContaining({ + folderId: folderId, + filter: "name eq 'abc'" + }) + ); + }); + + it('should handle API errors', async () => { + const error = createMockError(TEST_CONSTANTS.ERROR_MESSAGE); + vi.mocked(PaginationHelpers.getAll).mockRejectedValue(error); + + await expect(taskService.getUsers(TEST_CONSTANTS.FOLDER_ID)).rejects.toThrow(TEST_CONSTANTS.ERROR_MESSAGE); + }); + }); +}); diff --git a/tests/unit/services/tasks.test.ts b/tests/unit/services/tasks.test.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/utils/constants/index.ts b/tests/utils/constants/index.ts index 8b3be0eb..34981c15 100644 --- a/tests/utils/constants/index.ts +++ b/tests/utils/constants/index.ts @@ -3,4 +3,6 @@ */ export * from './common'; -export * from './maestro'; \ No newline at end of file +export * from './maestro'; +export * from './tasks'; + diff --git a/tests/utils/constants/tasks.ts b/tests/utils/constants/tasks.ts new file mode 100644 index 00000000..4028e11e --- /dev/null +++ b/tests/utils/constants/tasks.ts @@ -0,0 +1,58 @@ +/** + * Task service test constants + * Task-specific constants only + */ + +export const TASK_TEST_CONSTANTS = { + // Task IDs + TASK_ID: 123, + TASK_ID_2: 456, + TASK_ID_3: 789, + + // User IDs + USER_ID: 456, + USER_ID_2: 101, + + // Task Metadata + TASK_TITLE: 'Test Task', + TASK_TITLE_COMPLEX: 'Complex Task', + TASK_TITLE_FORM: 'Form Task', + TASK_KEY: 'TASK-123', + TASK_KEY_PREFIX: 'TASK-', + + // User Information + USER_NAME: 'User', + USER_USERNAME_PREFIX: 'user', + USER_DISPLAY_NAME_PREFIX: 'User ', + USER_EMAIL: 'user@example.com', + + // Timestamps + CREATION_TIME: '2025-01-15T10:00:00Z', + + // Pagination + CURSOR_NEXT: 'next-cursor-string', + + // Task Actions + ACTION_SUBMIT: 'submit', + ACTION_APPROVE: 'approve', + + // Task Form Data + FORM_DATA: { + fieldName: 'John Doe', + fieldEmail: 'john@example.com', + fieldNotes: 'Completed the form' + }, + + // Task Custom Data + CUSTOM_DATA: { + customField: 'customValue', + nested: { key: 'value' }, + array: [1, 2, 3] + }, + + // App Task Completion Data + APP_TASK_DATA: { + Content: null, + Comment: null + }, +} as const; diff --git a/tests/utils/mocks/core.ts b/tests/utils/mocks/core.ts index c27753a6..f2459e02 100644 --- a/tests/utils/mocks/core.ts +++ b/tests/utils/mocks/core.ts @@ -4,6 +4,38 @@ import { vi } from 'vitest'; import { TEST_CONSTANTS } from '../constants/common'; +/** + * Ready-to-use mock for transform utilities + * Import and spread this in your vi.mock() call + * + * @example + * vi.mock('../../../src/utils/transform', () => mockTransformUtils); + */ +export const mockTransformUtils = { + pascalToCamelCaseKeys: vi.fn((obj) => obj), + camelToPascalCaseKeys: vi.fn((obj) => obj), + transformData: vi.fn((data) => data), + applyDataTransforms: vi.fn((data) => data), + addPrefixToKeys: vi.fn((obj) => obj), +}; + +/** + * Ready-to-use mock for PaginationHelpers + * Import and spread this in your vi.mock() call + * + * @example + * vi.mock('../../../src/utils/pagination/helpers', () => mockPaginationHelpers); + */ +export const mockPaginationHelpers = { + PaginationHelpers: { + getAll: vi.fn(), + hasPaginationParameters: vi.fn((options = {}) => { + const { cursor, pageSize, jumpToPage } = options; + return cursor !== undefined || pageSize !== undefined || jumpToPage !== undefined; + }) + } +}; + /** * Generic factory for creating mock base response objects * Use this as a building block for service-specific responses @@ -16,6 +48,8 @@ import { TEST_CONSTANTS } from '../constants/common'; * ```typescript * const mockResponse = createMockBaseResponse( * { id: 'test', name: 'Test' }, + * const mockTask = createMockBaseResponse( + * { id: 123, title: 'Test', folderId: 456 }, * { customField: 'value' } * ); * ``` @@ -59,25 +93,25 @@ export const createMockOperationResponse = (data: T): { success: boolean; dat }); /** - * Pagination helpers mock - Used across all services that need pagination - * This provides a consistent mock for PaginationHelpers across all test files + * Generic factory for creating a collection of mock responses + * Useful for testing list/getAll endpoints * - * Usage in test files: - * ```typescript - * // Use vi.hoisted to ensure mockPaginationHelpers is available during hoisting - * const mocks = vi.hoisted(() => { - * return import('../../../utils/mocks/core'); - * }); + * @param count - Number of mock items to create + * @param factory - Function that creates a single mock item given an index + * @returns Array of mock items * - * vi.mock('../../../../src/utils/pagination/helpers', async () => (await mocks).mockPaginationHelpers); + * @example + * ```typescript + * const mockTasks = createMockCollection(3, (i) => ({ + * id: i + 1, + * title: `Task ${i + 1}`, + * status: 'Active' + * })); * ``` */ -export const mockPaginationHelpers = { - PaginationHelpers: { - getAll: vi.fn(), - hasPaginationParameters: vi.fn((options = {}) => { - const { cursor, pageSize, jumpToPage } = options; - return cursor !== undefined || pageSize !== undefined || jumpToPage !== undefined; - }) - } +export const createMockCollection = ( + count: number, + factory: (index: number) => T +): T[] => { + return Array.from({ length: count }, (_, i) => factory(i)); }; diff --git a/tests/utils/mocks/index.ts b/tests/utils/mocks/index.ts index 22cea9b7..5bce619a 100644 --- a/tests/utils/mocks/index.ts +++ b/tests/utils/mocks/index.ts @@ -8,6 +8,7 @@ export * from './core'; // Service-specific mock utilities export * from './maestro'; +export * from './tasks'; // Re-export constants for convenience export * from '../constants'; \ No newline at end of file diff --git a/tests/utils/mocks/tasks.ts b/tests/utils/mocks/tasks.ts new file mode 100644 index 00000000..c4d18109 --- /dev/null +++ b/tests/utils/mocks/tasks.ts @@ -0,0 +1,148 @@ +/** + * Tasks service mock utilities - Tasks-specific mocks only + * Uses generic utilities from core.ts for base functionality + */ + +import { TaskType, TaskPriority, TaskStatus, RawTaskGetResponse } from '../../../src/models/action-center/tasks.types'; +import { createMockBaseResponse, createMockCollection } from './core'; +import { TASK_TEST_CONSTANTS } from '../constants/tasks'; +import { TEST_CONSTANTS } from '../constants/common'; + +// Task-Specific Mock Factories + +/** + * Creates a mock Task response for create/update operations + * @param overrides - Optional overrides for specific fields + * @returns Mock Task response object + */ +export const createMockTaskResponse = (overrides: Partial = {}) => { + return createMockBaseResponse({ + id: TASK_TEST_CONSTANTS.TASK_ID, + title: TASK_TEST_CONSTANTS.TASK_TITLE, + type: TaskType.External, + priority: TaskPriority.Medium, + status: TaskStatus.Unassigned, + organizationUnitId: TEST_CONSTANTS.FOLDER_ID, + key: TASK_TEST_CONSTANTS.TASK_KEY, + isDeleted: false, + creationTime: TASK_TEST_CONSTANTS.CREATION_TIME, + action: null, + externalTag: null, + lastAssignedTime: null, + completionTime: null, + parentOperationId: null, + deleterUserId: null, + deletionTime: null, + lastModificationTime: null, + waitJobState: null, + assignedToUser: null, + taskSlaDetails: null, + completedByUser: null, + taskAssignees: null, + processingTime: null, + data: null, + }, overrides); +}; + +/** + * Creates a mock Task GET response + * @param overrides - Optional overrides for specific fields + * @returns Mock Task GET response object + */ +export const createMockTaskGetResponse = (overrides: Partial = {}) => { + return createMockBaseResponse({ + id: TASK_TEST_CONSTANTS.TASK_ID, + title: TASK_TEST_CONSTANTS.TASK_TITLE, + type: TaskType.External, + priority: TaskPriority.Medium, + status: TaskStatus.Unassigned, + organizationUnitId: TEST_CONSTANTS.FOLDER_ID, + key: TASK_TEST_CONSTANTS.TASK_KEY, + isDeleted: false, + creationTime: TASK_TEST_CONSTANTS.CREATION_TIME, + action: null, + externalTag: null, + lastAssignedTime: null, + completionTime: null, + parentOperationId: null, + deleterUserId: null, + deletionTime: null, + lastModificationTime: null, + isCompleted: false, + encrypted: false, + bulkFormLayoutId: null, + formLayoutId: null, + taskSlaDetail: null, + taskAssigneeName: null, + lastModifierUserId: null, + assignedToUser: null, + }, overrides); +}; + +/** + * Creates a basic task for model tests with all required fields + * @param overrides - Optional overrides for specific fields + * @returns Basic task response object cast as RawTaskGetResponse + */ +export const createBasicTask = (overrides: Partial = {}): RawTaskGetResponse => { + return createMockBaseResponse({ + id: TASK_TEST_CONSTANTS.TASK_ID, + title: TASK_TEST_CONSTANTS.TASK_TITLE, + type: TaskType.External, + priority: TaskPriority.Medium, + status: TaskStatus.Unassigned, + folderId: TEST_CONSTANTS.FOLDER_ID, + key: TASK_TEST_CONSTANTS.TASK_KEY, + isDeleted: false, + createdTime: TASK_TEST_CONSTANTS.CREATION_TIME, + action: null, + externalTag: null, + lastAssignedTime: null, + completedTime: null, + parentOperationId: null, + deleterUserId: null, + deletedTime: null, + lastModifiedTime: null, + isCompleted: false, + encrypted: false, + bulkFormLayoutId: null, + formLayoutId: null, + taskSlaDetail: null, + taskAssigneeName: null, + lastModifierUserId: null, + assignedToUser: null, + }, overrides) as RawTaskGetResponse; +}; + +/** + * Creates a collection of mock tasks + * @param count - Number of tasks to create + * @returns Array of mock tasks + */ +export const createMockTasks = (count: number) => { + return createMockCollection(count, (i) => + createMockTaskGetResponse({ + id: i + 1, + title: `Task ${i + 1}`, + key: `${TASK_TEST_CONSTANTS.TASK_KEY_PREFIX}${i + 1}`, + status: TaskStatus.Pending, + }) + ); +}; + +/** + * Creates a collection of mock users + * @param count - Number of users to create + * @returns Array of mock users + */ +export const createMockUsers = (count: number) => { + return createMockCollection(count, (i) => ({ + id: i + 1, + name: TASK_TEST_CONSTANTS.USER_NAME, + surname: `${i + 1}`, + userName: `${TASK_TEST_CONSTANTS.USER_USERNAME_PREFIX}${i + 1}`, + emailAddress: `${TASK_TEST_CONSTANTS.USER_USERNAME_PREFIX}${i + 1}@example.com`, + displayName: `${TASK_TEST_CONSTANTS.USER_DISPLAY_NAME_PREFIX}${i + 1}`, + })); +}; +