import { makeDefaultAdb2cServiceMockValue, makeDefaultTasksRepositoryMockValue, makeDefaultUsersRepositoryMockValue, makeTasksServiceMock, } from './test/tasks.service.mock'; import { HttpException, HttpStatus } from '@nestjs/common'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; import { TasksService } from './tasks.service'; import { DataSource } from 'typeorm'; import { createAccount, createCheckoutPermissions, createTask, createUser, createUserGroup, getCheckoutPermissions, getTask, } from './test/utility'; import { Adb2cTooManyRequestsError } from '../../gateways/adb2c/adb2c.service'; import { makeTestingModule } from '../../common/test/modules'; describe('TasksService', () => { it('タスク一覧を取得できる(admin)', async () => { const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue(); const service = await makeTasksServiceMock( tasksRepositoryMockValue, usersRepositoryMockValue, adb2cServiceMockValue, ); const accessToken = { userId: 'userId', role: 'admin', tier: 5 }; const offset = 0; const limit = 20; const status = ['Uploaded', 'Backup']; const paramName = 'JOB_NUMBER'; const direction = 'ASC'; expect( await service.tasksService.getTasks( accessToken, offset, limit, status, paramName, direction, ), ).toEqual({ tasks: [ { assignees: [{ typistName: 'XXXX XXX', typistUserId: 1 }], audioCreatedDate: '2023-01-01T01:01:01.000Z', audioDuration: '123000', audioFileId: 1, audioFinishedDate: '2023-01-01T01:01:01.000Z', audioFormat: 'DS', audioUploadedDate: '2023-01-01T01:01:01.000Z', authorId: 'AUTHOR', comment: 'comment', fileName: 'test.zip', fileSize: 123000, isEncrypted: true, jobNumber: '00000001', optionItemList: [ { optionItemLabel: 'label01', optionItemValue: 'value01' }, { optionItemLabel: 'label02', optionItemValue: 'value02' }, { optionItemLabel: 'label03', optionItemValue: 'value03' }, { optionItemLabel: 'label04', optionItemValue: 'value04' }, { optionItemLabel: 'label05', optionItemValue: 'value05' }, { optionItemLabel: 'label06', optionItemValue: 'value06' }, { optionItemLabel: 'label07', optionItemValue: 'value07' }, { optionItemLabel: 'label08', optionItemValue: 'value08' }, { optionItemLabel: 'label09', optionItemValue: 'value09' }, { optionItemLabel: 'label10', optionItemValue: 'value10' }, ], priority: '00', status: 'Uploaded', transcriptionFinishedDate: undefined, transcriptionStartedDate: undefined, typist: undefined, url: 'test/test.zip', workType: 'WorkType', }, ], total: 1, }); }); it('アカウント情報の取得に失敗した場合、エラーを返却する', async () => { const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue(); usersRepositoryMockValue.findUserByExternalId = new Error('DB failed'); const service = await makeTasksServiceMock( tasksRepositoryMockValue, usersRepositoryMockValue, adb2cServiceMockValue, ); const accessToken = { userId: 'userId', role: 'admin', tier: 5 }; const offset = 0; const limit = 20; const status = ['Uploaded', 'Backup']; const paramName = 'JOB_NUMBER'; const direction = 'ASC'; await expect( service.tasksService.getTasks( accessToken, offset, limit, status, paramName, direction, ), ).rejects.toEqual( new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, ), ); }); it('タスク一覧の取得に失敗した場合、エラーを返却する(admin)', async () => { const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue(); tasksRepositoryMockValue.getTasksFromAccountId = new Error('DB failed'); const service = await makeTasksServiceMock( tasksRepositoryMockValue, usersRepositoryMockValue, adb2cServiceMockValue, ); const accessToken = { userId: 'userId', role: 'admin', tier: 5 }; const offset = 0; const limit = 20; const status = ['Uploaded', 'Backup']; const paramName = 'JOB_NUMBER'; const direction = 'ASC'; await expect( service.tasksService.getTasks( accessToken, offset, limit, status, paramName, direction, ), ).rejects.toEqual( new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, ), ); }); it('取得したタスク一覧が不正な形式の場合、エラーを返却する', async () => { const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue(); tasksRepositoryMockValue.getTasksFromAccountId = { tasks: [ { id: 1, job_number: '00000001', account_id: 1, is_job_number_enabled: true, audio_file_id: 1, status: 'Uploaded', priority: '00', created_at: new Date('2023-01-01T01:01:01.000'), option_items: undefined, // 存在しない場合でも空配列であるはずのものがundefined file: { id: 1, account_id: 1, owner_user_id: 1, url: 'test/test.zip', file_name: 'test.zip', author_id: 'AUTHOR', work_type_id: 'WorkType', started_at: new Date('2023-01-01T01:01:01.000'), duration: '123000', finished_at: new Date('2023-01-01T01:01:01.000'), uploaded_at: new Date('2023-01-01T01:01:01.000'), file_size: 123000, priority: '00', audio_format: 'DS', comment: 'comment', is_encrypted: true, }, }, ], permissions: [], count: 1, }; const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue(); const service = await makeTasksServiceMock( tasksRepositoryMockValue, usersRepositoryMockValue, adb2cServiceMockValue, ); const accessToken = { userId: 'userId', role: 'admin', tier: 5 }; const offset = 0; const limit = 20; const status = ['Uploaded', 'Backup']; const paramName = 'JOB_NUMBER'; const direction = 'ASC'; await expect( service.tasksService.getTasks( accessToken, offset, limit, status, paramName, direction, ), ).rejects.toEqual( new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, ), ); }); it('タスク一覧を取得できる(author)', async () => { const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue(); if (usersRepositoryMockValue.findUserByExternalId instanceof Error) { return; } usersRepositoryMockValue.findUserByExternalId.role = 'author'; const service = await makeTasksServiceMock( tasksRepositoryMockValue, usersRepositoryMockValue, adb2cServiceMockValue, ); const accessToken = { userId: 'userId', role: 'author', tier: 5 }; const offset = 0; const limit = 20; const status = ['Uploaded', 'Backup']; const paramName = 'JOB_NUMBER'; const direction = 'ASC'; const result = await service.tasksService.getTasks( accessToken, offset, limit, status, paramName, direction, ); expect(result).toEqual({ tasks: [ { assignees: [{ typistName: 'XXXX XXX', typistUserId: 1 }], audioCreatedDate: '2023-01-01T01:01:01.000Z', audioDuration: '123000', audioFileId: 1, audioFinishedDate: '2023-01-01T01:01:01.000Z', audioFormat: 'DS', audioUploadedDate: '2023-01-01T01:01:01.000Z', authorId: 'AUTHOR', comment: 'comment', fileName: 'test.zip', fileSize: 123000, isEncrypted: true, jobNumber: '00000001', optionItemList: [ { optionItemLabel: 'label01', optionItemValue: 'value01' }, { optionItemLabel: 'label02', optionItemValue: 'value02' }, { optionItemLabel: 'label03', optionItemValue: 'value03' }, { optionItemLabel: 'label04', optionItemValue: 'value04' }, { optionItemLabel: 'label05', optionItemValue: 'value05' }, { optionItemLabel: 'label06', optionItemValue: 'value06' }, { optionItemLabel: 'label07', optionItemValue: 'value07' }, { optionItemLabel: 'label08', optionItemValue: 'value08' }, { optionItemLabel: 'label09', optionItemValue: 'value09' }, { optionItemLabel: 'label10', optionItemValue: 'value10' }, ], priority: '00', status: 'Uploaded', transcriptionFinishedDate: undefined, transcriptionStartedDate: undefined, typist: undefined, url: 'test/test.zip', workType: 'WorkType', }, ], total: 1, }); expect( service.taskRepoService.getTasksFromAuthorIdAndAccountId, ).toHaveBeenCalledWith('abcdef', 1, 0, 20, 'JOB_NUMBER', 'ASC', [ 'Uploaded', 'Backup', ]); }); it('タスク一覧の取得に失敗した場合、エラーを返却する(author)', async () => { const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue(); tasksRepositoryMockValue.getTasksFromAuthorIdAndAccountId = new Error( 'DB failed', ); const service = await makeTasksServiceMock( tasksRepositoryMockValue, usersRepositoryMockValue, adb2cServiceMockValue, ); const accessToken = { userId: 'userId', role: 'author', tier: 5 }; const offset = 0; const limit = 20; const status = ['Uploaded', 'Backup']; const paramName = 'JOB_NUMBER'; const direction = 'ASC'; await expect( service.tasksService.getTasks( accessToken, offset, limit, status, paramName, direction, ), ).rejects.toEqual( new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, ), ); }); it('タスク一覧を取得できる(typist)', async () => { const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue(); if (usersRepositoryMockValue.findUserByExternalId instanceof Error) { return; } usersRepositoryMockValue.findUserByExternalId.role = 'typist'; const service = await makeTasksServiceMock( tasksRepositoryMockValue, usersRepositoryMockValue, adb2cServiceMockValue, ); const accessToken = { userId: 'userId', role: 'typist', tier: 5 }; const offset = 0; const limit = 20; const status = ['Uploaded', 'Backup']; const paramName = 'JOB_NUMBER'; const direction = 'ASC'; const result = await service.tasksService.getTasks( accessToken, offset, limit, status, paramName, direction, ); expect(result).toEqual({ tasks: [ { assignees: [{ typistName: 'XXXX XXX', typistUserId: 1 }], audioCreatedDate: '2023-01-01T01:01:01.000Z', audioDuration: '123000', audioFileId: 1, audioFinishedDate: '2023-01-01T01:01:01.000Z', audioFormat: 'DS', audioUploadedDate: '2023-01-01T01:01:01.000Z', authorId: 'AUTHOR', comment: 'comment', fileName: 'test.zip', fileSize: 123000, isEncrypted: true, jobNumber: '00000001', optionItemList: [ { optionItemLabel: 'label01', optionItemValue: 'value01' }, { optionItemLabel: 'label02', optionItemValue: 'value02' }, { optionItemLabel: 'label03', optionItemValue: 'value03' }, { optionItemLabel: 'label04', optionItemValue: 'value04' }, { optionItemLabel: 'label05', optionItemValue: 'value05' }, { optionItemLabel: 'label06', optionItemValue: 'value06' }, { optionItemLabel: 'label07', optionItemValue: 'value07' }, { optionItemLabel: 'label08', optionItemValue: 'value08' }, { optionItemLabel: 'label09', optionItemValue: 'value09' }, { optionItemLabel: 'label10', optionItemValue: 'value10' }, ], priority: '00', status: 'Uploaded', transcriptionFinishedDate: undefined, transcriptionStartedDate: undefined, typist: undefined, url: 'test/test.zip', workType: 'WorkType', }, ], total: 1, }); expect( service.taskRepoService.getTasksFromTypistRelations, ).toHaveBeenCalledWith('userId', 0, 20, 'JOB_NUMBER', 'ASC', [ 'Uploaded', 'Backup', ]); }); it('タスク一覧の取得に失敗した場合、エラーを返却する(typist)', async () => { const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue(); tasksRepositoryMockValue.getTasksFromTypistRelations = new Error( 'DB failed', ); const service = await makeTasksServiceMock( tasksRepositoryMockValue, usersRepositoryMockValue, adb2cServiceMockValue, ); const accessToken = { userId: 'userId', role: 'typist', tier: 5 }; const offset = 0; const limit = 20; const status = ['Uploaded', 'Backup']; const paramName = 'JOB_NUMBER'; const direction = 'ASC'; await expect( service.tasksService.getTasks( accessToken, offset, limit, status, paramName, direction, ), ).rejects.toEqual( new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, ), ); }); it('想定外のRoleの場合、エラーを返却する', async () => { const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue(); const service = await makeTasksServiceMock( tasksRepositoryMockValue, usersRepositoryMockValue, adb2cServiceMockValue, ); const accessToken = { userId: 'userId', role: 'XXX', tier: 5 }; const offset = 0; const limit = 20; const status = ['Uploaded', 'Backup']; const paramName = 'JOB_NUMBER'; const direction = 'ASC'; await expect( service.tasksService.getTasks( accessToken, offset, limit, status, paramName, direction, ), ).rejects.toEqual( new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, ), ); }); it('AdB2Cのリクエスト上限超過時、専用のエラーを返却する', async () => { const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue(); adb2cServiceMockValue.getUsers = new Adb2cTooManyRequestsError(); const service = await makeTasksServiceMock( tasksRepositoryMockValue, usersRepositoryMockValue, adb2cServiceMockValue, ); const accessToken = { userId: 'userId', role: 'admin', tier: 5 }; const offset = 0; const limit = 20; const status = ['Uploaded,Backup']; const paramName = 'JOB_NUMBER'; const direction = 'ASC'; await expect( service.tasksService.getTasks( accessToken, offset, limit, status, paramName, direction, ), ).rejects.toEqual( new HttpException( makeErrorResponse('E000301'), HttpStatus.INTERNAL_SERVER_ERROR, ), ); }); describe('DBテスト', () => { let source: DataSource = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', database: ':memory:', logging: false, entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: true, // trueにすると自動的にmigrationが行われるため注意 }); return source.initialize(); }); afterEach(async () => { await source.destroy(); source = null; }); it('[Admin] Taskが0件であっても実行できる', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { externalId } = await createUser( source, accountId, 'userId', 'admin', ); const service = module.get(TasksService); const accessToken = { userId: externalId, role: 'admin', tier: 5 }; const offset = 0; const limit = 20; const status = ['Uploaded,Backup']; const paramName = 'JOB_NUMBER'; const direction = 'ASC'; const { tasks, total } = await service.getTasks( accessToken, offset, limit, status, paramName, direction, ); expect(tasks).toEqual([]); expect(total).toEqual(0); }); it('[Author] Authorは自分が作成者のTask一覧を取得できる', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId } = await createUser( source, accountId, 'userId', 'author', 'MY_AUTHOR_ID', ); await createTask( source, accountId, userId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Uploaded', ); await createTask( source, accountId, userId, 'MY_AUTHOR_ID', '', '01', '00000002', 'Uploaded', ); const service = module.get(TasksService); const accessToken = { userId: 'userId', role: 'author', tier: 5 }; const offset = 0; const limit = 20; const status = ['Uploaded', 'Backup']; const paramName = 'JOB_NUMBER'; const direction = 'ASC'; const { tasks, total } = await service.getTasks( accessToken, offset, limit, status, paramName, direction, ); expect(total).toEqual(2); { const task = tasks[0]; expect(task.jobNumber).toEqual('00000001'); } { const task = tasks[1]; expect(task.jobNumber).toEqual('00000002'); } }); it('[Author] Authorは同一アカウントであっても自分以外のAuhtorのTaskは取得できない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: userId_1 } = await createUser( source, accountId, 'userId_1', 'author', 'AUTHOR_ID_1', ); const { userId: userId_2 } = await createUser( source, accountId, 'userId_2', 'author', 'AUTHOR_ID_2', ); await createTask( source, accountId, userId_1, 'AUTHOR_ID_1', '', '01', '00000001', 'Uploaded', ); await createTask( source, accountId, userId_2, 'AUTHOR_ID_2', '', '01', '00000002', 'Uploaded', ); const service = module.get(TasksService); const accessToken = { userId: 'userId_1', role: 'author', tier: 5 }; const offset = 0; const limit = 20; const status = ['Uploaded', 'Backup']; const paramName = 'JOB_NUMBER'; const direction = 'ASC'; const { tasks, total } = await service.getTasks( accessToken, offset, limit, status, paramName, direction, ); expect(total).toEqual(1); { const task = tasks[0]; expect(task.jobNumber).toEqual('00000001'); } }); }); }); describe('changeCheckoutPermission', () => { let source: DataSource = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', database: ':memory:', logging: false, entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: true, // trueにすると自動的にmigrationが行われるため注意 }); return source.initialize(); }); afterEach(async () => { await source.destroy(); source = null; }); it('タスクのチェックアウト権限を変更できる。(個人指定)', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: typistUserId_1 } = await createUser( source, accountId, 'typist-user-external-id', 'typist', ); const { userId: typistUserId_2 } = await createUser( source, accountId, 'typist-user-2-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const { taskId } = await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Uploaded', ); const { userGroupId } = await createUserGroup( source, accountId, 'USER_GROUP_A', [typistUserId_1], ); await createCheckoutPermissions(source, taskId, typistUserId_1); await createCheckoutPermissions(source, taskId, undefined, userGroupId); const service = module.get(TasksService); await service.changeCheckoutPermission( 1, [{ typistName: 'typist-user-2', typistUserId: typistUserId_2 }], 'author-user-external-id', ['admin'], ); const permisions = await getCheckoutPermissions(source, taskId); expect(permisions.length).toEqual(1); expect(permisions[0]).toEqual({ id: 3, task_id: taskId, user_id: typistUserId_2, user_group_id: null, }); }); it('タスクのチェックアウト権限を変更できる。(グループ指定)', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: typistUserId_1 } = await createUser( source, accountId, 'typist-user-external-id', 'typist', ); const { userId: typistUserId_2 } = await createUser( source, accountId, 'typist-user-2-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const { taskId } = await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Uploaded', ); const { userGroupId: userGroupId_1 } = await createUserGroup( source, accountId, 'USER_GROUP_A', [typistUserId_1], ); const { userGroupId: userGroupId_2 } = await createUserGroup( source, accountId, 'USER_GROUP_B', [typistUserId_2], ); await createCheckoutPermissions(source, taskId, typistUserId_1); await createCheckoutPermissions(source, taskId, undefined, userGroupId_1); const service = module.get(TasksService); await service.changeCheckoutPermission( 1, [{ typistName: 'USER_GROUP_B', typistGroupId: userGroupId_2 }], 'author-user-external-id', ['admin'], ); const permisions = await getCheckoutPermissions(source, taskId); expect(permisions.length).toEqual(1); expect(permisions[0]).toEqual({ id: 3, task_id: taskId, user_id: null, user_group_id: userGroupId_2, }); }); it('タスクのチェックアウト権限を変更できる。(チェックアウト権限を外す)', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: typistUserId_1 } = await createUser( source, accountId, 'typist-user-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const { taskId } = await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Uploaded', ); const { userGroupId } = await createUserGroup( source, accountId, 'USER_GROUP_A', [typistUserId_1], ); await createCheckoutPermissions(source, taskId, typistUserId_1); await createCheckoutPermissions(source, taskId, undefined, userGroupId); const service = module.get(TasksService); await service.changeCheckoutPermission(1, [], 'author-user-external-id', [ 'admin', ]); const permisions = await getCheckoutPermissions(source, taskId); expect(permisions.length).toEqual(0); }); it('ユーザーが存在しない場合、タスクのチェックアウト権限を変更できない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: typistUserId_1 } = await createUser( source, accountId, 'typist-user-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const { taskId } = await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Uploaded', ); const { userGroupId } = await createUserGroup( source, accountId, 'USER_GROUP_A', [typistUserId_1], ); await createCheckoutPermissions(source, taskId, typistUserId_1); await createCheckoutPermissions(source, taskId, undefined, userGroupId); const service = module.get(TasksService); await expect( service.changeCheckoutPermission( 1, [{ typistName: 'not-exist-user', typistUserId: 999 }], 'author-user-external-id', ['admin'], ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010204'), HttpStatus.BAD_REQUEST), ); }); it('ユーザーグループが存在しない場合、タスクのチェックアウト権限を変更できない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: typistUserId_1 } = await createUser( source, accountId, 'typist-user-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const { taskId } = await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Uploaded', ); const { userGroupId } = await createUserGroup( source, accountId, 'USER_GROUP_A', [typistUserId_1], ); await createCheckoutPermissions(source, taskId, typistUserId_1); await createCheckoutPermissions(source, taskId, undefined, userGroupId); const service = module.get(TasksService); await expect( service.changeCheckoutPermission( 1, [{ typistName: 'not-exist-user-group', typistGroupId: 999 }], 'author-user-external-id', ['admin'], ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010204'), HttpStatus.BAD_REQUEST), ); }); it('タスクが存在しない場合、タスクのチェックアウト権限を変更できない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: typistUserId } = await createUser( source, accountId, 'typist-user-external-id', 'typist', ); await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const service = module.get(TasksService); await expect( service.changeCheckoutPermission( 1, [{ typistName: 'typist-user', typistUserId: typistUserId }], 'author-user-external-id', ['admin'], ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST), ); }); it('タスクのステータスがUploadedでない場合、タスクのチェックアウト権限を変更できない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: typistUserId } = await createUser( source, accountId, 'typist-user-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Inprogress', ); const service = module.get(TasksService); await expect( service.changeCheckoutPermission( 1, [{ typistName: 'typist-user', typistUserId: typistUserId }], 'author-user-external-id', ['admin'], ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST), ); }); it('ユーザーのRoleがAuthorでタスクのAuthorIDと自身のAuthorIDが一致しない場合、タスクのチェックアウト権限を変更できない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: typistUserId } = await createUser( source, accountId, 'typist-user-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); await createTask( source, accountId, authorUserId, 'OTHER_AUTHOR_ID', '', '01', '00000001', 'Uploaded', ); const service = module.get(TasksService); await expect( service.changeCheckoutPermission( 1, [{ typistName: 'typist-user', typistUserId: typistUserId }], 'author-user-external-id', ['author'], ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST), ); }); }); describe('checkout', () => { let source: DataSource = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', database: ':memory:', logging: false, entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: true, // trueにすると自動的にmigrationが行われるため注意 }); return source.initialize(); }); afterEach(async () => { await source.destroy(); source = null; }); it('ユーザーのRoleがTypistで、タスクのチェックアウト権限が個人指定である時、タスクをチェックアウトできる', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: typistUserId } = await createUser( source, accountId, 'typist-user-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const { taskId } = await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Uploaded', ); const { userGroupId } = await createUserGroup( source, accountId, 'USER_GROUP_A', [typistUserId], ); await createCheckoutPermissions(source, taskId, typistUserId); await createCheckoutPermissions(source, taskId, undefined, userGroupId); const service = module.get(TasksService); const initTask = await getTask(source, taskId); await service.checkout(1, ['typist'], 'typist-user-external-id'); const { status, typist_user_id, started_at } = await getTask( source, taskId, ); const permisions = await getCheckoutPermissions(source, taskId); expect(status).toEqual('InProgress'); expect(typist_user_id).toEqual(typistUserId); expect(started_at).not.toEqual(initTask.started_at); expect(permisions.length).toEqual(1); expect(permisions[0]).toEqual({ id: 3, task_id: 1, user_id: 1, user_group_id: null, }); }); it('ユーザーのRoleがTypistで、タスクのチェックアウト権限がグループ指定である時、タスクをチェックアウトできる', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: typistUserId } = await createUser( source, accountId, 'typist-user-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const { taskId } = await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Uploaded', ); const { userGroupId } = await createUserGroup( source, accountId, 'USER_GROUP_A', [typistUserId], ); await createCheckoutPermissions(source, taskId, typistUserId); await createCheckoutPermissions(source, taskId, undefined, userGroupId); const service = module.get(TasksService); const initTask = await getTask(source, taskId); await service.checkout(1, ['typist'], 'typist-user-external-id'); const { status, typist_user_id, started_at } = await getTask( source, taskId, ); const permisions = await getCheckoutPermissions(source, taskId); expect(status).toEqual('InProgress'); expect(typist_user_id).toEqual(typistUserId); expect(started_at).not.toEqual(initTask.started_at); expect(permisions.length).toEqual(1); expect(permisions[0]).toEqual({ id: 3, task_id: 1, user_id: 1, user_group_id: null, }); }); it('ユーザーのRoleがTypistで、タスクのステータスがPendingである時、タスクをチェックアウトできる', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: typistUserId } = await createUser( source, accountId, 'typist-user-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const { taskId } = await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Pending', ); await createCheckoutPermissions(source, taskId, typistUserId); const service = module.get(TasksService); const initTask = await getTask(source, taskId); await service.checkout(1, ['typist'], 'typist-user-external-id'); const { status, typist_user_id, started_at } = await getTask( source, taskId, ); const permisions = await getCheckoutPermissions(source, taskId); expect(status).toEqual('InProgress'); expect(typist_user_id).toEqual(typistUserId); //タスクの元々のステータスがPending,Inprogressの場合、文字起こし開始時刻は更新されない expect(started_at).toEqual(initTask.started_at); expect(permisions.length).toEqual(1); expect(permisions[0]).toEqual({ id: 2, task_id: 1, user_id: 1, user_group_id: null, }); }); it('ユーザーのRoleがTypistで、対象のタスクのStatus[Uploaded,Inprogress,Pending]以外の時、タスクをチェックアウトできない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); await createUser( source, accountId, 'typist-user-external-id', 'typist', 'MY_AUTHOR_ID', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Backup', ); const service = module.get(TasksService); await expect( service.checkout(1, ['typist'], 'typist-user-external-id'), ).rejects.toEqual( new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST), ); }); it('ユーザーのRoleがTypistで、チェックアウト権限が存在しない時、タスクをチェックアウトできない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); await createUser( source, accountId, 'typist-user-external-id', 'typist', 'MY_AUTHOR_ID', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Uploaded', ); const service = module.get(TasksService); await expect( service.checkout(1, ['typist'], 'typist-user-external-id'), ).rejects.toEqual( new HttpException(makeErrorResponse('E010602'), HttpStatus.BAD_REQUEST), ); }); it('ユーザーのRoleがAuthorで、アップロードした音声ファイルに紐づいたタスクをチェックアウトできる(Uploaded)', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Uploaded', ); const service = module.get(TasksService); expect( await service.checkout(1, ['author'], 'author-user-external-id'), ).toEqual(undefined); }); it('ユーザーのRoleがAuthorで、アップロードした音声ファイルに紐づいたタスクをチェックアウトできる(Finished)', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Uploaded', ); const service = module.get(TasksService); expect( await service.checkout(1, ['author'], 'author-user-external-id'), ).toEqual(undefined); }); it('ユーザーのRoleがAuthorで、アップロードした音声ファイルに紐づいたタスクが存在しない場合、タスクをチェックアウトできない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const service = module.get(TasksService); await expect( service.checkout(1, ['author'], 'author-user-external-id'), ).rejects.toEqual( new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST), ); }); it('ユーザーのRoleがAuthorで、音声ファイルに紐づいたタスクでユーザーと一致するAuthorIDでない場合、タスクをチェックアウトできない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); await createTask( source, accountId, authorUserId, 'OTHOR_AUTHOR', '', '01', '00000001', 'Uploaded', ); const service = module.get(TasksService); await expect( service.checkout(1, ['author'], 'author-user-external-id'), ).rejects.toEqual( new HttpException(makeErrorResponse('E010602'), HttpStatus.BAD_REQUEST), ); }); it('ユーザーのRoleに[Typist,author]が設定されていない時、タスクをチェックアウトできない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); await createUser( source, accountId, 'none-user-external-id', 'none', 'MY_AUTHOR_ID', ); const service = module.get(TasksService); await expect( service.checkout(1, ['none'], 'none-user-external-id'), ).rejects.toEqual( new HttpException(makeErrorResponse('E010602'), HttpStatus.BAD_REQUEST), ); }); }); describe('checkin', () => { let source: DataSource = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', database: ':memory:', logging: false, entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: true, // trueにすると自動的にmigrationが行われるため注意 }); return source.initialize(); }); afterEach(async () => { await source.destroy(); source = null; }); it('API実行者が文字起こし実行中のタスクである場合、タスクをチェックインできる', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: typistUserId } = await createUser( source, accountId, 'typist-user-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const { taskId } = await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'InProgress', typistUserId, ); await createCheckoutPermissions(source, taskId, typistUserId); const service = module.get(TasksService); const initTask = await getTask(source, taskId); await service.checkin(1, 'typist-user-external-id'); const { status, finished_at } = await getTask(source, taskId); expect(status).toEqual('Finished'); expect(finished_at).not.toEqual(initTask.finished_at); }); it('タスクのステータスがInprogressでない時、タスクをチェックインできない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: typistUserId } = await createUser( source, accountId, 'typist-user-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const { taskId } = await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Uploaded', typistUserId, ); await createCheckoutPermissions(source, taskId, typistUserId); const service = module.get(TasksService); await expect(service.checkin(1, 'typist-user-external-id')).rejects.toEqual( new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST), ); }); it('API実行者が文字起こし実行中のタスクでない場合、タスクをチェックインできない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); await createUser(source, accountId, 'typist-user-external-id', 'typist'); const { userId: anotherTypistUserId } = await createUser( source, accountId, 'another-typist-user-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const { taskId } = await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'InProgress', // API実行者のタスクではないため、typist_user_idは設定しない ); await createCheckoutPermissions(source, taskId, anotherTypistUserId); const service = module.get(TasksService); await expect(service.checkin(1, 'typist-user-external-id')).rejects.toEqual( new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST), ); }); it('タスクがない時、タスクをチェックインできない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); await createUser(source, accountId, 'typist-user-external-id', 'typist'); await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const service = module.get(TasksService); await expect(service.checkin(1, 'typist-user-external-id')).rejects.toEqual( new HttpException(makeErrorResponse('E010603'), HttpStatus.BAD_REQUEST), ); }); }); describe('suspend', () => { let source: DataSource = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', database: ':memory:', logging: false, entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: true, // trueにすると自動的にmigrationが行われるため注意 }); return source.initialize(); }); afterEach(async () => { await source.destroy(); source = null; }); it('API実行者が文字起こし実行中のタスクである場合、タスクを中断できる', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: typistUserId } = await createUser( source, accountId, 'typist-user-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const { taskId } = await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'InProgress', typistUserId, ); const service = module.get(TasksService); await service.suspend(1, 'typist-user-external-id'); const { status } = await getTask(source, taskId); expect(status).toEqual('Pending'); }); it('タスクのステータスがInprogressでない時、タスクを中断できない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); const { userId: typistUserId } = await createUser( source, accountId, 'typist-user-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const { taskId } = await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'Uploaded', typistUserId, ); await createCheckoutPermissions(source, taskId, typistUserId); const service = module.get(TasksService); await expect(service.suspend(1, 'typist-user-external-id')).rejects.toEqual( new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST), ); }); it('API実行者が文字起こし実行中のタスクでない場合、タスクを中断できない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); await createUser(source, accountId, 'typist-user-external-id', 'typist'); const { userId: anotherTypistUserId } = await createUser( source, accountId, 'another-typist-user-external-id', 'typist', ); const { userId: authorUserId } = await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const { taskId } = await createTask( source, accountId, authorUserId, 'MY_AUTHOR_ID', '', '01', '00000001', 'InProgress', // API実行者のタスクではないため、typist_user_idは設定しない ); await createCheckoutPermissions(source, taskId, anotherTypistUserId); const service = module.get(TasksService); await expect(service.checkin(1, 'typist-user-external-id')).rejects.toEqual( new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST), ); }); it('タスクがない時、タスクを中断できない', async () => { const module = await makeTestingModule(source); const { accountId } = await createAccount(source); await createUser(source, accountId, 'typist-user-external-id', 'typist'); await createUser( source, accountId, 'author-user-external-id', 'author', 'MY_AUTHOR_ID', ); const service = module.get(TasksService); await expect(service.checkin(1, 'typist-user-external-id')).rejects.toEqual( new HttpException(makeErrorResponse('E010603'), HttpStatus.BAD_REQUEST), ); }); });