OMDSCloud/dictation_server/src/features/tasks/tasks.service.spec.ts
saito.k e4ba5229df Merged PR 224: タスク中断API実装
## 概要
[Task2119: タスク中断API実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2119)

- タスク中断APIの処理を実装
- テスト実装
- チェックアウト処理のエラーチェックを修正
  - エラーが発生したときに何が原因なのかログに出力するように修正

## レビューポイント
- チェックインと同様の処理部分を切り出さずにそのまま実装したが、スマートに切り出せる方法はありそうか。
  - チェックインとサスペンドの取得処理だけ切り出してもあまりうれしくない(この2つ以外のところで使えなさそう)
    - キャンセルでは使えるかもだけど
  - チェックアウトやその他のメソッドのタスク取得とまとめようとすると、チェック内容や検索条件に差異がありきれいに切り出すことができなさそう。
    - まとめようとすると、引数が膨大でチェック項目も複雑になる

## UIの変更
- Before/Afterのスクショなど
- スクショ置き場

## 動作確認状況
- ローカルで確認

## 補足
- コンフリクトが発生しているが、現在のdevelopを取り込むとエラーになるので解消し次第、取り込んで競合解決します
- チェックインAPIの実装も入っていますが、そこは別のPRでレビューを行っているので対象外となります
2023-07-12 08:27:46 +00:00

1748 lines
52 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(TasksService);
await expect(service.checkin(1, 'typist-user-external-id')).rejects.toEqual(
new HttpException(makeErrorResponse('E010603'), HttpStatus.BAD_REQUEST),
);
});
});