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,
  createAccountAndAdminUser,
  createLicense,
  createUser,
  createUserGroup,
  getLicenses,
  getUser,
  getUserByExternalId,
  getUsers,
  makeTestingModuleWithAdb2c,
} from './test/utility';
import { DataSource } from 'typeorm';
import { UsersService } from './users.service';
import {
  LICENSE_ALLOCATED_STATUS,
  LICENSE_EXPIRATION_THRESHOLD_DAYS,
  LICENSE_TYPE,
  USER_LICENSE_STATUS,
  USER_ROLES,
} from '../../constants';
import { makeTestingModule } from '../../common/test/modules';
import { Context, makeContext } from '../../common/log';
import {
  overrideAdB2cService,
  overrideSendgridService,
  overrideUsersRepositoryService,
} from '../../common/test/overrides';
import { NewTrialLicenseExpirationDate } from '../licenses/types/types';
import { License } from '../../repositories/licenses/entity/license.entity';
import { AdB2cService } from '../../gateways/adb2c/adb2c.service';

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

  it('ユーザの仮登録時に払い出されるトークンにより、未認証のユーザが認証済みになり、トライアルライセンスが100件作成される', async () => {
    const module = await makeTestingModule(source);
    const { accountId } = await createAccount(source);
    await createUser(
      source,
      accountId,
      'externalId_user1',
      USER_ROLES.NONE,
      undefined,
      true,
      false,
      undefined,
      false,
      false,
    );
    const { userId } = await createUser(
      source,
      accountId,
      'externalId_user2',
      USER_ROLES.NONE,
      undefined,
      true,
      false,
      undefined,
      false,
      false,
    );

    const service = module.get<UsersService>(UsersService);
    const token =
      'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
    await service.confirmUser(token);
    //result
    const resultUser = await getUser(source, userId);
    const resultLicenses = await getLicenses(source, accountId);

    // トライアルライセンスは有効期限は今日を起算日として30日後の日付が変わるまで
    const expiryDate = new NewTrialLicenseExpirationDate();
    const resultLicensesCheckParam: Omit<
      License,
      'deleted_at' | 'created_by' | 'created_at' | 'updated_at' | 'updated_by'
    > = {
      id: 0,
      expiry_date: resultLicenses[0].expiry_date,
      account_id: resultLicenses[0].account_id,
      type: resultLicenses[0].type,
      status: resultLicenses[0].status,
      allocated_user_id: resultLicenses[0].allocated_user_id,
      order_id: resultLicenses[0].order_id,
      delete_order_id: resultLicenses[0].delete_order_id,
    };

    expect(resultUser.email_verified).toBe(true);
    expect(resultLicenses.length).toBe(100);
    expect(resultLicensesCheckParam).toEqual({
      id: 0,
      expiry_date: expiryDate,
      account_id: accountId,
      type: LICENSE_TYPE.TRIAL,
      status: LICENSE_ALLOCATED_STATUS.UNALLOCATED,
      allocated_user_id: null,
      order_id: null,
      delete_order_id: null,
    });
  });

  it('トークンの形式が不正な場合、形式不正エラーとなる。', async () => {
    const module = await makeTestingModule(source);
    const token = 'invalid.id.token';
    const service = module.get<UsersService>(UsersService);
    await expect(service.confirmUser(token)).rejects.toEqual(
      new HttpException(makeErrorResponse('E000101'), HttpStatus.BAD_REQUEST),
    );
  });

  it('ユーザが既に認証済みだった場合、認証済みユーザエラーとなる。', async () => {
    const module = await makeTestingModule(source);
    const { accountId } = await createAccount(source);
    await createUser(
      source,
      accountId,
      'externalId_user1',
      USER_ROLES.NONE,
      undefined,
      true,
      false,
      undefined,
      false,
      false,
    );
    await createUser(
      source,
      accountId,
      'externalId_user2',
      USER_ROLES.NONE,
      undefined,
      true,
      false,
      undefined,
      false,
      true, //emailを認証済みにする
    );
    const service = module.get<UsersService>(UsersService);
    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('ユーザーが存在しない場合は、想定外のエラーとなる', async () => {
    const module = await makeTestingModule(source);
    const service = module.get<UsersService>(UsersService);
    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,
      ),
    );
  });
});

describe('UsersService.confirmUserAndInitPassword', () => {
  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 licensesRepositoryMockValue = null;
    const adb2cParam = makeDefaultAdB2cMockValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      licensesRepositoryMockValue,
      adb2cParam,
      sendGridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );

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

  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 licensesRepositoryMockValue = null;
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      licensesRepositoryMockValue,
      adb2cParam,
      sendGridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const token = 'invalid.id.token';
    await expect(
      service.confirmUserAndInitPassword(makeContext('trackingId'), 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: 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 licensesRepositoryMockValue = null;
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    usersRepositoryMockValue.updateUserVerified =
      new EmailAlreadyVerifiedError();

    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      licensesRepositoryMockValue,
      adb2cParam,
      sendGridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const token =
      'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
    await expect(
      service.confirmUserAndInitPassword(makeContext('trackingId'), token),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010202'), HttpStatus.BAD_REQUEST),
    );
  });
  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 licensesRepositoryMockValue = null;
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    usersRepositoryMockValue.updateUserVerified = new Error('DB error');
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      licensesRepositoryMockValue,
      adb2cParam,
      sendGridMockValue,
      configMockValue,
      sortCriteriaRepositoryMockValue,
    );
    const token =
      'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
    await expect(
      service.confirmUserAndInitPassword(makeContext('trackingId'), token),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
});

describe('UsersService.createUser', () => {
  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:None)', async () => {
    const module = await makeTestingModule(source);
    const service = module.get<UsersService>(UsersService);
    const adminExternalId = 'ADMIN0001';
    const {
      accountId,
      role: adminRole,
      tier,
    } = await createAccountAndAdminUser(source, adminExternalId);

    const token: AccessToken = {
      userId: adminExternalId,
      role: adminRole,
      tier: tier,
    };

    const name = 'test_user1';
    const role = USER_ROLES.NONE;
    const email = 'test1@example.com';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;

    const externalId = '0001';

    overrideAdB2cService(service, {
      createUser: async (
        _context: Context,
        _email: string,
        _password: string,
        _username: string,
      ) => {
        // ユーザー作成時に指定したパラメータが正しく渡されていることを確認
        expect(email).toEqual(_email);
        expect(name).toEqual(_username);

        return { sub: externalId };
      },
    });
    overrideSendgridService(service, {
      sendMail: async () => {
        return;
      },
      createMailContentFromEmailConfirmForNormalUser: async () => {
        return { html: '', text: '', subject: '' };
      },
    });

    expect(
      await service.createUser(
        makeContext('trackingId'),
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
      ),
    ).toEqual(undefined);

    // 追加されたユーザーが正しくDBに登録されていることを確認
    const user = await getUserByExternalId(source, externalId);
    expect(user).not.toBeNull();
    expect(user.account_id).toEqual(accountId);
    expect(user.role).toEqual(role);
    expect(user.author_id).toEqual(null);
    expect(user.email_verified).toEqual(false);
    expect(user.auto_renew).toEqual(autoRenew);
    expect(user.license_alert).toEqual(licenseAlert);
    expect(user.notification).toEqual(notification);
    expect(user.encryption).toEqual(false);
    expect(user.encryption_password).toEqual(null);
    expect(user.prompt).toEqual(false);

    // 他にユーザーが登録されていないことを確認
    const users = await getUsers(source);
    expect(users.length).toEqual(2);
  });

  it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author; 暗号化あり)', async () => {
    const module = await makeTestingModule(source);
    const service = module.get<UsersService>(UsersService);
    const adminExternalId = 'ADMIN0001';
    const {
      accountId,
      role: adminRole,
      tier,
    } = await createAccountAndAdminUser(source, adminExternalId);

    const token: AccessToken = {
      userId: adminExternalId,
      role: adminRole,
      tier: tier,
    };

    const name = 'test_user2';
    const role = USER_ROLES.AUTHOR;
    const email = 'test2@example.com';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;
    const authorId = 'testID';
    const encryption = true;
    const prompt = true;
    const encryptionPassword = 'testPassword';

    const externalId = '0001';

    overrideAdB2cService(service, {
      createUser: async (
        _context: Context,
        _email: string,
        _password: string,
        _username: string,
      ) => {
        // ユーザー作成時に指定したパラメータが正しく渡されていることを確認
        expect(email).toEqual(_email);
        expect(name).toEqual(_username);

        return { sub: externalId };
      },
    });
    overrideSendgridService(service, {
      sendMail: async () => {
        return;
      },
      createMailContentFromEmailConfirmForNormalUser: async () => {
        return { html: '', text: '', subject: '' };
      },
    });

    expect(
      await service.createUser(
        makeContext('trackingId'),
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
        authorId,
        encryption,
        encryptionPassword,
        prompt,
      ),
    ).toEqual(undefined);

    // 追加されたユーザーが正しくDBに登録されていることを確認
    const user = await getUserByExternalId(source, externalId);
    expect(user).not.toBeNull();
    expect(user.account_id).toEqual(accountId);
    expect(user.role).toEqual(role);
    expect(user.author_id).toEqual(authorId);
    expect(user.email_verified).toEqual(false);
    expect(user.auto_renew).toEqual(autoRenew);
    expect(user.license_alert).toEqual(licenseAlert);
    expect(user.notification).toEqual(notification);
    expect(user.encryption).toEqual(encryption);
    expect(user.encryption_password).toEqual(encryptionPassword);
    expect(user.prompt).toEqual(prompt);

    // 他にユーザーが登録されていないことを確認
    const users = await getUsers(source);
    expect(users.length).toEqual(2);
  });

  it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author; 暗号化無し)', async () => {
    const module = await makeTestingModule(source);
    const service = module.get<UsersService>(UsersService);
    const adminExternalId = 'ADMIN0001';
    const {
      accountId,
      role: adminRole,
      tier,
    } = await createAccountAndAdminUser(source, adminExternalId);

    const token: AccessToken = {
      userId: adminExternalId,
      role: adminRole,
      tier: tier,
    };

    const name = 'test_user2';
    const role = USER_ROLES.AUTHOR;
    const email = 'test2@example.com';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;
    const authorId = 'testID';
    const encryption = false;
    const prompt = true;

    const externalId = '0001';

    overrideAdB2cService(service, {
      createUser: async (
        _context: Context,
        _email: string,
        _password: string,
        _username: string,
      ) => {
        // ユーザー作成時に指定したパラメータが正しく渡されていることを確認
        expect(email).toEqual(_email);
        expect(name).toEqual(_username);

        return { sub: externalId };
      },
    });
    overrideSendgridService(service, {
      sendMail: async () => {
        return;
      },
      createMailContentFromEmailConfirmForNormalUser: async () => {
        return { html: '', text: '', subject: '' };
      },
    });

    expect(
      await service.createUser(
        makeContext('trackingId'),
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
        authorId,
        encryption,
        undefined,
        prompt,
      ),
    ).toEqual(undefined);

    // 追加されたユーザーが正しくDBに登録されていることを確認
    const user = await getUserByExternalId(source, externalId);
    expect(user).not.toBeNull();
    expect(user.account_id).toEqual(accountId);
    expect(user.role).toEqual(role);
    expect(user.author_id).toEqual(authorId);
    expect(user.email_verified).toEqual(false);
    expect(user.auto_renew).toEqual(autoRenew);
    expect(user.license_alert).toEqual(licenseAlert);
    expect(user.notification).toEqual(notification);
    expect(user.encryption).toEqual(encryption);
    expect(user.encryption_password).toBeNull();
    expect(user.prompt).toEqual(prompt);

    // 他にユーザーが登録されていないことを確認
    const users = await getUsers(source);
    expect(users.length).toEqual(2);
  });

  it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Transcriptioninst)', async () => {
    const module = await makeTestingModule(source);
    const service = module.get<UsersService>(UsersService);
    const adminExternalId = 'ADMIN0001';
    const {
      accountId,
      role: adminRole,
      tier,
    } = await createAccountAndAdminUser(source, adminExternalId);

    const token: AccessToken = {
      userId: adminExternalId,
      role: adminRole,
      tier: tier,
    };

    const name = 'test_user3';
    const role = USER_ROLES.TYPIST;
    const email = 'test3@example.com';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;

    const externalId = '0001';

    overrideAdB2cService(service, {
      createUser: async (
        _context: Context,
        _email: string,
        _password: string,
        _username: string,
      ) => {
        // ユーザー作成時に指定したパラメータが正しく渡されていることを確認
        expect(email).toEqual(_email);
        expect(name).toEqual(_username);

        return { sub: externalId };
      },
    });
    overrideSendgridService(service, {
      sendMail: async () => {
        return;
      },
      createMailContentFromEmailConfirmForNormalUser: async () => {
        return { html: '', text: '', subject: '' };
      },
    });

    expect(
      await service.createUser(
        makeContext('trackingId'),
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
      ),
    ).toEqual(undefined);

    // 追加されたユーザーが正しくDBに登録されていることを確認
    const user = await getUserByExternalId(source, externalId);
    expect(user).not.toBeNull();
    expect(user.account_id).toEqual(accountId);
    expect(user.role).toEqual(role);
    expect(user.author_id).toBeNull();
    expect(user.email_verified).toEqual(false);
    expect(user.auto_renew).toEqual(autoRenew);
    expect(user.license_alert).toEqual(licenseAlert);
    expect(user.notification).toEqual(notification);
    expect(user.encryption).toEqual(false);
    expect(user.encryption_password).toBeNull();
    expect(user.prompt).toEqual(false);

    // 他にユーザーが登録されていないことを確認
    const users = await getUsers(source);
    expect(users.length).toEqual(2);
  });

  it('DBネットワークエラーとなる場合、リカバリ処理を実施し、ADB2Cに作成したユーザーを削除する', async () => {
    const module = await makeTestingModule(source);
    const service = module.get<UsersService>(UsersService);
    const b2cService = module.get<AdB2cService>(AdB2cService);
    const adminExternalId = 'ADMIN0001';
    const { role: adminRole, tier } = await createAccountAndAdminUser(
      source,
      adminExternalId,
    );

    const token: AccessToken = {
      userId: adminExternalId,
      role: adminRole,
      tier: tier,
    };

    const name = 'test_user1';
    const role = USER_ROLES.NONE;
    const email = 'test1@example.com';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;

    const externalId = '0001';

    overrideAdB2cService(service, {
      createUser: async (
        _context: Context,
        _email: string,
        _password: string,
        _username: string,
      ) => {
        // ユーザー作成時に指定したパラメータが正しく渡されていることを確認
        expect(email).toEqual(_email);
        expect(name).toEqual(_username);

        return { sub: externalId };
      },
      deleteUser: jest.fn(),
    });
    overrideSendgridService(service, {
      sendMail: async () => {
        return;
      },
      createMailContentFromEmailConfirmForNormalUser: async () => {
        return { html: '', text: '', subject: '' };
      },
    });

    // DBエラーを発生させる
    overrideUsersRepositoryService(service, {
      createNormalUser: async () => {
        throw new Error('DB error');
      },
    });

    try {
      await service.createUser(
        makeContext('trackingId'),
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
      );
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
        expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
      } else {
        fail();
      }
    }
    // ADB2Cに作成したユーザーを削除するメソッドが呼ばれていることを確認
    expect(b2cService.deleteUser).toBeCalledWith(
      externalId,
      makeContext('trackingId'),
    );
  });

  it('DBネットワークエラーとなる場合、リカバリ処理を実施されるが、そのリカバリ処理に失敗した場合、ADB2Cのユーザーは削除されない', async () => {
    const module = await makeTestingModule(source);
    const service = module.get<UsersService>(UsersService);
    const b2cService = module.get<AdB2cService>(AdB2cService);
    const adminExternalId = 'ADMIN0001';
    const {
      accountId,
      role: adminRole,
      tier,
    } = await createAccountAndAdminUser(source, adminExternalId);

    const token: AccessToken = {
      userId: adminExternalId,
      role: adminRole,
      tier: tier,
    };

    const name = 'test_user1';
    const role = USER_ROLES.NONE;
    const email = 'test1@example.com';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;

    const externalId = '0001';

    overrideAdB2cService(service, {
      createUser: async (
        _context: Context,
        _email: string,
        _password: string,
        _username: string,
      ) => {
        // ユーザー作成時に指定したパラメータが正しく渡されていることを確認
        expect(email).toEqual(_email);
        expect(name).toEqual(_username);

        return { sub: externalId };
      },
      deleteUser: jest.fn().mockRejectedValue(new Error('ADB2C error')),
    });
    overrideSendgridService(service, {
      sendMail: async () => {
        return;
      },
      createMailContentFromEmailConfirmForNormalUser: async () => {
        return { html: '', text: '', subject: '' };
      },
    });

    // DBエラーを発生させる
    overrideUsersRepositoryService(service, {
      createNormalUser: async () => {
        throw new Error('DB error');
      },
    });

    try {
      await service.createUser(
        makeContext('trackingId'),
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
      );
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
        expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
      } else {
        fail();
      }
    }
    // 新規ユーザーが登録されていないことを確認
    const users = await getUsers(source);
    expect(users.length).toEqual(1);
    //アカウントIDがテスト用の管理者ユーザーのものであることを確認
    expect(users[0].account_id).toEqual(accountId);
    // ADB2Cに作成したユーザーを削除するメソッドが呼ばれていることを確認
    expect(b2cService.deleteUser).toBeCalledWith(
      externalId,
      makeContext('trackingId'),
    );
  });

  it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。', async () => {
    const module = await makeTestingModule(source);
    const service = module.get<UsersService>(UsersService);
    const adminExternalId = 'ADMIN0001';
    const { role: adminRole, tier } = await createAccountAndAdminUser(
      source,
      adminExternalId,
    );

    const token: AccessToken = {
      userId: adminExternalId,
      role: adminRole,
      tier: tier,
    };

    const name = 'test_user1';
    const role = USER_ROLES.NONE;
    const email = 'test1@example.com';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;

    overrideAdB2cService(service, {
      createUser: async (
        _context: Context,
        _email: string,
        _password: string,
        _username: string,
      ) => {
        // ユーザー作成時に指定したパラメータが正しく渡されていることを確認
        expect(email).toEqual(_email);
        expect(name).toEqual(_username);

        throw new Error('ADB2C error');
      },
    });
    overrideSendgridService(service, {
      sendMail: async () => {
        return;
      },
      createMailContentFromEmailConfirmForNormalUser: async () => {
        return { html: '', text: '', subject: '' };
      },
    });

    try {
      await service.createUser(
        makeContext('trackingId'),
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
      );
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
        expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
      } else {
        fail();
      }
    }

    // ユーザーが登録されていないことを確認
    const users = await getUsers(source);
    expect(users.length).toEqual(1);
  });

  it('Azure AD B2C内でメールアドレスが重複している場合、エラーとなる。', async () => {
    const module = await makeTestingModule(source);
    const service = module.get<UsersService>(UsersService);
    const adminExternalId = 'ADMIN0001';
    const { role: adminRole, tier } = await createAccountAndAdminUser(
      source,
      adminExternalId,
    );

    const token: AccessToken = {
      userId: adminExternalId,
      role: adminRole,
      tier: tier,
    };

    const name = 'test_user1';
    const role = USER_ROLES.NONE;
    const email = 'test1@example.com';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;

    overrideAdB2cService(service, {
      createUser: async (
        _context: Context,
        _email: string,
        _password: string,
        _username: string,
      ) => {
        // ユーザー作成時に指定したパラメータが正しく渡されていることを確認
        expect(email).toEqual(_email);
        expect(name).toEqual(_username);

        // Conflictエラーを返す
        return {
          reason: 'email',
          message: 'The email address is already in use by another account.',
        };
      },
    });
    overrideSendgridService(service, {
      sendMail: async () => {
        return;
      },
      createMailContentFromEmailConfirmForNormalUser: async () => {
        return { html: '', text: '', subject: '' };
      },
    });

    try {
      await service.createUser(
        makeContext('trackingId'),
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
      );
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010301'));
      } else {
        fail();
      }
    }

    // ユーザーが登録されていないことを確認
    const users = await getUsers(source);
    expect(users.length).toEqual(1);
  });

  it('AuthorIDが重複している場合、エラーとなる。(AuthorID重複チェックでエラー)', async () => {
    const module = await makeTestingModule(source);
    const service = module.get<UsersService>(UsersService);
    const adminExternalId = 'ADMIN0001';
    const { role: adminRole, tier } = await createAccountAndAdminUser(
      source,
      adminExternalId,
    );

    const token: AccessToken = {
      userId: adminExternalId,
      role: adminRole,
      tier: tier,
    };

    const name = 'test_user2';
    const role = USER_ROLES.AUTHOR;
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;
    const authorId = 'testID';
    const encryption = true;
    const prompt = true;
    const encryptionPassword = 'testPassword';

    const email_1 = 'test_1@example.com';
    const externalId_1 = '0001';

    overrideAdB2cService(service, {
      createUser: async (
        _context: Context,
        _email: string,
        _password: string,
        _username: string,
      ) => {
        // ユーザー作成時に指定したパラメータが正しく渡されていることを確認
        expect(email_1).toEqual(_email);
        expect(name).toEqual(_username);

        return { sub: externalId_1 };
      },
    });
    overrideSendgridService(service, {
      sendMail: async () => {
        return;
      },
      createMailContentFromEmailConfirmForNormalUser: async () => {
        return { html: '', text: '', subject: '' };
      },
    });

    expect(
      await service.createUser(
        makeContext('trackingId'),
        token,
        name,
        role,
        email_1,
        autoRenew,
        licenseAlert,
        notification,
        authorId,
        encryption,
        encryptionPassword,
        prompt,
      ),
    ).toEqual(undefined);

    {
      // 他にユーザーが登録されていないことを確認
      const users = await getUsers(source);
      expect(users.length).toEqual(2);
    }

    // Azure Ad B2CのMockをユーザー2用に切り替える
    const email_2 = 'test_1@example.com';
    const externalId_2 = '0001';
    overrideAdB2cService(service, {
      createUser: async (
        _context: Context,
        _email: string,
        _password: string,
        _username: string,
      ) => {
        // ユーザー作成時に指定したパラメータが正しく渡されていることを確認
        expect(email_2).toEqual(_email);
        expect(name).toEqual(_username);

        return { sub: externalId_2 };
      },
    });

    try {
      await service.createUser(
        makeContext('trackingId'),
        token,
        name,
        role,
        email_2,
        autoRenew,
        licenseAlert,
        notification,
        authorId,
        encryption,
        encryptionPassword,
        prompt,
      );
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010302'));
      } else {
        fail();
      }
    }

    // 新規にユーザーが登録されていないことを確認
    {
      const users = await getUsers(source);
      expect(users.length).toEqual(2);
    }
  });

  it('AuthorIDが重複している場合、エラー(insert失敗)となり、リカバリ処理が実行され、ADB2Cに追加したユーザーが削除される', async () => {
    const module = await makeTestingModule(source);
    const service = module.get<UsersService>(UsersService);
    const b2cService = module.get<AdB2cService>(AdB2cService);
    const adminExternalId = 'ADMIN0001';
    const {
      accountId,
      role: adminRole,
      tier,
    } = await createAccountAndAdminUser(source, adminExternalId);

    const token: AccessToken = {
      userId: adminExternalId,
      role: adminRole,
      tier: tier,
    };

    const name = 'test_user2';
    const role = USER_ROLES.AUTHOR;
    const email = 'test2@example.com';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;
    const authorId = 'testID';
    const encryption = true;
    const prompt = true;
    const encryptionPassword = 'testPassword';

    const externalId = '0001';

    overrideAdB2cService(service, {
      createUser: async (
        _context: Context,
        _email: string,
        _password: string,
        _username: string,
      ) => {
        // ユーザー作成時に指定したパラメータが正しく渡されていることを確認
        expect(email).toEqual(_email);
        expect(name).toEqual(_username);

        return { sub: externalId };
      },
      deleteUser: jest.fn(),
    });
    overrideSendgridService(service, {
      sendMail: async () => {
        return;
      },
      createMailContentFromEmailConfirmForNormalUser: async () => {
        return { html: '', text: '', subject: '' };
      },
    });

    // AuthorIDのUNIQUE制約エラーを発生させる
    overrideUsersRepositoryService(service, {
      createNormalUser: async () => {
        throw { code: 'ER_DUP_ENTRY' };
      },
    });

    try {
      await service.createUser(
        makeContext('trackingId'),
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
        authorId,
        encryption,
        encryptionPassword,
        prompt,
      );
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010302'));
      } else {
        fail();
      }
    }
    // 新規にユーザーが登録されていないことを確認
    const users = await getUsers(source);
    expect(users.length).toEqual(1);
    expect(users[0].account_id).toEqual(accountId);
    // ADB2Cに作成したユーザーを削除するメソッドが呼ばれていることを確認
    expect(b2cService.deleteUser).toBeCalledWith(
      externalId,
      makeContext('trackingId'),
    );
  });

  it('メール送信に失敗した場合、リカバリ処理が実行され、ADB2C,DBのユーザーが削除される', async () => {
    const module = await makeTestingModule(source);
    const service = module.get<UsersService>(UsersService);
    const b2cService = module.get<AdB2cService>(AdB2cService);

    const adminExternalId = 'ADMIN0001';
    const {
      accountId,
      role: adminRole,
      tier,
    } = await createAccountAndAdminUser(source, adminExternalId);

    const token: AccessToken = {
      userId: adminExternalId,
      role: adminRole,
      tier: tier,
    };

    const name = 'test_user1';
    const role = USER_ROLES.NONE;
    const email = 'test1@example.com';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;

    const externalId = '0001';

    overrideAdB2cService(service, {
      createUser: async (
        _context: Context,
        _email: string,
        _password: string,
        _username: string,
      ) => {
        // ユーザー作成時に指定したパラメータが正しく渡されていることを確認
        expect(email).toEqual(_email);
        expect(name).toEqual(_username);

        return { sub: externalId };
      },
      deleteUser: jest.fn(),
    });
    overrideSendgridService(service, {
      sendMail: async () => {
        throw new Error();
      },
      createMailContentFromEmailConfirmForNormalUser: async () => {
        return { html: '', text: '', subject: '' };
      },
    });

    try {
      await service.createUser(
        makeContext('trackingId'),
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
      );
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
        expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
      } else {
        fail();
      }
    }

    // 新規ユーザーが登録されていないことを確認
    const users = await getUsers(source);
    expect(users.length).toEqual(1);
    //アカウントIDがテスト用の管理者ユーザーのものであることを確認
    expect(users[0].account_id).toEqual(accountId);
    // ADB2Cに作成したユーザーを削除するメソッドが呼ばれていることを確認
    expect(b2cService.deleteUser).toBeCalledWith(
      externalId,
      makeContext('trackingId'),
    );
  });

  it('メール送信に失敗した場合、リカバリ処理が実行されるが、そのリカバリ処理に失敗した場合、ADB2C,DBのユーザーが削除されない', async () => {
    const module = await makeTestingModule(source);
    const service = module.get<UsersService>(UsersService);
    const b2cService = module.get<AdB2cService>(AdB2cService);

    const adminExternalId = 'ADMIN0001';
    const { role: adminRole, tier } = await createAccountAndAdminUser(
      source,
      adminExternalId,
    );

    const token: AccessToken = {
      userId: adminExternalId,
      role: adminRole,
      tier: tier,
    };

    const name = 'test_user1';
    const role = USER_ROLES.NONE;
    const email = 'test1@example.com';
    const autoRenew = true;
    const licenseAlert = true;
    const notification = true;

    const externalId = '0001';

    overrideAdB2cService(service, {
      createUser: async (
        _context: Context,
        _email: string,
        _password: string,
        _username: string,
      ) => {
        // ユーザー作成時に指定したパラメータが正しく渡されていることを確認
        expect(email).toEqual(_email);
        expect(name).toEqual(_username);

        return { sub: externalId };
      },
      deleteUser: jest.fn().mockRejectedValue(new Error()),
    });
    overrideSendgridService(service, {
      sendMail: async () => {
        throw new Error();
      },
      createMailContentFromEmailConfirmForNormalUser: async () => {
        return { html: '', text: '', subject: '' };
      },
    });
    overrideUsersRepositoryService(service, {
      deleteNormalUser: async () => {
        throw new Error();
      },
    });

    try {
      await service.createUser(
        makeContext('trackingId'),
        token,
        name,
        role,
        email,
        autoRenew,
        licenseAlert,
        notification,
      );
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
        expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
      } else {
        fail();
      }
    }

    // リカバリ処理が失敗したため、DBのユーザーが削除されないことを確認
    const users = await getUsers(source);
    expect(users.length).toEqual(2);
    // ADB2Cに作成したユーザーを削除するメソッドが呼ばれていることを確認
    expect(b2cService.deleteUser).toBeCalledWith(
      externalId,
      makeContext('trackingId'),
    );
  });
});

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 licensesRepositoryMockValue = null;
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      licensesRepositoryMockValue,
      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 licensesRepositoryMockValue = null;
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();

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

    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      licensesRepositoryMockValue,
      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 licensesRepositoryMockValue = null;
    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,
      licensesRepositoryMockValue,
      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 licensesRepositoryMockValue = null;
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendgridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    const service = await makeUsersServiceMock(
      usersRepositoryMockValue,
      licensesRepositoryMockValue,
      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 licensesRepositoryMockValue = null;
    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,
      licensesRepositoryMockValue,
      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 licensesRepositoryMockValue = null;
    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,
      licensesRepositoryMockValue,
      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,
      ),
    );
  });
});

describe('UsersService.updateUser', () => {
  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('ユーザー情報を更新できる（None）', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { externalId: external_id } = await createUser(
      source,
      accountId,
      'external_id',
      USER_ROLES.NONE,
      undefined,
      true,
    );
    const { userId: user1 } = await createUser(
      source,
      accountId,
      'external_id1',
      USER_ROLES.NONE,
      undefined,
      true,
    );

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

    expect(
      await service.updateUser(
        { trackingId: 'trackingId' },
        external_id,
        user1,
        USER_ROLES.NONE,
        undefined,
        false,
        false,
        false,
        undefined,
        undefined,
        undefined,
      ),
    ).toEqual(undefined);

    const createdUser = await getUser(source, user1);

    expect(createdUser.id).toBe(user1);
    expect(createdUser.role).toBe(USER_ROLES.NONE);
    expect(createdUser.author_id).toBeNull();
    expect(createdUser.auto_renew).toBe(false);
    expect(createdUser.license_alert).toBe(false);
    expect(createdUser.notification).toBe(false);
    expect(createdUser.encryption).toBe(false);
    expect(createdUser.encryption_password).toBeNull();
    expect(createdUser.prompt).toBe(false);
  });

  it('ユーザー情報を更新できる（Typist）', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { externalId: external_id } = await createUser(
      source,
      accountId,
      'external_id',
      USER_ROLES.NONE,
      undefined,
      true,
    );
    const { userId: user1 } = await createUser(
      source,
      accountId,
      'external_id1',
      USER_ROLES.TYPIST,
      undefined,
      true,
    );

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

    expect(
      await service.updateUser(
        { trackingId: 'trackingId' },
        external_id,
        user1,
        USER_ROLES.TYPIST,
        undefined,
        false,
        false,
        false,
        undefined,
        undefined,
        undefined,
      ),
    ).toEqual(undefined);

    const createdUser = await getUser(source, user1);

    expect(createdUser.id).toBe(user1);
    expect(createdUser.role).toBe(USER_ROLES.TYPIST);
    expect(createdUser.author_id).toBeNull();
    expect(createdUser.auto_renew).toBe(false);
    expect(createdUser.license_alert).toBe(false);
    expect(createdUser.notification).toBe(false);
    expect(createdUser.encryption).toBe(false);
    expect(createdUser.encryption_password).toBeNull();
    expect(createdUser.prompt).toBe(false);
  });

  it('ユーザー情報を更新できる（Author）', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { externalId: external_id } = await createUser(
      source,
      accountId,
      'external_id',
      USER_ROLES.NONE,
      undefined,
      true,
    );
    const { userId: user1 } = await createUser(
      source,
      accountId,
      'external_id1',
      USER_ROLES.AUTHOR,
      undefined,
      true,
      true,
      'password',
      true,
    );

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

    expect(
      await service.updateUser(
        { trackingId: 'trackingId' },
        external_id,
        user1,
        USER_ROLES.AUTHOR,
        'AUTHOR_ID',
        false,
        false,
        false,
        true,
        'new_password',
        true,
      ),
    ).toEqual(undefined);

    const createdUser = await getUser(source, user1);

    expect(createdUser.id).toBe(user1);
    expect(createdUser.role).toBe(USER_ROLES.AUTHOR);
    expect(createdUser.author_id).toBe('AUTHOR_ID');
    expect(createdUser.auto_renew).toBe(false);
    expect(createdUser.license_alert).toBe(false);
    expect(createdUser.notification).toBe(false);
    expect(createdUser.encryption).toBe(true);
    expect(createdUser.encryption_password).toBe('new_password');
    expect(createdUser.prompt).toBe(true);
  });

  it('ユーザーのRoleを更新できる（None⇒Typist）', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { externalId: external_id } = await createUser(
      source,
      accountId,
      'external_id',
      USER_ROLES.NONE,
      undefined,
      true,
    );
    const { userId: user1 } = await createUser(
      source,
      accountId,
      'external_id1',
      USER_ROLES.NONE,
      undefined,
      true,
    );

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

    expect(
      await service.updateUser(
        { trackingId: 'trackingId' },
        external_id,
        user1,
        USER_ROLES.TYPIST,
        undefined,
        false,
        false,
        false,
        undefined,
        undefined,
        undefined,
      ),
    ).toEqual(undefined);

    const createdUser = await getUser(source, user1);

    expect(createdUser.id).toBe(user1);
    expect(createdUser.role).toBe(USER_ROLES.TYPIST);
    expect(createdUser.author_id).toBeNull();
    expect(createdUser.auto_renew).toBe(false);
    expect(createdUser.license_alert).toBe(false);
    expect(createdUser.notification).toBe(false);
    expect(createdUser.encryption).toBe(false);
    expect(createdUser.encryption_password).toBeNull();
    expect(createdUser.prompt).toBe(false);
  });

  it('ユーザーのRoleを更新できる（None⇒Author）', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { externalId: external_id } = await createUser(
      source,
      accountId,
      'external_id',
      USER_ROLES.NONE,
      undefined,
      true,
    );
    const { userId: user1 } = await createUser(
      source,
      accountId,
      'external_id1',
      USER_ROLES.NONE,
      undefined,
      true,
    );

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

    expect(
      await service.updateUser(
        { trackingId: 'trackingId' },
        external_id,
        user1,
        USER_ROLES.AUTHOR,
        'AUTHOR_ID',
        false,
        false,
        false,
        false,
        undefined,
        false,
      ),
    ).toEqual(undefined);

    const createdUser = await getUser(source, user1);

    expect(createdUser.id).toBe(user1);
    expect(createdUser.role).toBe(USER_ROLES.AUTHOR);
    expect(createdUser.author_id).toBe('AUTHOR_ID');
    expect(createdUser.auto_renew).toBe(false);
    expect(createdUser.license_alert).toBe(false);
    expect(createdUser.notification).toBe(false);
    expect(createdUser.encryption).toBe(false);
    expect(createdUser.encryption_password).toBeNull();
    expect(createdUser.prompt).toBe(false);
  });

  it('None以外からRoleを変更した場合、エラーとなる（Typist⇒None）', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { externalId: external_id } = await createUser(
      source,
      accountId,
      'external_id',
      USER_ROLES.NONE,
      undefined,
      true,
    );
    const { userId: user1 } = await createUser(
      source,
      accountId,
      'external_id1',
      USER_ROLES.TYPIST,
      undefined,
      true,
    );

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

    await expect(
      service.updateUser(
        { trackingId: 'trackingId' },
        external_id,
        user1,
        USER_ROLES.NONE,
        undefined,
        false,
        false,
        false,
        undefined,
        undefined,
        undefined,
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010207'), HttpStatus.BAD_REQUEST),
    );
  });

  it('Authorがパスワードundefinedで渡されたとき、元のパスワードを維持する（Encryptionがtrue）', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { externalId: external_id } = await createUser(
      source,
      accountId,
      'external_id',
      USER_ROLES.NONE,
      undefined,
      true,
    );
    const { userId: user1 } = await createUser(
      source,
      accountId,
      'external_id1',
      USER_ROLES.AUTHOR,
      'AUTHOR_ID',
      true,
      true,
      'password',
      true,
    );

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

    expect(
      await service.updateUser(
        { trackingId: 'trackingId' },
        external_id,
        user1,
        USER_ROLES.AUTHOR,
        'AUTHOR_ID',
        false,
        false,
        false,
        true,
        undefined,
        true,
      ),
    ).toEqual(undefined);

    const createdUser = await getUser(source, user1);

    expect(createdUser.id).toBe(user1);
    expect(createdUser.role).toBe(USER_ROLES.AUTHOR);
    expect(createdUser.author_id).toBe('AUTHOR_ID');
    expect(createdUser.auto_renew).toBe(false);
    expect(createdUser.license_alert).toBe(false);
    expect(createdUser.notification).toBe(false);
    expect(createdUser.encryption).toBe(true);
    expect(createdUser.encryption_password).toBe('password');
    expect(createdUser.prompt).toBe(true);
  });

  it('Authorが暗号化なしで更新した場合、パスワードをNULLにする（Encryptionがfalse）', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { externalId: external_id } = await createUser(
      source,
      accountId,
      'external_id',
      USER_ROLES.NONE,
      undefined,
      true,
    );
    const { userId: user1 } = await createUser(
      source,
      accountId,
      'external_id1',
      USER_ROLES.AUTHOR,
      'AUTHOR_ID',
      true,
      false,
      'password',
      true,
    );

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

    expect(
      await service.updateUser(
        { trackingId: 'trackingId' },
        external_id,
        user1,
        USER_ROLES.AUTHOR,
        'AUTHOR_ID',
        false,
        false,
        false,
        false,
        'password',
        true,
      ),
    ).toEqual(undefined);

    const createdUser = await getUser(source, user1);

    expect(createdUser.id).toBe(user1);
    expect(createdUser.role).toBe(USER_ROLES.AUTHOR);
    expect(createdUser.author_id).toBe('AUTHOR_ID');
    expect(createdUser.auto_renew).toBe(false);
    expect(createdUser.license_alert).toBe(false);
    expect(createdUser.notification).toBe(false);
    expect(createdUser.encryption).toBe(false);
    expect(createdUser.encryption_password).toBeNull();
    expect(createdUser.prompt).toBe(true);
  });

  it('AuthorのDBにパスワードが設定されていない場合、パスワードundefinedでわたすとエラーとなる（Encryptionがtrue）', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { externalId: external_id } = await createUser(
      source,
      accountId,
      'external_id',
      USER_ROLES.NONE,
      undefined,
      true,
    );
    const { userId: user1 } = await createUser(
      source,
      accountId,
      'external_id1',
      USER_ROLES.AUTHOR,
      'AUTHOR_ID',
      true,
      true,
      undefined,
      true,
    );

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

    await expect(
      service.updateUser(
        { trackingId: 'trackingId' },
        external_id,
        user1,
        USER_ROLES.AUTHOR,
        'AUTHOR_ID',
        false,
        false,
        false,
        true,
        undefined,
        true,
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010208'), HttpStatus.BAD_REQUEST),
    );
  });

  it('AuthorIdが既存のユーザーと重複した場合、エラーとなる', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { externalId: external_id } = await createUser(
      source,
      accountId,
      'external_id',
      USER_ROLES.NONE,
      undefined,
      true,
    );
    const { userId: user1 } = await createUser(
      source,
      accountId,
      'external_id1',
      USER_ROLES.AUTHOR,
      'AUTHOR_ID1',
      true,
      true,
      'password',
      true,
    );

    await createUser(
      source,
      accountId,
      'external_id2',
      USER_ROLES.AUTHOR,
      'AUTHOR_ID2',
      true,
      true,
      'password',
      true,
    );

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

    await expect(
      service.updateUser(
        { trackingId: 'trackingId' },
        external_id,
        user1,
        USER_ROLES.AUTHOR,
        'AUTHOR_ID2',
        false,
        false,
        false,
        true,
        undefined,
        true,
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010302'), HttpStatus.BAD_REQUEST),
    );
  });
});
