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 {
  createAccount,
  createCheckoutPermissions,
  createTask,
  createUser,
  createUserGroup,
  getCheckoutPermissions,
  getTask,
  makeTaskTestingModule,
} from './test/utility';
import { Adb2cTooManyRequestsError } from '../../gateways/adb2c/adb2c.service';
import { makeContext } from '../../common/log';

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

    const accessToken = { userId: 'userId', role: 'admin', tier: 5 };
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    expect(
      await service.tasksService.getTasks(
        makeContext('trackingId'),
        accessToken,
        offset,
        limit,
        status,
        paramName,
        direction,
      ),
    ).toEqual({
      tasks: [
        {
          assignees: [{ typistName: 'XXXX XXX', typistUserId: 1 }],
          audioCreatedDate: '2023-01-01T01:01:01.000Z',
          audioDuration: '123000',
          audioFileId: 1,
          audioFinishedDate: '2023-01-01T01:01:01.000Z',
          audioFormat: 'DS',
          audioUploadedDate: '2023-01-01T01:01:01.000Z',
          authorId: 'AUTHOR',
          comment: 'comment',
          fileName: 'test.zip',
          fileSize: 123000,
          isEncrypted: true,
          jobNumber: '00000001',
          optionItemList: [
            { optionItemLabel: 'label01', optionItemValue: 'value01' },
            { optionItemLabel: 'label02', optionItemValue: 'value02' },
            { optionItemLabel: 'label03', optionItemValue: 'value03' },
            { optionItemLabel: 'label04', optionItemValue: 'value04' },
            { optionItemLabel: 'label05', optionItemValue: 'value05' },
            { optionItemLabel: 'label06', optionItemValue: 'value06' },
            { optionItemLabel: 'label07', optionItemValue: 'value07' },
            { optionItemLabel: 'label08', optionItemValue: 'value08' },
            { optionItemLabel: 'label09', optionItemValue: 'value09' },
            { optionItemLabel: 'label10', optionItemValue: 'value10' },
          ],
          priority: '00',
          status: 'Uploaded',
          transcriptionFinishedDate: undefined,
          transcriptionStartedDate: undefined,
          typist: undefined,
          url: 'test/test.zip',
          workType: 'WorkType',
        },
      ],
      total: 1,
    });
  });

  it('アカウント情報の取得に失敗した場合、エラーを返却する', async () => {
    const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();

    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    usersRepositoryMockValue.findUserByExternalId = new Error('DB failed');
    const service = await makeTasksServiceMock(
      tasksRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cServiceMockValue,
      notificationhubServiceMockValue,
    );

    const accessToken = { userId: 'userId', role: 'admin', tier: 5 };
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    await expect(
      service.tasksService.getTasks(
        makeContext('trackingId'),
        accessToken,
        offset,
        limit,
        status,
        paramName,
        direction,
      ),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });

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

    const accessToken = { userId: 'userId', role: 'admin', tier: 5 };
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    await expect(
      service.tasksService.getTasks(
        makeContext('trackingId'),
        accessToken,
        offset,
        limit,
        status,
        paramName,
        direction,
      ),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });

  it('取得したタスク一覧が不正な形式の場合、エラーを返却する', async () => {
    const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
    tasksRepositoryMockValue.getTasksFromAccountId = {
      tasks: [
        {
          id: 1,
          job_number: '00000001',
          account_id: 1,
          is_job_number_enabled: true,
          audio_file_id: 1,
          status: 'Uploaded',
          priority: '00',
          created_at: new Date('2023-01-01T01:01:01.000'),
          option_items: undefined, // 存在しない場合でも空配列であるはずのものがundefined
          file: {
            id: 1,
            account_id: 1,
            owner_user_id: 1,
            url: 'test/test.zip',
            file_name: 'test.zip',
            author_id: 'AUTHOR',
            work_type_id: 'WorkType',
            started_at: new Date('2023-01-01T01:01:01.000'),
            duration: '123000',
            finished_at: new Date('2023-01-01T01:01:01.000'),
            uploaded_at: new Date('2023-01-01T01:01:01.000'),
            file_size: 123000,
            priority: '00',
            audio_format: 'DS',
            comment: 'comment',
            is_encrypted: true,
          },
        },
      ],
      permissions: [],
      count: 1,
    };
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const service = await makeTasksServiceMock(
      tasksRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cServiceMockValue,
      notificationhubServiceMockValue,
    );
    const accessToken = { userId: 'userId', role: 'admin', tier: 5 };
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    await expect(
      service.tasksService.getTasks(
        makeContext('trackingId'),
        accessToken,
        offset,
        limit,
        status,
        paramName,
        direction,
      ),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });

  it('タスク一覧を取得できる（author）', async () => {
    const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    if (usersRepositoryMockValue.findUserByExternalId instanceof Error) {
      return;
    }
    usersRepositoryMockValue.findUserByExternalId.role = 'author';
    const service = await makeTasksServiceMock(
      tasksRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cServiceMockValue,
      notificationhubServiceMockValue,
    );

    const accessToken = { userId: 'userId', role: 'author', tier: 5 };
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    const result = await service.tasksService.getTasks(
      makeContext('trackingId'),
      accessToken,
      offset,
      limit,
      status,
      paramName,
      direction,
    );
    expect(result).toEqual({
      tasks: [
        {
          assignees: [{ typistName: 'XXXX XXX', typistUserId: 1 }],
          audioCreatedDate: '2023-01-01T01:01:01.000Z',
          audioDuration: '123000',
          audioFileId: 1,
          audioFinishedDate: '2023-01-01T01:01:01.000Z',
          audioFormat: 'DS',
          audioUploadedDate: '2023-01-01T01:01:01.000Z',
          authorId: 'AUTHOR',
          comment: 'comment',
          fileName: 'test.zip',
          fileSize: 123000,
          isEncrypted: true,
          jobNumber: '00000001',
          optionItemList: [
            { optionItemLabel: 'label01', optionItemValue: 'value01' },
            { optionItemLabel: 'label02', optionItemValue: 'value02' },
            { optionItemLabel: 'label03', optionItemValue: 'value03' },
            { optionItemLabel: 'label04', optionItemValue: 'value04' },
            { optionItemLabel: 'label05', optionItemValue: 'value05' },
            { optionItemLabel: 'label06', optionItemValue: 'value06' },
            { optionItemLabel: 'label07', optionItemValue: 'value07' },
            { optionItemLabel: 'label08', optionItemValue: 'value08' },
            { optionItemLabel: 'label09', optionItemValue: 'value09' },
            { optionItemLabel: 'label10', optionItemValue: 'value10' },
          ],
          priority: '00',
          status: 'Uploaded',
          transcriptionFinishedDate: undefined,
          transcriptionStartedDate: undefined,
          typist: undefined,
          url: 'test/test.zip',
          workType: 'WorkType',
        },
      ],
      total: 1,
    });
    expect(
      service.taskRepoService.getTasksFromAuthorIdAndAccountId,
    ).toHaveBeenCalledWith('abcdef', 1, 0, 20, 'JOB_NUMBER', 'ASC', [
      'Uploaded',
      'Backup',
    ]);
  });

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

    const accessToken = { userId: 'userId', role: 'author', tier: 5 };
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    await expect(
      service.tasksService.getTasks(
        makeContext('trackingId'),
        accessToken,
        offset,
        limit,
        status,
        paramName,
        direction,
      ),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });

  it('タスク一覧を取得できる（typist）', async () => {
    const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    if (usersRepositoryMockValue.findUserByExternalId instanceof Error) {
      return;
    }
    usersRepositoryMockValue.findUserByExternalId.role = 'typist';

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

    const accessToken = { userId: 'userId', role: 'typist', tier: 5 };
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    const result = await service.tasksService.getTasks(
      makeContext('trackingId'),
      accessToken,
      offset,
      limit,
      status,
      paramName,
      direction,
    );
    expect(result).toEqual({
      tasks: [
        {
          assignees: [{ typistName: 'XXXX XXX', typistUserId: 1 }],
          audioCreatedDate: '2023-01-01T01:01:01.000Z',
          audioDuration: '123000',
          audioFileId: 1,
          audioFinishedDate: '2023-01-01T01:01:01.000Z',
          audioFormat: 'DS',
          audioUploadedDate: '2023-01-01T01:01:01.000Z',
          authorId: 'AUTHOR',
          comment: 'comment',
          fileName: 'test.zip',
          fileSize: 123000,
          isEncrypted: true,
          jobNumber: '00000001',
          optionItemList: [
            { optionItemLabel: 'label01', optionItemValue: 'value01' },
            { optionItemLabel: 'label02', optionItemValue: 'value02' },
            { optionItemLabel: 'label03', optionItemValue: 'value03' },
            { optionItemLabel: 'label04', optionItemValue: 'value04' },
            { optionItemLabel: 'label05', optionItemValue: 'value05' },
            { optionItemLabel: 'label06', optionItemValue: 'value06' },
            { optionItemLabel: 'label07', optionItemValue: 'value07' },
            { optionItemLabel: 'label08', optionItemValue: 'value08' },
            { optionItemLabel: 'label09', optionItemValue: 'value09' },
            { optionItemLabel: 'label10', optionItemValue: 'value10' },
          ],
          priority: '00',
          status: 'Uploaded',
          transcriptionFinishedDate: undefined,
          transcriptionStartedDate: undefined,
          typist: undefined,
          url: 'test/test.zip',
          workType: 'WorkType',
        },
      ],
      total: 1,
    });
    expect(
      service.taskRepoService.getTasksFromTypistRelations,
    ).toHaveBeenCalledWith('userId', 0, 20, 'JOB_NUMBER', 'ASC', [
      'Uploaded',
      'Backup',
    ]);
  });

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

    const accessToken = { userId: 'userId', role: 'typist', tier: 5 };
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    await expect(
      service.tasksService.getTasks(
        makeContext('trackingId'),
        accessToken,
        offset,
        limit,
        status,
        paramName,
        direction,
      ),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });

  it('想定外のRoleの場合、エラーを返却する', async () => {
    const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const service = await makeTasksServiceMock(
      tasksRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cServiceMockValue,
      notificationhubServiceMockValue,
    );

    const accessToken = { userId: 'userId', role: 'XXX', tier: 5 };
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded', 'Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    await expect(
      service.tasksService.getTasks(
        makeContext('trackingId'),
        accessToken,
        offset,
        limit,
        status,
        paramName,
        direction,
      ),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });

  it('AdB2Cのリクエスト上限超過時、専用のエラーを返却する', async () => {
    const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    adb2cServiceMockValue.getUsers = new Adb2cTooManyRequestsError();
    const service = await makeTasksServiceMock(
      tasksRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cServiceMockValue,
      notificationhubServiceMockValue,
    );

    const accessToken = { userId: 'userId', role: 'admin', tier: 5 };
    const offset = 0;
    const limit = 20;
    const status = ['Uploaded,Backup'];
    const paramName = 'JOB_NUMBER';
    const direction = 'ASC';
    await expect(
      service.tasksService.getTasks(
        makeContext('trackingId'),
        accessToken,
        offset,
        limit,
        status,
        paramName,
        direction,
      ),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E000301'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });

  describe('DBテスト', () => {
    let source: DataSource = null;
    beforeEach(async () => {
      source = new DataSource({
        type: 'sqlite',
        database: ':memory:',
        logging: false,
        entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
        synchronize: true, // trueにすると自動的にmigrationが行われるため注意
      });
      return source.initialize();
    });

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

    it('[Admin] Taskが0件であっても実行できる', async () => {
      const notificationhubServiceMockValue =
        makeDefaultNotificationhubServiceMockValue();
      const module = await makeTaskTestingModule(
        source,
        notificationhubServiceMockValue,
      );
      const { accountId } = await createAccount(source);
      const { externalId } = await createUser(
        source,
        accountId,
        'userId',
        'admin',
      );

      const service = module.get<TasksService>(TasksService);
      const accessToken = { userId: externalId, role: 'admin', tier: 5 };
      const offset = 0;
      const limit = 20;
      const status = ['Uploaded,Backup'];
      const paramName = 'JOB_NUMBER';
      const direction = 'ASC';

      const { tasks, total } = await service.getTasks(
        makeContext('trackingId'),
        accessToken,
        offset,
        limit,
        status,
        paramName,
        direction,
      );
      expect(tasks).toEqual([]);
      expect(total).toEqual(0);
    });
    it('[Author] Authorは自分が作成者のTask一覧を取得できる', async () => {
      const notificationhubServiceMockValue =
        makeDefaultNotificationhubServiceMockValue();
      const module = await makeTaskTestingModule(
        source,
        notificationhubServiceMockValue,
      );
      const { accountId } = await createAccount(source);
      const { userId } = await createUser(
        source,
        accountId,
        'userId',
        'author',
        'MY_AUTHOR_ID',
      );
      await createTask(
        source,
        accountId,
        userId,
        'MY_AUTHOR_ID',
        '',
        '01',
        '00000001',
        'Uploaded',
      );
      await createTask(
        source,
        accountId,
        userId,
        'MY_AUTHOR_ID',
        '',
        '01',
        '00000002',
        'Uploaded',
      );

      const service = module.get<TasksService>(TasksService);
      const accessToken = { userId: 'userId', role: 'author', tier: 5 };
      const offset = 0;
      const limit = 20;
      const status = ['Uploaded', 'Backup'];
      const paramName = 'JOB_NUMBER';
      const direction = 'ASC';

      const { tasks, total } = await service.getTasks(
        makeContext('trackingId'),
        accessToken,
        offset,
        limit,
        status,
        paramName,
        direction,
      );

      expect(total).toEqual(2);
      {
        const task = tasks[0];
        expect(task.jobNumber).toEqual('00000001');
      }
      {
        const task = tasks[1];
        expect(task.jobNumber).toEqual('00000002');
      }
    });
    it('[Author] Authorは同一アカウントであっても自分以外のAuhtorのTaskは取得できない', async () => {
      const notificationhubServiceMockValue =
        makeDefaultNotificationhubServiceMockValue();
      const module = await makeTaskTestingModule(
        source,
        notificationhubServiceMockValue,
      );
      const { accountId } = await createAccount(source);
      const { userId: userId_1 } = await createUser(
        source,
        accountId,
        'userId_1',
        'author',
        'AUTHOR_ID_1',
      );
      const { userId: userId_2 } = await createUser(
        source,
        accountId,
        'userId_2',
        'author',
        'AUTHOR_ID_2',
      );
      await createTask(
        source,
        accountId,
        userId_1,
        'AUTHOR_ID_1',
        '',
        '01',
        '00000001',
        'Uploaded',
      );

      await createTask(
        source,
        accountId,
        userId_2,
        'AUTHOR_ID_2',
        '',
        '01',
        '00000002',
        'Uploaded',
      );

      const service = module.get<TasksService>(TasksService);
      const accessToken = { userId: 'userId_1', role: 'author', tier: 5 };
      const offset = 0;
      const limit = 20;
      const status = ['Uploaded', 'Backup'];
      const paramName = 'JOB_NUMBER';
      const direction = 'ASC';

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

describe('changeCheckoutPermission', () => {
  let source: DataSource = null;
  beforeEach(async () => {
    source = new DataSource({
      type: 'sqlite',
      database: ':memory:',
      logging: false,
      entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
      synchronize: true, // trueにすると自動的にmigrationが行われるため注意
    });
    return source.initialize();
  });

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

  it('タスクのチェックアウト権限を変更できる。(個人指定)', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId_1 } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: typistUserId_2 } = await createUser(
      source,
      accountId,
      'typist-user-2-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
    );
    const { userGroupId } = await createUserGroup(
      source,
      accountId,
      'USER_GROUP_A',
      [typistUserId_1],
    );
    await createCheckoutPermissions(source, taskId, typistUserId_1);
    await createCheckoutPermissions(source, taskId, undefined, userGroupId);
    const service = module.get<TasksService>(TasksService);
    await service.changeCheckoutPermission(
      makeContext('trackingId'),
      1,
      [{ typistName: 'typist-user-2', typistUserId: typistUserId_2 }],
      'author-user-external-id',
      ['admin'],
    );
    const permisions = await getCheckoutPermissions(source, taskId);
    expect(permisions.length).toEqual(1);
    expect(permisions[0]).toEqual({
      id: 3,
      task_id: taskId,
      user_id: typistUserId_2,
      user_group_id: null,
    });
  });

  it('タスクのチェックアウト権限を変更できる。(グループ指定)', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId_1 } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: typistUserId_2 } = await createUser(
      source,
      accountId,
      'typist-user-2-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
    );
    const { userGroupId: userGroupId_1 } = await createUserGroup(
      source,
      accountId,
      'USER_GROUP_A',
      [typistUserId_1],
    );
    const { userGroupId: userGroupId_2 } = await createUserGroup(
      source,
      accountId,
      'USER_GROUP_B',
      [typistUserId_2],
    );
    await createCheckoutPermissions(source, taskId, typistUserId_1);
    await createCheckoutPermissions(source, taskId, undefined, userGroupId_1);
    const service = module.get<TasksService>(TasksService);
    await service.changeCheckoutPermission(
      makeContext('trackingId'),
      1,
      [{ typistName: 'USER_GROUP_B', typistGroupId: userGroupId_2 }],
      'author-user-external-id',
      ['admin'],
    );
    const permisions = await getCheckoutPermissions(source, taskId);
    expect(permisions.length).toEqual(1);
    expect(permisions[0]).toEqual({
      id: 3,
      task_id: taskId,
      user_id: null,
      user_group_id: userGroupId_2,
    });
  });

  it('タスクのチェックアウト権限を変更できる。(チェックアウト権限を外す)', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId_1 } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
    );
    const { userGroupId } = await createUserGroup(
      source,
      accountId,
      'USER_GROUP_A',
      [typistUserId_1],
    );
    await createCheckoutPermissions(source, taskId, typistUserId_1);
    await createCheckoutPermissions(source, taskId, undefined, userGroupId);
    const service = module.get<TasksService>(TasksService);
    await service.changeCheckoutPermission(
      makeContext('trackingId'),
      1,
      [],
      'author-user-external-id',
      ['admin'],
    );
    const permisions = await getCheckoutPermissions(source, taskId);
    expect(permisions.length).toEqual(0);
  });

  it('ユーザーが存在しない場合、タスクのチェックアウト権限を変更できない', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId_1 } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
    );
    const { userGroupId } = await createUserGroup(
      source,
      accountId,
      'USER_GROUP_A',
      [typistUserId_1],
    );
    await createCheckoutPermissions(source, taskId, typistUserId_1);
    await createCheckoutPermissions(source, taskId, undefined, userGroupId);
    const service = module.get<TasksService>(TasksService);

    await expect(
      service.changeCheckoutPermission(
        makeContext('trackingId'),
        1,
        [{ typistName: 'not-exist-user', typistUserId: 999 }],
        'author-user-external-id',
        ['admin'],
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010204'), HttpStatus.BAD_REQUEST),
    );
  });

  it('ユーザーグループが存在しない場合、タスクのチェックアウト権限を変更できない', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId_1 } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
    );
    const { userGroupId } = await createUserGroup(
      source,
      accountId,
      'USER_GROUP_A',
      [typistUserId_1],
    );
    await createCheckoutPermissions(source, taskId, typistUserId_1);
    await createCheckoutPermissions(source, taskId, undefined, userGroupId);
    const service = module.get<TasksService>(TasksService);

    await expect(
      service.changeCheckoutPermission(
        makeContext('trackingId'),
        1,
        [{ typistName: 'not-exist-user-group', typistGroupId: 999 }],
        'author-user-external-id',
        ['admin'],
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010204'), HttpStatus.BAD_REQUEST),
    );
  });

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

    await expect(
      service.changeCheckoutPermission(
        makeContext('trackingId'),
        1,
        [{ typistName: 'typist-user', typistUserId: typistUserId }],
        'author-user-external-id',
        ['admin'],
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
    );
  });

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

    await expect(
      service.changeCheckoutPermission(
        makeContext('trackingId'),
        1,
        [{ typistName: 'typist-user', typistUserId: typistUserId }],
        'author-user-external-id',
        ['admin'],
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
    );
  });

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

    await expect(
      service.changeCheckoutPermission(
        makeContext('trackingId'),
        1,
        [{ typistName: 'typist-user', typistUserId: typistUserId }],
        'author-user-external-id',
        ['author'],
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
    );
  });

  it('通知に失敗した場合、エラーとなる', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    notificationhubServiceMockValue.notify = new Error('Notify Error.');
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId_1 } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: typistUserId_2 } = await createUser(
      source,
      accountId,
      'typist-user-2-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
    );
    const { userGroupId } = await createUserGroup(
      source,
      accountId,
      'USER_GROUP_A',
      [typistUserId_1],
    );
    await createCheckoutPermissions(source, taskId, typistUserId_1);
    await createCheckoutPermissions(source, taskId, undefined, userGroupId);
    const service = module.get<TasksService>(TasksService);

    await expect(
      service.changeCheckoutPermission(
        makeContext('trackingId'),
        1,
        [{ typistName: 'typist-user-2', typistUserId: typistUserId_2 }],
        'author-user-external-id',
        ['admin'],
      ),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
});

describe('checkout', () => {
  let source: DataSource = null;
  beforeEach(async () => {
    source = new DataSource({
      type: 'sqlite',
      database: ':memory:',
      logging: false,
      entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
      synchronize: true, // trueにすると自動的にmigrationが行われるため注意
    });
    return source.initialize();
  });

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

  it('ユーザーのRoleがTypistで、タスクのチェックアウト権限が個人指定である時、タスクをチェックアウトできる', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
    );
    const { userGroupId } = await createUserGroup(
      source,
      accountId,
      'USER_GROUP_A',
      [typistUserId],
    );
    await createCheckoutPermissions(source, taskId, typistUserId);
    await createCheckoutPermissions(source, taskId, undefined, userGroupId);

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

    const initTask = await getTask(source, taskId);

    await service.checkout(
      makeContext('trackingId'),
      1,
      ['typist'],
      'typist-user-external-id',
    );
    const { status, typist_user_id, started_at } = await getTask(
      source,
      taskId,
    );
    const permisions = await getCheckoutPermissions(source, taskId);

    expect(status).toEqual('InProgress');
    expect(typist_user_id).toEqual(typistUserId);
    expect(started_at).not.toEqual(initTask.started_at);
    expect(permisions.length).toEqual(1);
    expect(permisions[0]).toEqual({
      id: 3,
      task_id: 1,
      user_id: 1,
      user_group_id: null,
    });
  });

  it('ユーザーのRoleがTypistで、タスクのチェックアウト権限がグループ指定である時、タスクをチェックアウトできる', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
    );
    const { userGroupId } = await createUserGroup(
      source,
      accountId,
      'USER_GROUP_A',
      [typistUserId],
    );
    await createCheckoutPermissions(source, taskId, typistUserId);
    await createCheckoutPermissions(source, taskId, undefined, userGroupId);

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

    const initTask = await getTask(source, taskId);

    await service.checkout(
      makeContext('trackingId'),
      1,
      ['typist'],
      'typist-user-external-id',
    );
    const { status, typist_user_id, started_at } = await getTask(
      source,
      taskId,
    );
    const permisions = await getCheckoutPermissions(source, taskId);

    expect(status).toEqual('InProgress');
    expect(typist_user_id).toEqual(typistUserId);
    expect(started_at).not.toEqual(initTask.started_at);
    expect(permisions.length).toEqual(1);
    expect(permisions[0]).toEqual({
      id: 3,
      task_id: 1,
      user_id: 1,
      user_group_id: null,
    });
  });

  it('ユーザーのRoleがTypistで、タスクのステータスがPendingである時、タスクをチェックアウトできる', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Pending',
    );
    await createCheckoutPermissions(source, taskId, typistUserId);

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

    const initTask = await getTask(source, taskId);

    await service.checkout(
      makeContext('trackingId'),
      1,
      ['typist'],
      'typist-user-external-id',
    );
    const { status, typist_user_id, started_at } = await getTask(
      source,
      taskId,
    );
    const permisions = await getCheckoutPermissions(source, taskId);

    expect(status).toEqual('InProgress');
    expect(typist_user_id).toEqual(typistUserId);
    //タスクの元々のステータスがPending,Inprogressの場合、文字起こし開始時刻は更新されない
    expect(started_at).toEqual(initTask.started_at);
    expect(permisions.length).toEqual(1);
    expect(permisions[0]).toEqual({
      id: 2,
      task_id: 1,
      user_id: 1,
      user_group_id: null,
    });
  });

  it('ユーザーのRoleがTypistで、対象のタスクのStatus[Uploaded,Inprogress,Pending]以外の時、タスクをチェックアウトできない', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
      'MY_AUTHOR_ID',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Backup',
    );

    const service = module.get<TasksService>(TasksService);
    await expect(
      service.checkout(
        makeContext('trackingId'),
        1,
        ['typist'],
        'typist-user-external-id',
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
    );
  });

  it('ユーザーのRoleがTypistで、チェックアウト権限が存在しない時、タスクをチェックアウトできない', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
      'MY_AUTHOR_ID',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
    );

    const service = module.get<TasksService>(TasksService);
    await expect(
      service.checkout(
        makeContext('trackingId'),
        1,
        ['typist'],
        'typist-user-external-id',
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010602'), HttpStatus.BAD_REQUEST),
    );
  });

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

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

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

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

  it('ユーザーのRoleがAuthorで、アップロードした音声ファイルに紐づいたタスクが存在しない場合、タスクをチェックアウトできない', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );

    const service = module.get<TasksService>(TasksService);
    await expect(
      service.checkout(
        makeContext('trackingId'),
        1,
        ['author'],
        'author-user-external-id',
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
    );
  });

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

    const service = module.get<TasksService>(TasksService);
    await expect(
      service.checkout(
        makeContext('trackingId'),
        1,
        ['author'],
        'author-user-external-id',
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010602'), HttpStatus.BAD_REQUEST),
    );
  });

  it('ユーザーのRoleに[Typist,author]が設定されていない時、タスクをチェックアウトできない', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    await createUser(
      source,
      accountId,
      'none-user-external-id',
      'none',
      'MY_AUTHOR_ID',
    );

    const service = module.get<TasksService>(TasksService);
    await expect(
      service.checkout(
        makeContext('trackingId'),
        1,
        ['none'],
        'none-user-external-id',
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010602'), HttpStatus.BAD_REQUEST),
    );
  });
});

describe('checkin', () => {
  let source: DataSource = null;
  beforeEach(async () => {
    source = new DataSource({
      type: 'sqlite',
      database: ':memory:',
      logging: false,
      entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
      synchronize: true, // trueにすると自動的にmigrationが行われるため注意
    });
    return source.initialize();
  });

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

  it('API実行者が文字起こし実行中のタスクである場合、タスクをチェックインできる', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'InProgress',
      typistUserId,
    );
    await createCheckoutPermissions(source, taskId, typistUserId);

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

    const initTask = await getTask(source, taskId);

    await service.checkin(
      makeContext('trackingId'),
      1,
      'typist-user-external-id',
    );
    const { status, finished_at } = await getTask(source, taskId);

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

  it('タスクのステータスがInprogressでない時、タスクをチェックインできない', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
      typistUserId,
    );
    await createCheckoutPermissions(source, taskId, typistUserId);

    const service = module.get<TasksService>(TasksService);
    await expect(
      service.checkin(makeContext('trackingId'), 1, 'typist-user-external-id'),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
    );
  });

  it('API実行者が文字起こし実行中のタスクでない場合、タスクをチェックインできない', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    await createUser(source, accountId, 'typist-user-external-id', 'typist');
    const { userId: anotherTypistUserId } = await createUser(
      source,
      accountId,
      'another-typist-user-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'InProgress',
      // API実行者のタスクではないため、typist_user_idは設定しない
    );
    await createCheckoutPermissions(source, taskId, anotherTypistUserId);

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

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

  it('タスクがない時、タスクをチェックインできない', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    await createUser(source, accountId, 'typist-user-external-id', 'typist');

    await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );

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

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

describe('suspend', () => {
  let source: DataSource = null;
  beforeEach(async () => {
    source = new DataSource({
      type: 'sqlite',
      database: ':memory:',
      logging: false,
      entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
      synchronize: true, // trueにすると自動的にmigrationが行われるため注意
    });
    return source.initialize();
  });

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

  it('API実行者が文字起こし実行中のタスクである場合、タスクを中断できる', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'InProgress',
      typistUserId,
    );

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

    await service.suspend(
      makeContext('trackingId'),
      1,
      'typist-user-external-id',
    );
    const { status } = await getTask(source, taskId);

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

  it('タスクのステータスがInprogressでない時、タスクを中断できない', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
      typistUserId,
    );
    await createCheckoutPermissions(source, taskId, typistUserId);

    const service = module.get<TasksService>(TasksService);
    await expect(
      service.suspend(makeContext('trackingId'), 1, 'typist-user-external-id'),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
    );
  });

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

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

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

  it('タスクがない時、タスクを中断できない', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    await createUser(source, accountId, 'typist-user-external-id', 'typist');

    await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );

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

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

describe('cancel', () => {
  let source: DataSource = null;
  beforeEach(async () => {
    source = new DataSource({
      type: 'sqlite',
      database: ':memory:',
      logging: false,
      entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
      synchronize: true, // trueにすると自動的にmigrationが行われるため注意
    });
    return source.initialize();
  });

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

  it('API実行者のRoleがTypistの場合、自身が文字起こし実行中のタスクをキャンセルできる', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'InProgress',
      typistUserId,
    );
    await createCheckoutPermissions(source, taskId, typistUserId);

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

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

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

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

    await service.cancel(
      makeContext('trackingId'),
      1,
      'typist-user-external-id',
      ['typist', 'standard'],
    );
    const { status, typist_user_id } = await getTask(source, taskId);

    const permisions = await getCheckoutPermissions(source, taskId);

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

  it('API実行者のRoleがAdminの場合、文字起こし実行中のタスクをキャンセルできる', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'InProgress',
      typistUserId,
    );

    await createCheckoutPermissions(source, taskId, typistUserId);

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

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

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

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

    await createCheckoutPermissions(source, taskId, typistUserId);

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

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

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

  it('タスクのステータスが[Inprogress,Pending]でない時、タスクをキャンセルできない', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    const { userId: typistUserId } = await createUser(
      source,
      accountId,
      'typist-user-external-id',
      'typist',
    );
    const { userId: authorUserId } = await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );
    const { taskId } = await createTask(
      source,
      accountId,
      authorUserId,
      'MY_AUTHOR_ID',
      '',
      '01',
      '00000001',
      'Uploaded',
      typistUserId,
    );
    await createCheckoutPermissions(source, taskId, typistUserId);

    const service = module.get<TasksService>(TasksService);
    await expect(
      service.cancel(makeContext('trackingId'), 1, 'typist-user-external-id', [
        'admin',
        'author',
      ]),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
    );
  });

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

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

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

  it('タスクがない時、タスクをキャンセルできない', async () => {
    const notificationhubServiceMockValue =
      makeDefaultNotificationhubServiceMockValue();
    const module = await makeTaskTestingModule(
      source,
      notificationhubServiceMockValue,
    );
    const { accountId } = await createAccount(source);
    await createUser(source, accountId, 'typist-user-external-id', 'typist');

    await createUser(
      source,
      accountId,
      'author-user-external-id',
      'author',
      'MY_AUTHOR_ID',
    );

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

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