import {
  makeDefaultAdb2cServiceMockValue,
  makeDefaultNotificationhubServiceMockValue,
  makeDefaultTasksRepositoryMockValue,
  makeDefaultUserGroupsRepositoryMockValue,
  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 {
  createCheckoutPermissions,
  createTask,
  createUserGroup,
  getCheckoutPermissions,
  getTask,
  makeTaskTestingModuleWithNotificaiton,
} from './test/utility';
import { Adb2cTooManyRequestsError } from '../../gateways/adb2c/adb2c.service';
import { makeContext } from '../../common/log';
import {
  makeTestAccount,
  makeTestSimpleAccount,
  makeTestUser,
} from '../../common/test/utility';
import {
  ADMIN_ROLES,
  LICENSE_ALLOCATED_STATUS,
  LICENSE_TYPE,
  TASK_STATUS,
  USER_ROLES,
} from '../../constants';
import { makeTestingModule } from '../../common/test/modules';
import { createSortCriteria } from '../users/test/utility';
import { createWorktype } from '../accounts/test/utility';
import {
  createWorkflow,
  createWorkflowTypist,
} from '../workflows/test/utility';
import { createTemplateFile } from '../templates/test/utility';
import { NotificationhubService } from '../../gateways/notificationhub/notificationhub.service';
import { Roles } from '../../common/types/role';
import { TasksRepositoryService } from '../../repositories/tasks/tasks.repository.service';
import { truncateAllTable } from '../../common/test/init';
import { makeDefaultLicensesRepositoryMockValue } from '../accounts/test/accounts.service.mock';
import { DateWithZeroTime } from '../licenses/types/types';
import { createLicense } from '../licenses/test/utility';

describe('TasksService', () => {
  it('タスク一覧を取得できる（admin）', async () => {
    const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const service = await makeTasksServiceMock(
      tasksRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cServiceMockValue,
      notificationhubServiceMockValue,
      licensesRepositoryMockValue,
    );

    const userId = 'userId';
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    expect(
      await service.tasksService.getTasks(
        makeContext('trackingId', 'requestId'),
        userId,
        [ADMIN_ROLES.ADMIN, USER_ROLES.NONE],
        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 userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    usersRepositoryMockValue.findUserByExternalId = new Error('DB failed');
    const service = await makeTasksServiceMock(
      tasksRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cServiceMockValue,
      notificationhubServiceMockValue,
      licensesRepositoryMockValue,
    );

    const userId = 'userId';
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    await expect(
      service.tasksService.getTasks(
        makeContext('trackingId', 'requestId'),
        userId,
        [ADMIN_ROLES.ADMIN, USER_ROLES.NONE],
        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 userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    tasksRepositoryMockValue.getTasksFromAccountId = new Error('DB failed');
    const service = await makeTasksServiceMock(
      tasksRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cServiceMockValue,
      notificationhubServiceMockValue,
      licensesRepositoryMockValue,
    );

    const userId = 'userId';
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    await expect(
      service.tasksService.getTasks(
        makeContext('trackingId', 'requestId'),
        userId,
        [ADMIN_ROLES.ADMIN, USER_ROLES.NONE],
        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: null, // 存在しない場合でも空配列であるはずのものがnullになっているため形式不正
          finished_at: null,
          started_at: null,
          typist_user_id: null,
          template_file_id: null,
          typist_user: null,
          template_file: null,
          created_by: null,
          updated_by: null,
          updated_at: new Date('2023-01-01T01:01:01.000'),
          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,
            deleted_at: null,
            task: null,
          },
        },
      ],
      permissions: [],
      count: 1,
    };
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const service = await makeTasksServiceMock(
      tasksRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cServiceMockValue,
      notificationhubServiceMockValue,
      licensesRepositoryMockValue,
    );
    const userId = 'userId';
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    await expect(
      service.tasksService.getTasks(
        makeContext('trackingId', 'requestId'),
        userId,
        [ADMIN_ROLES.ADMIN, USER_ROLES.NONE],
        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 userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    if (usersRepositoryMockValue.findUserByExternalId instanceof Error) {
      return;
    }
    usersRepositoryMockValue.findUserByExternalId.role = 'author';
    const service = await makeTasksServiceMock(
      tasksRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cServiceMockValue,
      notificationhubServiceMockValue,
      licensesRepositoryMockValue,
    );

    const userId = 'userId';
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    const context = makeContext('trackingId', 'requestId');
    const result = await service.tasksService.getTasks(
      context,
      userId,
      [USER_ROLES.AUTHOR],
      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(context, 'abcdef', 1, 0, 20, 'JOB_NUMBER', 'ASC', [
      'Uploaded',
      'Backup',
    ]);
  });

  it('タスク一覧の取得に失敗した場合、エラーを返却する（author）', async () => {
    const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    tasksRepositoryMockValue.getTasksFromAuthorIdAndAccountId = new Error(
      'DB failed',
    );
    const service = await makeTasksServiceMock(
      tasksRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cServiceMockValue,
      notificationhubServiceMockValue,
      licensesRepositoryMockValue,
    );

    const userId = 'userId';
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    await expect(
      service.tasksService.getTasks(
        makeContext('trackingId', 'requestId'),
        userId,
        [USER_ROLES.AUTHOR],
        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 userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    if (usersRepositoryMockValue.findUserByExternalId instanceof Error) {
      return;
    }
    usersRepositoryMockValue.findUserByExternalId.role = 'typist';

    const service = await makeTasksServiceMock(
      tasksRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cServiceMockValue,
      notificationhubServiceMockValue,
      licensesRepositoryMockValue,
    );

    const userId = 'userId';
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    const context = makeContext('trackingId', 'requestId');
    const result = await service.tasksService.getTasks(
      context,
      userId,
      [USER_ROLES.TYPIST],
      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(context, 'userId', 0, 20, 'JOB_NUMBER', 'ASC', [
      'Uploaded',
      'Backup',
    ]);
  });

  it('タスク一覧の取得に失敗した場合、エラーを返却する（typist）', async () => {
    const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    tasksRepositoryMockValue.getTasksFromTypistRelations = new Error(
      'DB failed',
    );
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const service = await makeTasksServiceMock(
      tasksRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cServiceMockValue,
      notificationhubServiceMockValue,
      licensesRepositoryMockValue,
    );

    const userId = 'userId';
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    await expect(
      service.tasksService.getTasks(
        makeContext('trackingId', 'requestId'),
        userId,
        [USER_ROLES.TYPIST],
        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 userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    adb2cServiceMockValue.getUsers = new Adb2cTooManyRequestsError();
    const service = await makeTasksServiceMock(
      tasksRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cServiceMockValue,
      notificationhubServiceMockValue,
      licensesRepositoryMockValue,
    );

    const userId = 'userId';
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded,Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    await expect(
      service.tasksService.getTasks(
        makeContext('trackingId', 'requestId'),
        userId,
        [ADMIN_ROLES.ADMIN, USER_ROLES.NONE],
        offset,
        limit,
        status,
        paramName,
        direction,
      ),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E000301'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });

  describe('DBテスト', () => {
    let source: DataSource | null = null;
    beforeAll(async () => {
      if (source == null) {
        source = await (async () => {
          const s = new DataSource({
            type: 'mysql',
            host: 'test_mysql_db',
            port: 3306,
            username: 'user',
            password: 'password',
            database: 'odms',
            entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
            synchronize: false, // trueにすると自動的にmigrationが行われるため注意
          });
          return await s.initialize();
        })();
      }
    });

    beforeEach(async () => {
      if (source) {
        await truncateAllTable(source);
      }
    });

    afterAll(async () => {
      await source?.destroy();
      source = null;
    });

    it('[Admin] Taskが0件であっても実行できる', async () => {
      const notificationhubServiceMockValue =
        makeDefaultNotificationhubServiceMockValue();
      if (!source) fail();
      const module = await makeTaskTestingModuleWithNotificaiton(
        source,
        notificationhubServiceMockValue,
      );
      if (!module) fail();
      const { id: accountId } = await makeTestSimpleAccount(source);
      const { external_id: externalId } = await makeTestUser(source, {
        account_id: accountId,
        external_id: 'userId',
        role: 'admin',
      });

      const service = module.get<TasksService>(TasksService);
      const offset = 0;
      const limit = 20;
      const status = ['Uploaded,Backup'];
      const paramName = 'JOB_NUMBER';
      const direction = 'ASC';

      const { tasks, total } = await service.getTasks(
        makeContext('trackingId', 'requestId'),
        externalId,
        [ADMIN_ROLES.ADMIN, USER_ROLES.NONE],
        offset,
        limit,
        status,
        paramName,
        direction,
      );
      expect(tasks).toEqual([]);
      expect(total).toEqual(0);
    });

    it('[Author] Authorは自分が作成者のTask一覧を取得できる', async () => {
      const notificationhubServiceMockValue =
        makeDefaultNotificationhubServiceMockValue();
      if (!source) fail();
      const module = await makeTaskTestingModuleWithNotificaiton(
        source,
        notificationhubServiceMockValue,
      );
      if (!module) fail();
      const { id: accountId } = await makeTestSimpleAccount(source);
      const { id: userId, external_id } = await makeTestUser(source, {
        account_id: accountId,
        external_id: 'userId',
        role: 'author',
        author_id: '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 offset = 0;
      const limit = 20;
      const status = ['Uploaded', 'Backup'];
      const paramName = 'JOB_NUMBER';
      const direction = 'ASC';

      const { tasks, total } = await service.getTasks(
        makeContext('trackingId', 'requestId'),
        external_id,
        [USER_ROLES.AUTHOR],
        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 notificationhubServiceMockValue =
        makeDefaultNotificationhubServiceMockValue();
      if (!source) fail();
      const module = await makeTaskTestingModuleWithNotificaiton(
        source,
        notificationhubServiceMockValue,
      );
      if (!module) fail();
      const { id: accountId } = await makeTestSimpleAccount(source);
      const { id: userId_1, external_id } = await makeTestUser(source, {
        account_id: accountId,
        external_id: 'userId_1',
        role: 'author',
        author_id: 'AUTHOR_ID_1',
      });
      const { id: userId_2 } = await makeTestUser(source, {
        account_id: accountId,
        external_id: 'userId_2',
        role: 'author',
        author_id: '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 offset = 0;
      const limit = 20;
      const status = ['Uploaded', 'Backup'];
      const paramName = 'JOB_NUMBER';
      const direction = 'ASC';

      const { tasks, total } = await service.getTasks(
        makeContext('trackingId', 'requestId'),
        external_id,
        [USER_ROLES.AUTHOR],
        offset,
        limit,
        status,
        paramName,
        direction,
      );
      expect(total).toEqual(1);
      {
        const task = tasks[0];
        expect(task.jobNumber).toEqual('00000001');
      }
    });
  });
});

describe('changeCheckoutPermission', () => {
  let source: DataSource | null = null;
  beforeAll(async () => {
    if (source == null) {
      source = await (async () => {
        const s = new DataSource({
          type: 'mysql',
          host: 'test_mysql_db',
          port: 3306,
          username: 'user',
          password: 'password',
          database: 'odms',
          entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
          synchronize: false, // trueにすると自動的にmigrationが行われるため注意
        });
        return await s.initialize();
      })();
    }
  });

  beforeEach(async () => {
    if (source) {
      await truncateAllTable(source);
    }
  });

  afterAll(async () => {
    await source?.destroy();
    source = null;
  });

  it('タスクのチェックアウト権限を変更できる。(個人指定)', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId_1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: typistUserId_2 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-2-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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);
    const NotificationHubService = module.get<NotificationhubService>(
      NotificationhubService,
    );
    await service.changeCheckoutPermission(
      makeContext('trackingId', 'requestId'),
      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,
    });
    const resultTask = await getTask(source, taskId);
    // 通知処理が想定通りの引数で呼ばれているか確認
    expect(NotificationHubService.notify).toHaveBeenCalledWith(
      makeContext('trackingId', 'requestId'),
      [`user_${typistUserId_2}`],
      {
        authorId: 'MY_AUTHOR_ID',
        filename: 'x',
        priority: 'High',
        uploadedAt: resultTask?.file?.uploaded_at.toISOString(),
      },
    );
  });

  it('タスクのチェックアウト権限を変更できる。(グループ指定)', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId_1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: typistUserId_2 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-2-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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);
    const NotificationHubService = module.get<NotificationhubService>(
      NotificationhubService,
    );
    await service.changeCheckoutPermission(
      makeContext('trackingId', 'requestId'),
      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,
    });

    const resultTask = await getTask(source, taskId);
    // 通知処理が想定通りの引数で呼ばれているか確認
    expect(NotificationHubService.notify).toHaveBeenCalledWith(
      makeContext('trackingId', 'requestId'),
      [`user_${typistUserId_2}`],
      {
        authorId: 'MY_AUTHOR_ID',
        filename: 'x',
        priority: 'High',
        uploadedAt: resultTask?.file?.uploaded_at.toISOString(),
      },
    );
  });

  it('タスクのチェックアウト権限を変更できる。(チェックアウト権限を外す)', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId_1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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(
      makeContext('trackingId', 'requestId'),
      1,
      [],
      'author-user-external-id',
      ['admin'],
    );
    const permisions = await getCheckoutPermissions(source, taskId);
    expect(permisions.length).toEqual(0);
  });

  it('ユーザーが存在しない場合、タスクのチェックアウト権限を変更できない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId_1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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);

    try {
      await service.changeCheckoutPermission(
        makeContext('trackingId', 'requestId'),
        1,
        [{ typistName: 'not-exist-user', typistUserId: 999 }],
        'author-user-external-id',
        ['admin'],
      );
      fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010204'));
      } else {
        fail();
      }
    }
  });

  it('ユーザーがメール認証されていない場合、タスクのチェックアウト権限を変更できない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId_1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id_1',
      role: 'typist',
    });
    const { id: typistUserId_2 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id_2',
      role: 'typist',
      email_verified: false,
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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);

    try {
      await service.changeCheckoutPermission(
        makeContext('trackingId', 'requestId'),
        1,
        [{ typistName: 'not-verified-user', typistUserId: typistUserId_2 }],
        'author-user-external-id',
        ['admin'],
      );
      fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010204'));
      } else {
        fail();
      }
    }
  });

  it('ユーザーグループが存在しない場合、タスクのチェックアウト権限を変更できない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId_1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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);

    try {
      await service.changeCheckoutPermission(
        makeContext('trackingId', 'requestId'),
        1,
        [{ typistName: 'not-exist-user-group', typistGroupId: 999 }],
        'author-user-external-id',
        ['admin'],
      ),
        fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010204'));
      } else {
        fail();
      }
    }
  });

  it('タスクが存在しない場合、タスクのチェックアウト権限を変更できない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    const service = module.get<TasksService>(TasksService);

    try {
      await service.changeCheckoutPermission(
        makeContext('trackingId', 'requestId'),
        1,
        [{ typistName: 'typist-user', typistUserId: typistUserId }],
        'author-user-external-id',
        ['admin'],
      ),
        fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010601'));
      } else {
        fail();
      }
    }
  });

  it('タスクのステータスがUploadedでない場合、タスクのチェックアウト権限を変更できない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Inprogress',
    );
    const service = module.get<TasksService>(TasksService);

    try {
      await service.changeCheckoutPermission(
        makeContext('trackingId', 'requestId'),
        1,
        [{ typistName: 'typist-user', typistUserId: typistUserId }],
        'author-user-external-id',
        ['admin'],
      ),
        fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010601'));
      } else {
        fail();
      }
    }
  });

  it('ユーザーのRoleがAuthorでタスクのAuthorIDと自身のAuthorIDが一致しない場合、タスクのチェックアウト権限を変更できない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    await createTask(
      source,
      accountId,
      authorUserId,
      'OTHER_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
    );
    const service = module.get<TasksService>(TasksService);

    try {
      await service.changeCheckoutPermission(
        makeContext('trackingId', 'requestId'),
        1,
        [{ typistName: 'typist-user', typistUserId: typistUserId }],
        'author-user-external-id',
        ['author'],
      ),
        fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010601'));
      } else {
        fail();
      }
    }
  });

  it('通知に失敗した場合、エラーとなる', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    notificationhubServiceMockValue.notify = new Error('Notify Error.');
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId_1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: typistUserId_2 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-2-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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);

    try {
      await service.changeCheckoutPermission(
        makeContext('trackingId', 'requestId'),
        1,
        [{ typistName: 'typist-user-2', typistUserId: typistUserId_2 }],
        'author-user-external-id',
        ['admin'],
      ),
        fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
        expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
      } else {
        fail();
      }
    }
  });
});

describe('checkout', () => {
  let source: DataSource | null = null;
  beforeAll(async () => {
    if (source == null) {
      source = await (async () => {
        const s = new DataSource({
          type: 'mysql',
          host: 'test_mysql_db',
          port: 3306,
          username: 'user',
          password: 'password',
          database: 'odms',
          entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
          synchronize: false, // trueにすると自動的にmigrationが行われるため注意
        });
        return await s.initialize();
      })();
    }
  });

  beforeEach(async () => {
    if (source) {
      await truncateAllTable(source);
    }
  });

  afterAll(async () => {
    await source?.destroy();
    source = null;
  });

  it('ユーザーのRoleがTypistで、タスクのチェックアウト権限が個人指定である時、タスクをチェックアウトできる', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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(
      makeContext('trackingId', 'requestId'),
      1,
      ['typist'],
      'typist-user-external-id',
    );
    const resultTask = await getTask(source, taskId);
    const permisions = await getCheckoutPermissions(source, taskId);

    expect(resultTask?.status).toEqual('InProgress');
    expect(resultTask?.typist_user_id).toEqual(typistUserId);
    expect(resultTask?.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 () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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(
      makeContext('trackingId', 'requestId'),
      1,
      ['typist'],
      'typist-user-external-id',
    );
    const resultTask = await getTask(source, taskId);
    const permisions = await getCheckoutPermissions(source, taskId);

    expect(resultTask?.status).toEqual('InProgress');
    expect(resultTask?.typist_user_id).toEqual(typistUserId);
    expect(resultTask?.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 () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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(
      makeContext('trackingId', 'requestId'),
      1,
      ['typist'],
      'typist-user-external-id',
    );
    const resultTask = await getTask(source, taskId);
    const permisions = await getCheckoutPermissions(source, taskId);

    expect(resultTask?.status).toEqual('InProgress');
    expect(resultTask?.typist_user_id).toEqual(typistUserId);
    //タスクの元々のステータスがPending,Inprogressの場合、文字起こし開始時刻は更新されない
    expect(resultTask?.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('第五階層のアカウントの場合、有効なライセンスが割当されている場合チェックアウトできる', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    // 第五階層のアカウントを作成
    const { id: accountId } = await makeTestSimpleAccount(source, { tier: 5 });
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    // 本日の日付を作成
    const today = new Date();
    // 有効なライセンスを作成して紐づける
    await createLicense(
      source,
      1,
      today,
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      typistUserId,
      null,
      null,
      null,
    );
    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(
      makeContext('trackingId', 'requestId'),
      1,
      ['typist'],
      'typist-user-external-id',
    );
    const resultTask = await getTask(source, taskId);
    const permisions = await getCheckoutPermissions(source, taskId);

    expect(resultTask?.status).toEqual('InProgress');
    expect(resultTask?.typist_user_id).toEqual(typistUserId);
    //タスクの元々のステータスがPending,Inprogressの場合、文字起こし開始時刻は更新されない
    expect(resultTask?.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 () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Backup',
    );

    const service = module.get<TasksService>(TasksService);
    try {
      await service.checkout(
        makeContext('trackingId', 'requestId'),
        1,
        ['typist'],
        'typist-user-external-id',
      );
      fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010601'));
      } else {
        fail();
      }
    }
  });
  it('第五階層のアカウントの場合、ライセンスが未割当の場合チェックアウトできない', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    // 第五階層のアカウントを作成
    const { id: accountId } = await makeTestSimpleAccount(source, { tier: 5 });
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Backup',
    );

    const service = module.get<TasksService>(TasksService);
    try {
      await service.checkout(
        makeContext('trackingId', 'requestId'),
        1,
        ['typist'],
        'typist-user-external-id',
      );
      fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010812'));
      } else {
        fail();
      }
    }
  });
  it('第五階層のアカウントの場合、ライセンスが有効期限切れの場合チェックアウトできない', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    // 第五階層のアカウントを作成
    const { id: accountId } = await makeTestSimpleAccount(source, { tier: 5 });
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    // 昨日の日付を作成
    let yesterday = new Date();
    yesterday.setDate(yesterday.getDate() - 1);
    yesterday = new DateWithZeroTime(yesterday);
    // 期限切れのライセンスを作成して紐づける
    await createLicense(
      source,
      1,
      yesterday,
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      typistUserId,
      null,
      null,
      null,
    );

    await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Backup',
    );

    const service = module.get<TasksService>(TasksService);
    try {
      await service.checkout(
        makeContext('trackingId', 'requestId'),
        1,
        ['typist'],
        'typist-user-external-id',
      );
      fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010805'));
      } else {
        fail();
      }
    }
  });
  it('ユーザーのRoleがTypistで、チェックアウト権限が存在しない時、タスクをチェックアウトできない', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
    );

    const service = module.get<TasksService>(TasksService);
    try {
      await service.checkout(
        makeContext('trackingId', 'requestId'),
        1,
        ['typist'],
        'typist-user-external-id',
      );
      fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010602'));
      } else {
        fail();
      }
    }
  });

  it('ユーザーのRoleがTypistで、既にチェックアウト中のタスク（InProgress）がある時、タスクをチェックアウトできない', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });

    await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      TASK_STATUS.IN_PROGRESS,
      typistUserId,
    );
    const { taskId, audioFileId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000002',
      TASK_STATUS.UPLOADED,
    );

    await createCheckoutPermissions(source, taskId, typistUserId);

    const service = module.get<TasksService>(TasksService);

    try {
      await service.checkout(
        makeContext('trackingId', 'requestId'),
        audioFileId,
        ['typist'],
        'typist-user-external-id',
      );
      fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010601'));
      } else {
        fail();
      }
    }
  });

  it('ユーザーのRoleがTypistで、別ユーザーによってチェックアウト中のタスク（InProgress）がある時、タスクをチェックアウトできる', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id1',
      role: 'typist',
    });
    const { id: typistUserId2 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id2',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });

    await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      TASK_STATUS.IN_PROGRESS,
      typistUserId1,
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000002',
      TASK_STATUS.UPLOADED,
    );

    await createCheckoutPermissions(source, taskId, typistUserId2);

    const service = module.get<TasksService>(TasksService);

    await service.checkout(
      makeContext('trackingId', 'requestId'),
      2,
      ['typist'],
      'typist-user-external-id2',
    );

    const resultTask = await getTask(source, taskId);
    const permisions = await getCheckoutPermissions(source, taskId);

    expect(resultTask?.status).toEqual(TASK_STATUS.IN_PROGRESS);
    expect(resultTask?.typist_user_id).toEqual(typistUserId2);
    expect(permisions.length).toBe(1);
    expect(permisions[0].task_id).toBe(taskId);
    expect(permisions[0].user_id).toBe(typistUserId2);
  });

  it('ユーザーのRoleがAuthorで、アップロードした音声ファイルに紐づいたタスクをチェックアウトできる(Uploaded)', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
    );

    const service = module.get<TasksService>(TasksService);
    expect(
      await service.checkout(
        makeContext('trackingId', 'requestId'),
        1,
        ['author'],
        'author-user-external-id',
      ),
    ).toEqual(undefined);
  });

  it('ユーザーのRoleがAuthorで、アップロードした音声ファイルに紐づいたタスクをチェックアウトできる(Finished)', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
    );

    const service = module.get<TasksService>(TasksService);

    expect(
      await service.checkout(
        makeContext('trackingId', 'requestId'),
        1,
        ['author'],
        'author-user-external-id',
      ),
    ).toEqual(undefined);
  });

  it('ユーザーのRoleがAuthorで、アップロードした音声ファイルに紐づいたタスクが存在しない場合、タスクをチェックアウトできない', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });

    const service = module.get<TasksService>(TasksService);
    try {
      await service.checkout(
        makeContext('trackingId', 'requestId'),
        1,
        ['author'],
        'author-user-external-id',
      ),
        fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toBe(HttpStatus.NOT_FOUND);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010601'));
      } else {
        fail();
      }
    }
  });

  it('ユーザーのRoleがAuthorで、音声ファイルに紐づいたタスクでユーザーと一致するAuthorIDでない場合、タスクをチェックアウトできない', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    await createTask(
      source,
      accountId,
      authorUserId,
      'OTHOR_AUTHOR',
      '',
      '01',
      '00000001',
      'Uploaded',
    );

    const service = module.get<TasksService>(TasksService);
    try {
      await service.checkout(
        makeContext('trackingId', 'requestId'),
        1,
        ['author'],
        'author-user-external-id',
      );
      fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010602'));
      } else {
        fail();
      }
    }
  });

  it('ユーザーのRoleに[Typist,author]が設定されていない時、タスクをチェックアウトできない', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'none-user-external-id',
      role: 'none',
      author_id: 'MY_AUTHOR_ID',
    });

    const service = module.get<TasksService>(TasksService);
    try {
      await service.checkout(
        makeContext('trackingId', 'requestId'),
        1,
        ['none'],
        'none-user-external-id',
      );
      fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010602'));
      } else {
        fail();
      }
    }
  });
});

describe('checkin', () => {
  let source: DataSource | null = null;
  beforeAll(async () => {
    if (source == null) {
      source = await (async () => {
        const s = new DataSource({
          type: 'mysql',
          host: 'test_mysql_db',
          port: 3306,
          username: 'user',
          password: 'password',
          database: 'odms',
          entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
          synchronize: false, // trueにすると自動的にmigrationが行われるため注意
        });
        return await s.initialize();
      })();
    }
  });

  beforeEach(async () => {
    if (source) {
      await truncateAllTable(source);
    }
  });

  afterAll(async () => {
    await source?.destroy();
    source = null;
  });

  it('API実行者が文字起こし実行中のタスクである場合、タスクをチェックインできる', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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(
      makeContext('trackingId', 'requestId'),
      1,
      'typist-user-external-id',
    );
    const resultTask = await getTask(source, taskId);

    expect(resultTask?.status).toEqual('Finished');
    expect(resultTask?.finished_at).not.toEqual(initTask?.finished_at);
  });

  it('タスクのステータスがInprogressでない時、タスクをチェックインできない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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(
        makeContext('trackingId', 'requestId'),
        1,
        'typist-user-external-id',
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
    );
  });

  it('API実行者が文字起こし実行中のタスクでない場合、タスクをチェックインできない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: anotherTypistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'another-typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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(
        makeContext('trackingId', 'requestId'),
        1,
        'typist-user-external-id',
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
    );
  });

  it('タスクがない時、タスクをチェックインできない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });

    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });

    const service = module.get<TasksService>(TasksService);

    await expect(
      service.checkin(
        makeContext('trackingId', 'requestId'),
        1,
        'typist-user-external-id',
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010603'), HttpStatus.NOT_FOUND),
    );
  });
});

describe('suspend', () => {
  let source: DataSource | null = null;
  beforeAll(async () => {
    if (source == null) {
      source = await (async () => {
        const s = new DataSource({
          type: 'mysql',
          host: 'test_mysql_db',
          port: 3306,
          username: 'user',
          password: 'password',
          database: 'odms',
          entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
          synchronize: false, // trueにすると自動的にmigrationが行われるため注意
        });
        return await s.initialize();
      })();
    }
  });

  beforeEach(async () => {
    if (source) {
      await truncateAllTable(source);
    }
  });

  afterAll(async () => {
    await source?.destroy();
    source = null;
  });

  it('API実行者が文字起こし実行中のタスクである場合、タスクを中断できる', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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(
      makeContext('trackingId', 'requestId'),
      1,
      'typist-user-external-id',
    );
    const resultTask = await getTask(source, taskId);

    expect(resultTask?.status).toEqual('Pending');
  });

  it('タスクのステータスがInprogressでない時、タスクを中断できない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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(
        makeContext('trackingId', 'requestId'),
        1,
        'typist-user-external-id',
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
    );
  });

  it('API実行者が文字起こし実行中のタスクでない場合、タスクを中断できない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: anotherTypistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'another-typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'InProgress',
      anotherTypistUserId,
    );
    await createCheckoutPermissions(source, taskId, anotherTypistUserId);

    const service = module.get<TasksService>(TasksService);

    await expect(
      service.checkin(
        makeContext('trackingId', 'requestId'),
        1,
        'typist-user-external-id',
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
    );
  });

  it('タスクがない時、タスクを中断できない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });

    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });

    const service = module.get<TasksService>(TasksService);

    await expect(
      service.checkin(
        makeContext('trackingId', 'requestId'),
        1,
        'typist-user-external-id',
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010603'), HttpStatus.NOT_FOUND),
    );
  });
});

describe('cancel', () => {
  let source: DataSource | null = null;
  beforeAll(async () => {
    if (source == null) {
      source = await (async () => {
        const s = new DataSource({
          type: 'mysql',
          host: 'test_mysql_db',
          port: 3306,
          username: 'user',
          password: 'password',
          database: 'odms',
          entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
          synchronize: false, // trueにすると自動的にmigrationが行われるため注意
        });
        return await s.initialize();
      })();
    }
  });

  beforeEach(async () => {
    if (source) {
      await truncateAllTable(source);
    }
  });

  afterAll(async () => {
    await source?.destroy();
    source = null;
  });

  it('API実行者のRoleがTypistの場合、自身が文字起こし実行中のタスクをキャンセルできる', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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);

    await service.cancel(
      makeContext('trackingId', 'requestId'),
      1,
      'typist-user-external-id',
      ['typist', 'standard'],
    );
    const resultTask = await getTask(source, taskId);
    const permisions = await getCheckoutPermissions(source, taskId);

    expect(resultTask?.status).toEqual('Uploaded');
    expect(resultTask?.typist_user_id).toEqual(null);
    expect(permisions.length).toEqual(0);
  });

  it('API実行者のRoleがTypistの場合、自身が文字起こし中断しているタスクをキャンセルできる', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Pending',
      typistUserId,
    );
    await createCheckoutPermissions(source, taskId, typistUserId);
    const service = module.get<TasksService>(TasksService);

    await service.cancel(
      makeContext('trackingId', 'requestId'),
      1,
      'typist-user-external-id',
      ['typist', 'standard'],
    );

    const resultTask = await getTask(source, taskId);
    const permisions = await getCheckoutPermissions(source, taskId);

    expect(resultTask?.status).toEqual('Uploaded');
    expect(resultTask?.typist_user_id).toEqual(null);
    expect(permisions.length).toEqual(0);
  });

  it('API実行者のRoleがAdminの場合、文字起こし実行中のタスクをキャンセルできる', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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);

    await service.cancel(
      makeContext('trackingId', 'requestId'),
      1,
      'typist-user-external-id',
      ['admin', 'author'],
    );
    const resultTask = await getTask(source, taskId);
    const permisions = await getCheckoutPermissions(source, taskId);

    expect(resultTask?.status).toEqual('Uploaded');
    expect(resultTask?.typist_user_id).toEqual(null);
    expect(permisions.length).toEqual(0);
  });

  it('API実行者のRoleがAdminの場合、文字起こし中断しているタスクをキャンセルできる', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Pending',
      typistUserId,
    );

    await createCheckoutPermissions(source, taskId, typistUserId);

    const service = module.get<TasksService>(TasksService);

    await service.cancel(
      makeContext('trackingId', 'requestId'),
      1,
      'typist-user-external-id',
      ['admin', 'author'],
    );
    const resultTask = await getTask(source, taskId);
    const permisions = await getCheckoutPermissions(source, taskId);

    expect(resultTask?.status).toEqual('Uploaded');
    expect(resultTask?.typist_user_id).toEqual(null);
    expect(permisions.length).toEqual(0);
  });

  it('タスクのステータスが[Inprogress,Pending]でない時、タスクをキャンセルできない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: '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.cancel(
        makeContext('trackingId', 'requestId'),
        1,
        'typist-user-external-id',
        ['admin', 'author'],
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
    );
  });

  it('API実行者のRoleがTypistの場合、他人が文字起こし実行中のタスクをキャンセルできない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: anotherTypistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'another-typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'InProgress',
      anotherTypistUserId,
    );
    await createCheckoutPermissions(source, taskId, anotherTypistUserId);

    const service = module.get<TasksService>(TasksService);

    await expect(
      service.cancel(
        makeContext('trackingId', 'requestId'),
        1,
        'typist-user-external-id',
        ['typist', 'standard'],
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
    );
  });

  it('タスクがない時、タスクをキャンセルできない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });

    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });

    const service = module.get<TasksService>(TasksService);

    await expect(
      service.cancel(
        makeContext('trackingId', 'requestId'),
        1,
        'typist-user-external-id',
        ['typist', 'standard'],
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010603'), HttpStatus.NOT_FOUND),
    );
  });

  it('API実行者のRoleがTypistの場合、自身が文字起こし実行中のタスクをキャンセルし、そのタスクの自動ルーティングを行う', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    const { id: authorUserId, author_id } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'AUTHOR_ID',
    });
    //ワークタイプIDを作成
    await createWorktype(source, accountId, '01');
    // テンプレートファイルを作成
    const { id: templateFileId } = await createTemplateFile(
      source,
      accountId,
      'template-file-name',
      'https://example.com',
    );
    // ワークフローを作成
    const { id: workflowId } = await createWorkflow(
      source,
      accountId,
      authorUserId,
      undefined,
      templateFileId,
    );
    // ワークフロータイピストを作成
    await createWorkflowTypist(source, workflowId, typistUserId);

    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      author_id ?? '',
      '',
      '01',
      '00000001',
      'InProgress',
      typistUserId,
    );
    await createCheckoutPermissions(source, taskId, typistUserId);

    const service = module.get<TasksService>(TasksService);
    const NotificationHubService = module.get<NotificationhubService>(
      NotificationhubService,
    );
    await service.cancel(
      makeContext('trackingId', 'requestId'),
      1,
      'typist-user-external-id',
      ['typist', 'standard'],
    );
    const resultTask = await getTask(source, taskId);
    const permisions = await getCheckoutPermissions(source, taskId);

    expect(resultTask?.status).toEqual('Uploaded');
    expect(resultTask?.typist_user_id).toEqual(null);
    // タスクのテンプレートファイルIDを確認
    expect(resultTask?.template_file_id).toEqual(templateFileId);
    // タスクのチェックアウト権限が想定通り（ワークフローで設定されている）のユーザーIDで作成されているか確認
    expect(permisions.length).toEqual(1);
    expect(permisions[0].user_id).toEqual(typistUserId);
    // 通知処理が想定通りの引数で呼ばれているか確認
    expect(NotificationHubService.notify).toHaveBeenCalledWith(
      makeContext('trackingId', 'requestId'),
      [`user_${typistUserId}`],
      {
        authorId: 'AUTHOR_ID',
        filename: 'x',
        priority: 'High',
        uploadedAt: resultTask?.file?.uploaded_at.toISOString(),
      },
    );
  });

  it('API実行者のRoleがAdminの場合、自身が文字起こし実行中のタスクをキャンセルし、そのタスクの自動ルーティングを行う（API実行者のAuthorIDと音声ファイルに紐づくWorkType）', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    // タスクの文字起こし担当者
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    // 自動ルーティングされるタイピストユーザーを作成
    const { id: autoRoutingTypistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'auto-routing-typist-user-external-id',
      role: 'typist',
    });
    // API実行者
    const {
      id: myAuthorUserId,
      external_id,
      role,
    } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'my-author-user-external-id',
      role: 'author admin',
      author_id: 'MY_AUTHOR_ID',
    });
    // 音声ファイルのアップロード者
    const { id: authorUserId, author_id } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'AUTHOR_ID',
    });
    //ワークタイプIDを作成
    const { id: workTypeId, custom_worktype_id } = await createWorktype(
      source,
      accountId,
      '01',
    );
    // テンプレートファイルを作成
    const { id: templateFileId } = await createTemplateFile(
      source,
      accountId,
      'template-file-name',
      'https://example.com',
    );
    // ワークフローを作成
    const { id: workflowId } = await createWorkflow(
      source,
      accountId,
      myAuthorUserId,
      workTypeId,
      templateFileId,
    );
    // ワークフロータイピストを作成
    await createWorkflowTypist(source, workflowId, autoRoutingTypistUserId);

    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      author_id ?? '',
      custom_worktype_id,
      '01',
      '00000001',
      'InProgress',
      typistUserId,
    );
    await createCheckoutPermissions(source, taskId, typistUserId);

    const service = module.get<TasksService>(TasksService);
    const NotificationHubService = module.get<NotificationhubService>(
      NotificationhubService,
    );
    await service.cancel(
      makeContext('trackingId', 'requestId'),
      1,
      external_id,
      role.split(' ') as Roles[],
    );
    const resultTask = await getTask(source, taskId);
    const permisions = await getCheckoutPermissions(source, taskId);

    expect(resultTask?.status).toEqual('Uploaded');
    expect(resultTask?.typist_user_id).toEqual(null);
    // タスクのテンプレートファイルIDを確認
    expect(resultTask?.template_file_id).toEqual(templateFileId);
    // タスクのチェックアウト権限が想定通り（ワークフローで設定されている）のユーザーIDで作成されているか確認
    expect(permisions.length).toEqual(1);
    expect(permisions[0].user_id).toEqual(autoRoutingTypistUserId);
    // 通知処理が想定通りの引数で呼ばれているか確認
    expect(NotificationHubService.notify).toHaveBeenCalledWith(
      makeContext('trackingId', 'requestId'),
      [`user_${autoRoutingTypistUserId}`],
      {
        authorId: 'AUTHOR_ID',
        filename: 'x',
        priority: 'High',
        uploadedAt: resultTask?.file?.uploaded_at.toISOString(),
      },
    );
  });
  it('API実行者のRoleがTypistの場合、自身が文字起こし実行中のタスクをキャンセルするが、一致するワークフローがない場合は自動ルーティングを行うことができない', async () => {
    if (!source) fail();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModuleWithNotificaiton(
      source,
      notificationhubServiceMockValue,
    );
    if (!module) fail();
    const { id: accountId } = await makeTestSimpleAccount(source);
    // タスクの文字起こし担当者
    const {
      id: typistUserId,
      external_id,
      role,
    } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });
    // 音声ファイルのアップロード者
    const { id: authorUserId, author_id } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'AUTHOR_ID',
    });
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      author_id ?? '',
      'custom_worktype_id',
      '01',
      '00000001',
      'InProgress',
      typistUserId,
    );
    await createCheckoutPermissions(source, taskId, typistUserId);

    const service = module.get<TasksService>(TasksService);
    const NotificationHubService = module.get<NotificationhubService>(
      NotificationhubService,
    );
    await service.cancel(
      makeContext('trackingId', 'requestId'),
      1,
      external_id,
      role.split(' ') as Roles[],
    );
    const resultTask = await getTask(source, taskId);
    const permisions = await getCheckoutPermissions(source, taskId);

    expect(resultTask?.status).toEqual('Uploaded');
    expect(resultTask?.typist_user_id).toEqual(null);
    // タスクのチェックアウト権限が削除されていることを確認
    expect(permisions.length).toEqual(0);
    // 通知処理が想定通りの引数で呼ばれていないか確認
    expect(NotificationHubService.notify).not.toHaveBeenCalled();
  });
});

describe('backup', () => {
  let source: DataSource | null = null;
  beforeAll(async () => {
    if (source == null) {
      source = await (async () => {
        const s = new DataSource({
          type: 'mysql',
          host: 'test_mysql_db',
          port: 3306,
          username: 'user',
          password: 'password',
          database: 'odms',
          entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
          synchronize: false, // trueにすると自動的にmigrationが行われるため注意
        });
        return await s.initialize();
      })();
    }
  });

  beforeEach(async () => {
    if (source) {
      await truncateAllTable(source);
    }
  });

  afterAll(async () => {
    await source?.destroy();
    source = null;
  });

  it('文字起こし完了のタスクをバックアップできる', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { account, admin } = await makeTestAccount(source);
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: account.id,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: account.id,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });

    const { taskId, audioFileId } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      TASK_STATUS.FINISHED,
      typistUserId,
    );

    // 作成したデータを確認
    {
      const task = await getTask(source, taskId);
      expect(task?.status).toBe(TASK_STATUS.FINISHED);
      expect(task?.is_job_number_enabled).toBe(true);
    }

    const service = module.get<TasksService>(TasksService);
    await service.backup(
      makeContext(admin.external_id, 'requestId'),
      audioFileId,
      admin.external_id,
    );

    // バックアップしたデータを確認
    {
      const task = await getTask(source, taskId);
      expect(task?.status).toBe(TASK_STATUS.BACKUP);
      expect(task?.is_job_number_enabled).toBe(false);
    }
  });

  it('文字起こしバックアップ済みのタスクをバックアップできる', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { account, admin } = await makeTestAccount(source);
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: account.id,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: account.id,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });

    const { taskId, audioFileId } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      TASK_STATUS.BACKUP,
      typistUserId,
      false,
    );

    // 作成したデータを確認
    {
      const task = await getTask(source, taskId);
      expect(task?.status).toBe(TASK_STATUS.BACKUP);
      expect(task?.is_job_number_enabled).toBe(false);
    }

    const service = module.get<TasksService>(TasksService);
    await service.backup(
      makeContext(admin.external_id, 'requestId'),
      audioFileId,
      admin.external_id,
    );

    // バックアップしたデータを確認
    {
      const task = await getTask(source, taskId);
      expect(task?.status).toBe(TASK_STATUS.BACKUP);
      expect(task?.is_job_number_enabled).toBe(false);
    }
  });

  it('タスクのステータスが[Finished,Backup]でない時、エラーを返却する', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { account, admin } = await makeTestAccount(source);
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: account.id,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: account.id,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });

    const { taskId, audioFileId } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      TASK_STATUS.IN_PROGRESS,
      typistUserId,
    );

    // 作成したデータを確認
    {
      const task = await getTask(source, taskId);
      expect(task?.status).toBe(TASK_STATUS.IN_PROGRESS);
      expect(task?.is_job_number_enabled).toBe(true);
    }

    const service = module.get<TasksService>(TasksService);

    try {
      await service.backup(
        makeContext(admin.external_id, 'requestId'),
        audioFileId,
        admin.external_id,
      );
      fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010601'));
      } else {
        fail();
      }
    }
  });

  it('指定タスクが存在しない時、エラーを返却する', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { account, admin } = await makeTestAccount(source);
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: account.id,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: account.id,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });

    const { taskId } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      TASK_STATUS.FINISHED,
      typistUserId,
    );

    // 作成したデータを確認
    {
      const task = await getTask(source, taskId);
      expect(task?.status).toBe(TASK_STATUS.FINISHED);
      expect(task?.is_job_number_enabled).toBe(true);
    }

    const service = module.get<TasksService>(TasksService);

    try {
      await service.backup(
        makeContext(admin.external_id, 'requestId'),
        9999, // 存在しないタスクID
        admin.external_id,
      );
      fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.NOT_FOUND);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010603'));
      } else {
        fail();
      }
    }
  });

  it('DBアクセスに失敗した場合、エラーを返却する', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { account, admin } = await makeTestAccount(source);
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: account.id,
      external_id: 'author-user-external-id',
      role: 'author',
      author_id: 'MY_AUTHOR_ID',
    });
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: account.id,
      external_id: 'typist-user-external-id',
      role: 'typist',
    });

    const { taskId, audioFileId } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      TASK_STATUS.FINISHED,
      typistUserId,
    );

    // 作成したデータを確認
    {
      const task = await getTask(source, taskId);
      expect(task?.status).toBe(TASK_STATUS.FINISHED);
      expect(task?.is_job_number_enabled).toBe(true);
    }

    const service = module.get<TasksService>(TasksService);

    //DBアクセスに失敗するようにする
    const tasksService = module.get<TasksRepositoryService>(
      TasksRepositoryService,
    );
    tasksService.backup = jest.fn().mockRejectedValue('DB failed');

    try {
      await service.backup(
        makeContext(admin.external_id, 'requestId'),
        audioFileId,
        admin.external_id,
      );
      fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
        expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
      } else {
        fail();
      }
    }
  });
});

describe('getNextTask', () => {
  let source: DataSource | null = null;
  beforeAll(async () => {
    if (source == null) {
      source = await (async () => {
        const s = new DataSource({
          type: 'mysql',
          host: 'test_mysql_db',
          port: 3306,
          username: 'user',
          password: 'password',
          database: 'odms',
          entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
          synchronize: false, // trueにすると自動的にmigrationが行われるため注意
        });
        return await s.initialize();
      })();
    }
  });

  beforeEach(async () => {
    if (source) {
      await truncateAllTable(source);
    }
  });

  afterAll(async () => {
    await source?.destroy();
    source = null;
  });

  it('次タスクを取得できる（JobNumber順）', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    // 第五階層のアカウント作成
    const { account, admin } = await makeTestAccount(source, { tier: 5 });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: account.id,
      author_id: 'AUTHOR_ID',
      external_id: 'author-user-external-id',
      role: USER_ROLES.AUTHOR,
    });
    const { id: typistUserId, external_id: typistExternalId } =
      await makeTestUser(source, {
        account_id: account.id,
        external_id: 'typist-user-external-id',
        role: USER_ROLES.TYPIST,
      });

    await createSortCriteria(source, typistUserId, 'JOB_NUMBER', 'ASC');

    const { taskId: taskId1 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId1, typistUserId);

    const { taskId: taskId3, audioFileId: audioFileId3 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000003',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId3, typistUserId);

    const { taskId: taskId2, audioFileId: audioFileId2 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000002',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId2, typistUserId);

    const service = module.get<TasksService>(TasksService);
    const context = makeContext(admin.external_id, 'requestId');

    const nextAudioFileId = await service.getNextTask(
      context,
      typistExternalId,
      audioFileId2,
    );

    // 実行結果が正しいか確認
    {
      expect(nextAudioFileId).toEqual(audioFileId3);
    }
  });

  it('次タスクを取得できる（JobNumber順＋優先度）', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    // 第五階層のアカウント作成
    const { account, admin } = await makeTestAccount(source, { tier: 5 });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: account.id,
      author_id: 'AUTHOR_ID',
      external_id: 'author-user-external-id',
      role: USER_ROLES.AUTHOR,
    });
    const { id: typistUserId, external_id: typistExternalId } =
      await makeTestUser(source, {
        account_id: account.id,
        external_id: 'typist-user-external-id',
        role: USER_ROLES.TYPIST,
      });

    await createSortCriteria(source, typistUserId, 'JOB_NUMBER', 'ASC');

    const { taskId: taskId1, audioFileId: audioFileId1 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '00',
      '00000001',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId1, typistUserId);

    const { taskId: taskId3, audioFileId: audioFileId3 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '00',
      '00000003',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId3, typistUserId);

    const { taskId: taskId2 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000002',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId2, typistUserId);

    const service = module.get<TasksService>(TasksService);
    const context = makeContext(admin.external_id, 'requestId');

    const nextAudioFileId = await service.getNextTask(
      context,
      typistExternalId,
      audioFileId1,
    );

    // 実行結果が正しいか確認
    {
      expect(nextAudioFileId).toEqual(audioFileId3);
    }
  });

  it('次タスクを取得できる（JobNumber順、先頭）', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    // 第五階層のアカウント作成
    const { account, admin } = await makeTestAccount(source, { tier: 5 });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: account.id,
      author_id: 'AUTHOR_ID',
      external_id: 'author-user-external-id',
      role: USER_ROLES.AUTHOR,
    });
    const { id: typistUserId, external_id: typistExternalId } =
      await makeTestUser(source, {
        account_id: account.id,
        external_id: 'typist-user-external-id',
        role: USER_ROLES.TYPIST,
      });

    await createSortCriteria(source, typistUserId, 'JOB_NUMBER', 'ASC');

    const { taskId: taskId1, audioFileId: audioFileId1 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId1, typistUserId);

    const { taskId: taskId3, audioFileId: audioFileId3 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000003',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId3, typistUserId);

    const { taskId: taskId2 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000002',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId2, typistUserId);

    const service = module.get<TasksService>(TasksService);
    const context = makeContext(admin.external_id, 'requestId');

    const nextAudioFileId = await service.getNextTask(
      context,
      typistExternalId,
      audioFileId3,
    );

    // 実行結果が正しいか確認
    {
      expect(nextAudioFileId).toEqual(audioFileId1);
    }
  });

  it('次タスクを取得できる（Worktype順）', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    // 第五階層のアカウント作成
    const { account, admin } = await makeTestAccount(source, { tier: 5 });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: account.id,
      author_id: 'AUTHOR_ID',
      external_id: 'author-user-external-id',
      role: USER_ROLES.AUTHOR,
    });
    const { id: typistUserId, external_id: typistExternalId } =
      await makeTestUser(source, {
        account_id: account.id,
        external_id: 'typist-user-external-id',
        role: USER_ROLES.TYPIST,
      });

    await createSortCriteria(source, typistUserId, 'WORK_TYPE', 'ASC');

    const { taskId: taskId1 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      'worktype1',
      '01',
      '00000001',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId1, typistUserId);

    const { taskId: taskId3, audioFileId: audioFileId3 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      'worktype2',
      '01',
      '00000003',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId3, typistUserId);

    const { taskId: taskId2, audioFileId: audioFileId2 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      'worktype3',
      '01',
      '00000002',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId2, typistUserId);

    const service = module.get<TasksService>(TasksService);
    const context = makeContext(admin.external_id, 'requestId');

    const nextAudioFileId = await service.getNextTask(
      context,
      typistExternalId,
      audioFileId3,
    );

    // 実行結果が正しいか確認
    {
      expect(nextAudioFileId).toEqual(audioFileId2);
    }
  });

  it('次タスクを取得できる（Status順）', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    // 第五階層のアカウント作成
    const { account, admin } = await makeTestAccount(source, { tier: 5 });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: account.id,
      author_id: 'AUTHOR_ID',
      external_id: 'author-user-external-id',
      role: USER_ROLES.AUTHOR,
    });
    const { id: typistUserId, external_id: typistExternalId } =
      await makeTestUser(source, {
        account_id: account.id,
        external_id: 'typist-user-external-id',
        role: USER_ROLES.TYPIST,
      });

    await createSortCriteria(source, typistUserId, 'STATUS', 'ASC');

    const { taskId: taskId1, audioFileId: audioFileId1 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      'worktype1',
      '01',
      '00000001',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId1, typistUserId);

    const { taskId: taskId3 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      'worktype2',
      '01',
      '00000003',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId3, typistUserId);

    const { taskId: taskId2, audioFileId: audioFileId2 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      'worktype3',
      '01',
      '00000002',
      TASK_STATUS.PENDING,
    );
    await createCheckoutPermissions(source, taskId2, typistUserId);

    const service = module.get<TasksService>(TasksService);
    const context = makeContext(admin.external_id, 'requestId');

    const nextAudioFileId = await service.getNextTask(
      context,
      typistExternalId,
      audioFileId2,
    );

    // 実行結果が正しいか確認
    {
      expect(nextAudioFileId).toEqual(audioFileId1);
    }
  });

  it('次タスクが存在しない場合undefinedを返す（JobNumber順）', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    // 第五階層のアカウント作成
    const { account, admin } = await makeTestAccount(source, { tier: 5 });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: account.id,
      author_id: 'AUTHOR_ID',
      external_id: 'author-user-external-id',
      role: USER_ROLES.AUTHOR,
    });
    const { id: typistUserId, external_id: typistExternalId } =
      await makeTestUser(source, {
        account_id: account.id,
        external_id: 'typist-user-external-id',
        role: USER_ROLES.TYPIST,
      });

    await createSortCriteria(source, typistUserId, 'JOB_NUMBER', 'ASC');

    const { taskId: taskId1, audioFileId: audioFileId1 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      'worktype1',
      '01',
      '00000001',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId1, typistUserId);

    const service = module.get<TasksService>(TasksService);
    const context = makeContext(admin.external_id, 'requestId');

    const nextAudioFileId = await service.getNextTask(
      context,
      typistExternalId,
      audioFileId1,
    );

    // 実行結果が正しいか確認
    {
      expect(nextAudioFileId).toEqual(undefined);
    }
  });
  it('指定タスクが存在しない場合エラーを返す（JobNumber順）', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    // 第五階層のアカウント作成
    const { account, admin } = await makeTestAccount(source, { tier: 5 });
    const { id: authorUserId } = await makeTestUser(source, {
      account_id: account.id,
      author_id: 'AUTHOR_ID',
      external_id: 'author-user-external-id',
      role: USER_ROLES.AUTHOR,
    });
    const { id: typistUserId, external_id: typistExternalId } =
      await makeTestUser(source, {
        account_id: account.id,
        external_id: 'typist-user-external-id',
        role: USER_ROLES.TYPIST,
      });

    await createSortCriteria(source, typistUserId, 'WORK_TYPE', 'ASC');

    const { taskId: taskId1, audioFileId: audioFileId1 } = await createTask(
      source,
      account.id,
      authorUserId,
      'MY_AUTHOR_ID',
      'worktype1',
      '01',
      '00000001',
      TASK_STATUS.UPLOADED,
    );
    await createCheckoutPermissions(source, taskId1, typistUserId);

    const service = module.get<TasksService>(TasksService);
    const context = makeContext(admin.external_id, 'requestId');

    // 実行結果が正しいか確認
    try {
      await service.getNextTask(context, typistExternalId, audioFileId1 + 1);
      fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010603'));
      } else {
        fail();
      }
    }
  });
});
