import { HttpException, HttpStatus } from '@nestjs/common';
import { AccessToken } from '../../common/token';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import {
  makeDefaultAdB2cMockValue,
  makeDefaultConfigValue,
  makeDefaultSendGridlValue,
  makeDefaultSortCriteriaRepositoryMockValue,
  makeDefaultUsersRepositoryMockValue,
  makeUsersServiceMock,
} from './test/users.service.mock';
import { EmailAlreadyVerifiedError } from '../../repositories/users/errors/types';
import {
  createAccount,
  createLicense,
  createUser,
  createUserGroup,
  makeTestingModuleWithAdb2c,
} from './test/utility';
import { DataSource } from 'typeorm';
import { UsersService } from './users.service';
import {
  LICENSE_EXPIRATION_THRESHOLD_DAYS,
  USER_LICENSE_STATUS,
} from '../../constants';

describe('UsersService.confirmUser', () => {
  it('ユーザの仮登録時に払い出されるトークンにより、未認証のユーザが認証済みになる', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendGridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const token =
      'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
    expect(await service.confirmUser(token)).toEqual(undefined);
  });

  it('ユーザーが発行されたパスワードでログインできるようにする', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    usersRepositoryMockValue.findUserById = {
      id: 1,
      external_id: 'TEST9999',
      account_id: 1,
      role: 'None',
      accepted_terms_version: 'string',
      email_verified: false,
      created_by: 'string;',
      created_at: new Date(),
      updated_by: 'string;',
      updated_at: new Date(),
      auto_renew: true,
      license_alert: true,
      notification: true,
      encryption: false,
      prompt: false,
    };
    const adb2cParam = makeDefaultAdB2cMockValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendGridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );

    const token =
      'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
    expect(await service.confirmUserAndInitPassword(token)).toEqual(undefined);
  });

  it('トークンの形式が不正な場合、形式不正エラーとなる。', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const token = 'invalid.id.token';
    await expect(service.confirmUser(token)).rejects.toEqual(
      new HttpException(makeErrorResponse('E000101'), HttpStatus.BAD_REQUEST),
    );
  });

  it('トークンの形式が不正な場合、形式不正エラーとなる。(メール認証API)', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    usersRepositoryMockValue.findUserById = {
      id: 1,
      external_id: 'TEST9999',
      account_id: 1,
      role: 'None',
      accepted_terms_version: 'string',
      email_verified: false,
      created_by: 'string;',
      created_at: new Date(),
      updated_by: 'string;',
      updated_at: new Date(),
      auto_renew: true,
      license_alert: true,
      notification: true,
      encryption: false,
      prompt: false,
    };
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendGridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const token = 'invalid.id.token';
    await expect(service.confirmUserAndInitPassword(token)).rejects.toEqual(
      new HttpException(makeErrorResponse('E000101'), HttpStatus.BAD_REQUEST),
    );
  });
  it('ユーザが既に認証済みだった場合、認証済みユーザエラーとなる。', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();

    usersRepositoryMockValue.updateUserVerified =
      new EmailAlreadyVerifiedError();

    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const token =
      'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
    await expect(service.confirmUser(token)).rejects.toEqual(
      new HttpException(makeErrorResponse('E010202'), HttpStatus.BAD_REQUEST),
    );
  });
  it('ユーザが既に認証済みだった場合、認証済みユーザエラーとなる。(メール認証API)', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    usersRepositoryMockValue.findUserById = {
      id: 1,
      external_id: 'TEST9999',
      account_id: 1,
      role: 'None',
      accepted_terms_version: 'string',
      email_verified: true,
      created_by: 'string;',
      created_at: new Date(),
      updated_by: 'string;',
      updated_at: new Date(),
      auto_renew: true,
      license_alert: true,
      notification: true,
      encryption: false,
      prompt: false,
    };
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    usersRepositoryMockValue.updateUserVerified =
      new EmailAlreadyVerifiedError();

    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendGridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const token =
      'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
    await expect(service.confirmUserAndInitPassword(token)).rejects.toEqual(
      new HttpException(makeErrorResponse('E010202'), HttpStatus.BAD_REQUEST),
    );
  });
  it('DBネットワークエラーとなる場合、エラーとなる。', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    usersRepositoryMockValue.updateUserVerified = new Error('DB error');

    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const token =
      'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
    await expect(service.confirmUser(token)).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('DBネットワークエラーとなる場合、エラーとなる。(メール認証API)', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    usersRepositoryMockValue.findUserById = {
      id: 1,
      external_id: 'TEST9999',
      account_id: 1,
      role: 'None',
      accepted_terms_version: 'string',
      email_verified: false,
      created_by: 'string;',
      created_at: new Date(),
      updated_by: 'string;',
      updated_at: new Date(),
      auto_renew: true,
      license_alert: true,
      notification: true,
      encryption: false,
      prompt: false,
    };
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    usersRepositoryMockValue.updateUserVerified = new Error('DB error');
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendGridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const token =
      'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
    await expect(service.confirmUserAndInitPassword(token)).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:None)', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const name = 'test_user1';
    const role = 'None';
    const email = 'test1@example.co.jp';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    expect(
      await service.createUser(
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
      ),
    ).toEqual(undefined);
  });
});

describe('UsersService.createUser', () => {
  it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author)', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const name = 'test_user2';
    const role = 'Author';
    const email = 'test2@example.co.jp';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;
    const authorId = 'testID';
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    expect(
      await service.createUser(
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
        authorId,
      ),
    ).toEqual(undefined);
  });

  it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Transcriptioninst)', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const name = 'test_user3';
    const role = 'Transcriptioninst';
    const email = 'test3@example.co.jp';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;
    const typistGroupId = 111;
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    expect(
      await service.createUser(
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
        undefined,
        typistGroupId,
      ),
    ).toEqual(undefined);
  });
  it('DBネットワークエラーとなる場合、エラーとなる。', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    usersRepositoryMockValue.createNormalUser = new Error('DB error');
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const name = 'test_user5';
    const role = 'Transcriptioninst';
    const email = 'test5@example.co.jp';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    await expect(
      service.createUser(
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
      ),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    adb2cParam.createUser = new Error();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const name = 'test_user6';
    const role = 'Transcriptioninst';
    const email = 'test6@example.co.jp';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    await expect(
      service.createUser(
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
      ),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('メールアドレスが重複している場合、エラーとなる。', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    adb2cParam.createUser = { reason: 'email', message: 'ObjectConflict' };
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const name = 'test_user7';
    const role = 'Transcriptioninst';
    const email = 'test7@example.co.jp';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    await expect(
      service.createUser(
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010301'), HttpStatus.BAD_REQUEST),
    );
  });
  it('AuthorIDが重複している場合、エラーとなる。(AuthorID重複チェックでエラー)', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    usersRepositoryMockValue.createNormalUser = new Error();
    usersRepositoryMockValue.existsAuthorId = true;

    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const name = 'test_user8';
    const role = 'Author';
    const email = 'test8@example.co.jp';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;
    const authorId = 'testID';
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    await expect(
      service.createUser(
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
        authorId,
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010302'), HttpStatus.BAD_REQUEST),
    );
  });
  it('AuthorIDが重複している場合、エラーとなる。(insert失敗)', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    usersRepositoryMockValue.createNormalUser = new Error();
    usersRepositoryMockValue.createNormalUser.name = 'ER_DUP_ENTRY';

    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const name = 'test_user9';
    const role = 'Author';
    const email = 'test9@example.co.jp';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;
    const authorId = 'testID';
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    await expect(
      service.createUser(
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
        authorId,
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010302'), HttpStatus.BAD_REQUEST),
    );
  });
});

describe('UsersService.getUsers', () => {
  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 adb2cParam = makeDefaultAdB2cMockValue();
    const module = await makeTestingModuleWithAdb2c(source, adb2cParam);

    const { accountId } = await createAccount(source);
    const { externalId: externalId_author, userId: authorUserId } =
      await createUser(
        source,
        accountId,
        'external_id1',
        'author',
        'AUTHOR_ID',
        true,
      );

    const { userId: typistUserId } = await createUser(
      source,
      accountId,
      'external_id2',
      'typist',
      undefined,
      true,
    );

    await createUserGroup(source, accountId, 'group1', [typistUserId]);

    const { userId: noneUserId } = await createUser(
      source,
      accountId,
      'external_id3',
      'none',
      undefined,
      true,
    );

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

    const expectedUsers = [
      {
        id: authorUserId,
        name: 'test1',
        role: 'author',
        authorId: 'AUTHOR_ID',
        typistGroupName: [],
        email: 'test1@mail.com',
        emailVerified: true,
        autoRenew: true,
        licenseAlert: true,
        notification: true,
        encryption: false,
        prompt: false,
        expiration: undefined,
        remaining: undefined,
        licenseStatus: USER_LICENSE_STATUS.NO_LICENSE,
      },
      {
        id: typistUserId,
        name: 'test2',
        role: 'typist',
        authorId: undefined,
        typistGroupName: ['group1'],
        email: 'test2@mail.com',
        emailVerified: true,
        autoRenew: true,
        licenseAlert: true,
        notification: true,
        encryption: false,
        prompt: false,
        expiration: undefined,
        remaining: undefined,
        licenseStatus: USER_LICENSE_STATUS.NO_LICENSE,
      },
      {
        id: noneUserId,
        name: 'test3',
        role: 'none',
        authorId: undefined,
        typistGroupName: [],
        email: 'test3@mail.com',
        emailVerified: true,
        autoRenew: true,
        licenseAlert: true,
        notification: true,
        encryption: false,
        prompt: false,
        expiration: undefined,
        remaining: undefined,
        licenseStatus: USER_LICENSE_STATUS.NO_LICENSE,
      },
    ];

    expect(await service.getUsers(externalId_author)).toEqual(expectedUsers);
  });

  it('ユーザーの一覧を取得できること（ライセンス割当済み）', async () => {
    const adb2cParam = makeDefaultAdB2cMockValue();
    const module = await makeTestingModuleWithAdb2c(source, adb2cParam);

    const { accountId } = await createAccount(source);
    const { userId: user1, externalId: external_id1 } = await createUser(
      source,
      accountId,
      'external_id1',
      'author',
      'AUTHOR_ID1',
      true,
    );
    const { userId: user2 } = await createUser(
      source,
      accountId,
      'external_id2',
      'author',
      'AUTHOR_ID2',
      true,
    );
    const { userId: user3 } = await createUser(
      source,
      accountId,
      'external_id3',
      'author',
      'AUTHOR_ID3',
      false,
    );

    const date1 = new Date();
    date1.setDate(date1.getDate() + LICENSE_EXPIRATION_THRESHOLD_DAYS + 1);
    const date2 = new Date();
    date2.setDate(date2.getDate() + LICENSE_EXPIRATION_THRESHOLD_DAYS);
    const date3 = new Date();
    date3.setDate(date3.getDate() + LICENSE_EXPIRATION_THRESHOLD_DAYS - 1);
    await createLicense(source, accountId, user1, date1);
    await createLicense(source, accountId, user2, date2);
    await createLicense(source, accountId, user3, date3);

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

    const expectedUsers = [
      {
        id: user1,
        name: 'test1',
        role: 'author',
        authorId: 'AUTHOR_ID1',
        typistGroupName: [],
        email: 'test1@mail.com',
        emailVerified: true,
        autoRenew: true,
        licenseAlert: true,
        notification: true,
        encryption: false,
        prompt: false,
        expiration: `${date1.getFullYear()}/${
          date1.getMonth() + 1
        }/${date1.getDate()}`,
        remaining: LICENSE_EXPIRATION_THRESHOLD_DAYS + 1,
        licenseStatus: USER_LICENSE_STATUS.NORMAL,
      },
      {
        id: user2,
        name: 'test2',
        role: 'author',
        authorId: 'AUTHOR_ID2',
        typistGroupName: [],
        email: 'test2@mail.com',
        emailVerified: true,
        autoRenew: true,
        licenseAlert: true,
        notification: true,
        encryption: false,
        prompt: false,
        expiration: `${date2.getFullYear()}/${
          date2.getMonth() + 1
        }/${date2.getDate()}`,
        remaining: LICENSE_EXPIRATION_THRESHOLD_DAYS,
        licenseStatus: USER_LICENSE_STATUS.RENEW,
      },
      {
        id: user3,
        name: 'test3',
        role: 'author',
        authorId: 'AUTHOR_ID3',
        typistGroupName: [],
        email: 'test3@mail.com',
        emailVerified: true,
        autoRenew: false,
        licenseAlert: true,
        notification: true,
        encryption: false,
        prompt: false,
        expiration: `${date3.getFullYear()}/${
          date3.getMonth() + 1
        }/${date3.getDate()}`,
        remaining: LICENSE_EXPIRATION_THRESHOLD_DAYS - 1,
        licenseStatus: USER_LICENSE_STATUS.ALERT,
      },
    ];

    expect(await service.getUsers(external_id1)).toEqual(expectedUsers);
  });

  it('DBからのユーザーの取得に失敗した場合、エラーとなる', async () => {
    const adb2cParam = makeDefaultAdB2cMockValue();
    const module = await makeTestingModuleWithAdb2c(source, adb2cParam);

    const { accountId } = await createAccount(source);
    await createUser(
      source,
      accountId,
      'external_id1',
      'author',
      'AUTHOR_ID',
      true,
    );

    const service = module.get<UsersService>(UsersService);
    await expect(service.getUsers('externalId_failed')).rejects.toEqual(
      new HttpException(makeErrorResponse('E009999'), HttpStatus.NOT_FOUND),
    );
  });

  it('ADB2Cからのユーザーの取得に失敗した場合、エラーとなる', async () => {
    const adb2cParam = makeDefaultAdB2cMockValue();
    adb2cParam.getUsers = new Error('ADB2C error');
    const module = await makeTestingModuleWithAdb2c(source, adb2cParam);

    const { accountId } = await createAccount(source);
    const { externalId: externalId_author } = await createUser(
      source,
      accountId,
      'external_id1',
      'author',
      'AUTHOR_ID',
      true,
    );

    const service = module.get<UsersService>(UsersService);
    await expect(service.getUsers(externalId_author)).rejects.toEqual(
      new HttpException(makeErrorResponse('E009999'), HttpStatus.NOT_FOUND),
    );
  });
});

describe('UsersService.updateSortCriteria', () => {
  it('ソート条件を変更できる', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );

    expect(
      await service.updateSortCriteria('AUTHOR_ID', 'ASC', {
        role: 'none admin',
        userId: 'xxxxxxxxxxxx',
        tier: 5,
      }),
    ).toEqual(undefined);
  });

  it('ユーザー情報が存在せず、ソート条件を変更できない', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();

    usersRepositoryMockValue.findUserByExternalId = new Error('user not found');

    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );

    await expect(
      service.updateSortCriteria('AUTHOR_ID', 'ASC', {
        role: 'none admin',
        userId: 'xxxxxxxxxxxx',
        tier: 5,
      }),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });

  it('ソート条件が存在せず、ソート条件を変更できない', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    sortCriteriaRepositoryMockValue.updateSortCriteria = new Error(
      'sort criteria not found',
    );

    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );

    await expect(
      service.updateSortCriteria('AUTHOR_ID', 'ASC', {
        role: 'none admin',
        userId: 'xxxxxxxxxxxx',
        tier: 5,
      }),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
});

describe('UsersService.getSortCriteria', () => {
  it('ソート条件を取得できる', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );

    expect(
      await service.getSortCriteria({
        role: 'none admin',
        userId: 'xxxxxxxxxxxx',
        tier: 5,
      }),
    ).toEqual({ direction: 'ASC', paramName: 'JOB_NUMBER' });
  });

  it('ソート条件が存在せず、ソート条件を取得できない', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();

    sortCriteriaRepositoryMockValue.getSortCriteria = new Error(
      'sort criteria not found',
    );

    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );

    await expect(
      service.getSortCriteria({
        role: 'none admin',
        userId: 'xxxxxxxxxxxx',
        tier: 5,
      }),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });

  it('DBから取得した値が不正だった場合、エラーとなる', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    sortCriteriaRepositoryMockValue.getSortCriteria = {
      id: 1,
      direction: 'AAA',
      parameter: 'BBBBB',
      user_id: 1,
    };

    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      adb2cParam,
      sendgridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );

    await expect(
      service.getSortCriteria({
        role: 'none admin',
        userId: 'xxxxxxxxxxxx',
        tier: 5,
      }),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
});
