import { HttpException, HttpStatus } from '@nestjs/common';
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 {
  createLicense,
  createUserGroup,
  getLicenses,
  makeTestingModuleWithAdb2c,
} from './test/utility';
import { DataSource } from 'typeorm';
import { UsersService } from './users.service';
import {
  LICENSE_ALLOCATED_STATUS,
  LICENSE_EXPIRATION_THRESHOLD_DAYS,
  LICENSE_TYPE,
  USER_AUDIO_FORMAT,
  USER_LICENSE_EXPIRY_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';
import {
  getUser,
  getUserFromExternalId,
  getUsers,
  makeTestAccount,
  makeTestSimpleAccount,
  makeTestUser,
} from '../../common/test/utility';
import { v4 as uuidv4 } from 'uuid';
import { createOptionItems, createWorktype } from '../accounts/test/utility';
import { createWorkflow, getWorkflows } from '../workflows/test/utility';
import { truncateAllTable } from '../../common/test/init';

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

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

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

  it('ユーザの仮登録時に払い出されるトークンにより、未認証のユーザが認証済みになり、トライアルライセンスが100件作成される', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { id: accountId } = (await makeTestAccount(source)).account;
    const { id: userId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'externalId_user1',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
      email_verified: false,
    });
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'externalId_user2',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
      email_verified: false,
    });

    const service = module.get<UsersService>(UsersService);
    let _subject: string = '';
    let _url: string | undefined = '';
    overrideSendgridService(service, {
      sendMail: async (
        context: Context,
        to: string[],
        cc: string[],
        from: string,
        subject: string,
        text: string,
        html: string,
      ) => {
        const urlPattern = /https?:\/\/[^\s]+/g;
        const urls = text.match(urlPattern);
        const url = urls?.pop();

        _subject = subject;
        _url = url;
      },
    });

    // account id:1, user id: 2のトークン
    const token =
      'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
    const context = makeContext(`uuidv4`, 'requestId');
    await service.confirmUser(context, 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,
      user: resultLicenses[0].user ?? null,
    };

    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,
      user: null,
    });
    expect(_subject).toBe('Account Registered Notification [U-101]');
    expect(_url).toBe('http://localhost:8081/');
  }, 600000);

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

  it('ユーザが既に認証済みだった場合、認証済みユーザエラーとなる。', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { id: accountId } = (await makeTestAccount(source)).account;
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'externalId_user1',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
      email_verified: true, //emailを認証済みにする
    });
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'externalId_user2',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
      email_verified: false,
    });
    const service = module.get<UsersService>(UsersService);
    overrideSendgridService(service, {});
    const token =
      'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
    const context = makeContext(`uuidv4`, 'requestId');
    await expect(service.confirmUser(context, token)).rejects.toEqual(
      new HttpException(makeErrorResponse('E010202'), HttpStatus.BAD_REQUEST),
    );
  });
  it('ユーザーが存在しない場合は、想定外のエラーとなる', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const service = module.get<UsersService>(UsersService);
    overrideSendgridService(service, {});
    const token =
      'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
    const context = makeContext(`uuidv4`, 'requestId');
    await expect(service.confirmUser(context, 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_eula_version: 'string',
      accepted_privacy_notice_version: 'string',
      accepted_dpa_version: 'string',
      email_verified: false,
      created_by: 'string;',
      created_at: new Date(),
      updated_by: 'string;',
      updated_at: new Date(),
      auto_renew: true,
      notification: true,
      encryption: false,
      prompt: false,
      account: null,
      author_id: null,
      deleted_at: null,
      encryption_password: null,
      license: null,
      userGroupMembers: null,
    };
    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', 'requestId'),
        token,
      ),
    ).toEqual(undefined);
  });

  it('トークンの形式が不正な場合、形式不正エラーとなる。(メール認証API)', async () => {
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    usersRepositoryMockValue.findUserById = {
      id: 1,
      external_id: 'TEST9999',
      account_id: 1,
      role: 'None',
      accepted_eula_version: 'string',
      accepted_privacy_notice_version: 'string',
      accepted_dpa_version: 'string',
      email_verified: false,
      created_by: 'string;',
      created_at: new Date(),
      updated_by: 'string;',
      updated_at: new Date(),
      auto_renew: true,
      notification: true,
      encryption: false,
      prompt: false,
      account: null,
      author_id: null,
      deleted_at: null,
      encryption_password: null,
      license: null,
      userGroupMembers: null,
    };
    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', 'requestId'),
        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_eula_version: 'string',
      accepted_privacy_notice_version: 'string',
      accepted_dpa_version: 'string',
      email_verified: true,
      created_by: 'string;',
      created_at: new Date(),
      updated_by: 'string;',
      updated_at: new Date(),
      auto_renew: true,
      notification: true,
      encryption: false,
      prompt: false,
      account: null,
      author_id: null,
      deleted_at: null,
      encryption_password: null,
      license: null,
      userGroupMembers: null,
    };
    const licensesRepositoryMockValue = null;
    const adb2cParam = makeDefaultAdB2cMockValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const configMockValue = makeDefaultConfigValue();
    const sortCriteriaRepositoryMockValue =
      makeDefaultSortCriteriaRepositoryMockValue();
    usersRepositoryMockValue.updateUserVerified = new EmailAlreadyVerifiedError(
      `Email already verified user`,
    );

    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', 'requestId'),
        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_eula_version: 'string',
      accepted_privacy_notice_version: 'string',
      accepted_dpa_version: 'string',
      email_verified: false,
      created_by: 'string;',
      created_at: new Date(),
      updated_by: 'string;',
      updated_at: new Date(),
      auto_renew: true,
      notification: true,
      encryption: false,
      prompt: false,
      account: null,
      author_id: null,
      deleted_at: null,
      encryption_password: null,
      license: null,
      userGroupMembers: null,
    };
    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', 'requestId'),
        token,
      ),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
});

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

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

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

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

    const { account } = await makeTestAccount(
      source,
      {},
      { external_id: adminExternalId },
    );
    const { id: accountId } = account;

    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 };
      },
      getUser: async () => {
        return {
          id: adminExternalId,
          displayName: 'admin',
        };
      },
    });
    let _subject: string = '';
    let _url: string | undefined = '';
    overrideSendgridService(service, {
      sendMail: async (
        context: Context,
        to: string[],
        cc: string[],
        from: string,
        subject: string,
        text: string,
        html: string,
      ) => {
        const urlPattern = /https?:\/\/[^\s]+/g;
        const urls = text.match(urlPattern);
        const url = urls?.pop();

        _subject = subject;
        _url = url;
      },
    });

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

    // 追加されたユーザーが正しくDBに登録されていることを確認
    const user = await getUserFromExternalId(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?.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);

    expect(_subject).toBe('User Registration Notification [U-114]');
    expect(
      _url?.startsWith('http://localhost:8081/mail-confirm/user?verify='),
    ).toBeTruthy();
  });

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

    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 };
      },
      getUser: async () => {
        return {
          id: adminExternalId,
          displayName: 'admin',
        };
      },
    });
    overrideSendgridService(service, {});

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

    // 追加されたユーザーが正しくDBに登録されていることを確認
    const user = await getUserFromExternalId(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?.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 () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const service = module.get<UsersService>(UsersService);
    const adminExternalId = 'ADMIN0001';
    const { account } = await makeTestAccount(
      source,
      {},
      { external_id: adminExternalId },
    );
    const { id: accountId } = account;

    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 };
      },
      getUser: async () => {
        return {
          id: adminExternalId,
          displayName: 'admin',
        };
      },
    });
    overrideSendgridService(service, {});

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

    // 追加されたユーザーが正しくDBに登録されていることを確認
    const user = await getUserFromExternalId(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?.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 () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const service = module.get<UsersService>(UsersService);
    const adminExternalId = 'ADMIN0001';
    const { account } = await makeTestAccount(
      source,
      {},
      { external_id: adminExternalId },
    );
    const { id: accountId } = account;

    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 };
      },
      getUser: async () => {
        return {
          id: adminExternalId,
          displayName: 'admin',
        };
      },
    });
    overrideSendgridService(service, {});

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

    // 追加されたユーザーが正しくDBに登録されていることを確認
    const user = await getUserFromExternalId(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?.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 () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const service = module.get<UsersService>(UsersService);
    const b2cService = module.get<AdB2cService>(AdB2cService);
    const adminExternalId = 'ADMIN0001';
    await makeTestAccount(source, {}, { external_id: adminExternalId });

    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(),
      getUser: async () => {
        return {
          id: adminExternalId,
          displayName: 'admin',
        };
      },
    });
    overrideSendgridService(service, {});

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

    try {
      await service.createUser(
        makeContext('trackingId', 'requestId'),
        adminExternalId,
        name,
        role,
        email,
        autoRenew,
        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', 'requestId'),
    );
  });

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

    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')),
      getUser: async () => {
        return {
          id: adminExternalId,
          displayName: 'admin',
        };
      },
    });
    overrideSendgridService(service, {});

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

    try {
      await service.createUser(
        makeContext('trackingId', 'requestId'),
        adminExternalId,
        name,
        role,
        email,
        autoRenew,
        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', 'requestId'),
    );
  });

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

    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, {});

    try {
      await service.createUser(
        makeContext('trackingId', 'requestId'),
        adminExternalId,
        name,
        role,
        email,
        autoRenew,
        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 () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const service = module.get<UsersService>(UsersService);
    const adminExternalId = 'ADMIN0001';
    await makeTestAccount(source, {}, { external_id: adminExternalId });

    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, {});

    try {
      await service.createUser(
        makeContext('trackingId', 'requestId'),
        adminExternalId,
        name,
        role,
        email,
        autoRenew,
        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 () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const service = module.get<UsersService>(UsersService);
    const adminExternalId = 'ADMIN0001';
    await makeTestAccount(source, {}, { external_id: adminExternalId });

    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 };
      },
      getUser: async () => {
        return {
          id: adminExternalId,
          displayName: 'admin',
        };
      },
    });
    overrideSendgridService(service, {});

    expect(
      await service.createUser(
        makeContext('trackingId', 'requestId'),
        adminExternalId,
        name,
        role,
        email_1,
        autoRenew,
        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', 'requestId'),
        adminExternalId,
        name,
        role,
        email_2,
        autoRenew,
        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 () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const service = module.get<UsersService>(UsersService);
    const b2cService = module.get<AdB2cService>(AdB2cService);
    const adminExternalId = 'ADMIN0001';
    const { account } = await makeTestAccount(
      source,
      {},
      { external_id: adminExternalId },
    );
    const { id: accountId } = account;

    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, {});

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

    try {
      await service.createUser(
        makeContext('trackingId', 'requestId'),
        adminExternalId,
        name,
        role,
        email,
        autoRenew,
        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', 'requestId'),
    );
  });

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

    const adminExternalId = 'ADMIN0001';
    const { account } = await makeTestAccount(
      source,
      {},
      { external_id: adminExternalId },
    );
    const { id: accountId } = account;

    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, {});

    try {
      await service.createUser(
        makeContext('trackingId', 'requestId'),
        adminExternalId,
        name,
        role,
        email,
        autoRenew,
        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', 'requestId'),
    );
  });

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

    const adminExternalId = 'ADMIN0001';
    await makeTestAccount(source, {}, { external_id: adminExternalId });

    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();
      },
    });
    overrideUsersRepositoryService(service, {
      deleteNormalUser: async () => {
        throw new Error();
      },
    });

    try {
      await service.createUser(
        makeContext('trackingId', 'requestId'),
        adminExternalId,
        name,
        role,
        email,
        autoRenew,
        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', 'requestId'),
    );
  });
});

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

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

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

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

    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: typistUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id2',
      role: 'typist',
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });
    const { external_id: externalId_author, id: authorUserId } =
      await makeTestUser(source, {
        account_id: accountId,
        external_id: 'external_id1',
        role: 'author',
        author_id: 'AUTHOR_ID',
        auto_renew: true,
        encryption: false,
        encryption_password: undefined,
        prompt: false,
      });

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

    const { id: noneUserId } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id3',
      role: 'none',
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });

    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,
        notification: true,
        encryption: false,
        prompt: false,
        expiration: undefined,
        remaining: undefined,
        licenseStatus: USER_LICENSE_EXPIRY_STATUS.NO_LICENSE,
      },
      {
        id: typistUserId,
        name: 'test2',
        role: 'typist',
        authorId: undefined,
        typistGroupName: ['group1'],
        email: 'test2@mail.com',
        emailVerified: true,
        autoRenew: true,
        notification: true,
        encryption: false,
        prompt: false,
        expiration: undefined,
        remaining: undefined,
        licenseStatus: USER_LICENSE_EXPIRY_STATUS.NO_LICENSE,
      },
      {
        id: noneUserId,
        name: 'test3',
        role: 'none',
        authorId: undefined,
        typistGroupName: [],
        email: 'test3@mail.com',
        emailVerified: true,
        autoRenew: true,
        notification: true,
        encryption: false,
        prompt: false,
        expiration: undefined,
        remaining: undefined,
        licenseStatus: USER_LICENSE_EXPIRY_STATUS.NO_LICENSE,
      },
    ];

    const context = makeContext(`uuidv4`, 'requestId');
    expect(await service.getUsers(context, externalId_author)).toEqual(
      expectedUsers,
    );
  });

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

    const { id: accountId } = await makeTestSimpleAccount(source);
    const { id: user1, external_id: external_id1 } = await makeTestUser(
      source,
      {
        account_id: accountId,
        external_id: 'external_id1',
        role: 'author',
        author_id: 'AUTHOR_ID1',
        auto_renew: true,
        encryption: false,
        encryption_password: undefined,
        prompt: false,
      },
    );
    const { id: user2 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id2',
      role: 'author',
      author_id: 'AUTHOR_ID2',
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });
    const { id: user3 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id3',
      role: 'author',
      author_id: 'AUTHOR_ID3',
      auto_renew: false,
      encryption: false,
      encryption_password: undefined,
      prompt: 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,
        notification: true,
        encryption: false,
        prompt: false,
        expiration: `${date1.getFullYear()}/${
          date1.getMonth() + 1
        }/${date1.getDate()}`,
        remaining: LICENSE_EXPIRATION_THRESHOLD_DAYS + 1,
        licenseStatus: USER_LICENSE_EXPIRY_STATUS.NORMAL,
      },
      {
        id: user2,
        name: 'test2',
        role: 'author',
        authorId: 'AUTHOR_ID2',
        typistGroupName: [],
        email: 'test2@mail.com',
        emailVerified: true,
        autoRenew: true,
        notification: true,
        encryption: false,
        prompt: false,
        expiration: `${date2.getFullYear()}/${
          date2.getMonth() + 1
        }/${date2.getDate()}`,
        remaining: LICENSE_EXPIRATION_THRESHOLD_DAYS,
        licenseStatus: USER_LICENSE_EXPIRY_STATUS.RENEW,
      },
      {
        id: user3,
        name: 'test3',
        role: 'author',
        authorId: 'AUTHOR_ID3',
        typistGroupName: [],
        email: 'test3@mail.com',
        emailVerified: true,
        autoRenew: false,
        notification: true,
        encryption: false,
        prompt: false,
        expiration: `${date3.getFullYear()}/${
          date3.getMonth() + 1
        }/${date3.getDate()}`,
        remaining: LICENSE_EXPIRATION_THRESHOLD_DAYS - 1,
        licenseStatus: USER_LICENSE_EXPIRY_STATUS.ALERT,
      },
    ];

    const context = makeContext(`uuidv4`, 'requestId');
    expect(await service.getUsers(context, external_id1)).toEqual(
      expectedUsers,
    );
  });

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

    const { id: accountId } = await makeTestSimpleAccount(source);
    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id1',
      role: 'author',
      author_id: 'AUTHOR_ID',
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });

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

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

    const { id: accountId } = await makeTestSimpleAccount(source);
    const { external_id: externalId_author } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id1',
      role: 'author',
      author_id: 'AUTHOR_ID',
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });

    const context = makeContext(`uuidv4`, 'requestId');
    const service = module.get<UsersService>(UsersService);
    await expect(service.getUsers(context, 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,
    );
    const context = makeContext(`uuidv4`, 'requestId');

    expect(
      await service.updateSortCriteria(
        context,
        'AUTHOR_ID',
        'ASC',
        'external_id',
      ),
    ).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,
    );
    const context = makeContext(`uuidv4`, 'requestId');

    await expect(
      service.updateSortCriteria(context, 'AUTHOR_ID', 'ASC', 'external_id'),
    ).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,
    );
    const context = makeContext(`uuidv4`, 'requestId');

    await expect(
      service.updateSortCriteria(context, 'AUTHOR_ID', 'ASC', 'external_id'),
    ).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,
    );
    const context = makeContext(`uuidv4`, 'requestId');

    expect(await service.getSortCriteria(context, 'external_id')).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,
    );
    const context = makeContext(`uuidv4`, 'requestId');

    await expect(
      service.getSortCriteria(context, 'external_id'),
    ).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,
    );
    const context = makeContext(`uuidv4`, 'requestId');

    await expect(
      service.getSortCriteria(context, 'external_id'),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
});

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

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

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

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

    const { id: accountId } = await makeTestSimpleAccount(source);
    const { external_id: external_id } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });
    const { id: user1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id1',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });

    const service = module.get<UsersService>(UsersService);
    overrideSendgridService(service, {});
    const context = makeContext(`uuidv4`, 'requestId');

    expect(
      await service.updateUser(
        context,
        external_id,
        user1,
        USER_ROLES.NONE,
        undefined,
        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?.notification).toBe(false);
    expect(createdUser?.encryption).toBe(false);
    expect(createdUser?.encryption_password).toBeNull();
    expect(createdUser?.prompt).toBe(false);
  });

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

    const { id: accountId } = await makeTestSimpleAccount(source);
    const { external_id: external_id } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });
    const { id: user1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id1',
      role: USER_ROLES.TYPIST,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });

    const service = module.get<UsersService>(UsersService);
    overrideSendgridService(service, {});
    const context = makeContext(`uuidv4`, 'requestId');

    expect(
      await service.updateUser(
        context,
        external_id,
        user1,
        USER_ROLES.TYPIST,
        undefined,
        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?.notification).toBe(false);
    expect(createdUser?.encryption).toBe(false);
    expect(createdUser?.encryption_password).toBeNull();
    expect(createdUser?.prompt).toBe(false);
  });

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

    const { id: accountId } = await makeTestSimpleAccount(source);
    const { external_id: external_id } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });
    const { id: user1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id1',
      role: USER_ROLES.AUTHOR,
      author_id: undefined,
      auto_renew: true,
      encryption: true,
      encryption_password: 'password',
      prompt: true,
    });

    const service = module.get<UsersService>(UsersService);
    overrideSendgridService(service, {});
    const context = makeContext(`uuidv4`, 'requestId');

    expect(
      await service.updateUser(
        context,
        external_id,
        user1,
        USER_ROLES.AUTHOR,
        'AUTHOR_ID',
        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?.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 () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();

    const { id: accountId } = await makeTestSimpleAccount(source);
    const { external_id: external_id } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });
    const { id: user1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id1',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });

    const service = module.get<UsersService>(UsersService);
    overrideSendgridService(service, {});
    const context = makeContext(`uuidv4`, 'requestId');

    expect(
      await service.updateUser(
        context,
        external_id,
        user1,
        USER_ROLES.TYPIST,
        undefined,
        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?.notification).toBe(false);
    expect(createdUser?.encryption).toBe(false);
    expect(createdUser?.encryption_password).toBeNull();
    expect(createdUser?.prompt).toBe(false);
  });

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

    const { id: accountId } = await makeTestSimpleAccount(source);
    const { external_id: external_id } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });
    const { id: user1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id1',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });

    const service = module.get<UsersService>(UsersService);
    overrideSendgridService(service, {});
    const context = makeContext(`uuidv4`, 'requestId');

    expect(
      await service.updateUser(
        context,
        external_id,
        user1,
        USER_ROLES.AUTHOR,
        'AUTHOR_ID',
        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?.notification).toBe(false);
    expect(createdUser?.encryption).toBe(false);
    expect(createdUser?.encryption_password).toBeNull();
    expect(createdUser?.prompt).toBe(false);
  });

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

    const { id: accountId } = await makeTestSimpleAccount(source);
    const { external_id: external_id } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });
    const { id: user1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id1',
      role: USER_ROLES.TYPIST,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });

    const service = module.get<UsersService>(UsersService);
    overrideSendgridService(service, {});
    const context = makeContext(`uuidv4`, 'requestId');

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

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

    const { id: accountId } = await makeTestSimpleAccount(source);
    const { external_id: external_id } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });
    const { id: user1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id1',
      role: USER_ROLES.AUTHOR,
      author_id: 'AUTHOR_ID',
      auto_renew: true,
      encryption: true,
      encryption_password: 'password',
      prompt: true,
    });

    const service = module.get<UsersService>(UsersService);
    overrideSendgridService(service, {});
    const context = makeContext(`uuidv4`, 'requestId');

    expect(
      await service.updateUser(
        context,
        external_id,
        user1,
        USER_ROLES.AUTHOR,
        'AUTHOR_ID',
        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?.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 () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();

    const { id: accountId } = await makeTestSimpleAccount(source);
    const { external_id: external_id } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });
    const { id: user1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id1',
      role: USER_ROLES.AUTHOR,
      author_id: 'AUTHOR_ID',
      auto_renew: true,
      encryption: false,
      encryption_password: 'password',
      prompt: true,
    });

    const service = module.get<UsersService>(UsersService);
    overrideSendgridService(service, {});
    const context = makeContext(`uuidv4`, 'requestId');

    expect(
      await service.updateUser(
        context,
        external_id,
        user1,
        USER_ROLES.AUTHOR,
        'AUTHOR_ID',
        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?.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 () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();

    const { id: accountId } = await makeTestSimpleAccount(source);
    const { external_id: external_id } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });
    const { id: user1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id1',
      role: USER_ROLES.AUTHOR,
      author_id: 'AUTHOR_ID',
      auto_renew: true,
      encryption: true,
      encryption_password: undefined,
      prompt: true,
    });

    const service = module.get<UsersService>(UsersService);
    overrideSendgridService(service, {});
    const context = makeContext(`uuidv4`, 'requestId');

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

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

    const { id: accountId } = await makeTestSimpleAccount(source);
    const { external_id: external_id } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id',
      role: USER_ROLES.NONE,
      author_id: undefined,
      auto_renew: true,
      encryption: false,
      encryption_password: undefined,
      prompt: false,
    });
    const { id: user1 } = await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id1',
      role: USER_ROLES.AUTHOR,
      author_id: 'AUTHOR_ID1',
      auto_renew: true,
      encryption: true,
      encryption_password: 'password',
      prompt: true,
    });

    await makeTestUser(source, {
      account_id: accountId,
      external_id: 'external_id2',
      role: USER_ROLES.AUTHOR,
      author_id: 'AUTHOR_ID2',
      auto_renew: true,
      encryption: true,
      encryption_password: 'password',
      prompt: true,
    });

    const service = module.get<UsersService>(UsersService);
    overrideSendgridService(service, {});
    const context = makeContext(`uuidv4`, 'requestId');

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

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

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

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

  it('同意済み利用規約バージョンを更新できる（第五）', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { admin } = await makeTestAccount(source, {
      tier: 5,
    });
    const context = makeContext(uuidv4(), 'requestId');

    const service = module.get<UsersService>(UsersService);
    await service.updateAcceptedVersion(
      context,
      admin.external_id,
      'v2.0',
      'v2.0',
    );
    const user = await getUser(source, admin.id);

    expect(user?.accepted_eula_version).toBe('v2.0');
  });

  it('同意済み利用規約バージョンを更新できる（第一～第四）', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { admin } = await makeTestAccount(source, {
      tier: 4,
    });
    const context = makeContext(uuidv4(), 'requestId');

    const service = module.get<UsersService>(UsersService);
    await service.updateAcceptedVersion(
      context,
      admin.external_id,
      'v2.0',
      'v2.0',
      'v3.0',
    );
    const user = await getUser(source, admin.id);

    expect(user?.accepted_eula_version).toBe('v2.0');
    expect(user?.accepted_dpa_version).toBe('v3.0');
  });

  it('パラメータが不在のときエラーとなる（第一～第四）', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();
    const { admin } = await makeTestAccount(source, {
      tier: 4,
    });
    const context = makeContext(uuidv4(), 'requestId');

    const service = module.get<UsersService>(UsersService);
    await expect(
      service.updateAcceptedVersion(
        context,
        admin.external_id,
        'v2.0',
        'v2.0',
        undefined,
      ),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010001'), HttpStatus.BAD_REQUEST),
    );
  });
});

describe('UsersService.getUserName', () => {
  let source: DataSource | null = null;

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

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

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

  it('ユーザーが存在しない場合は、ユーザー未存在エラー', async () => {
    if (!source) fail();

    try {
      const module = await makeTestingModule(source);
      if (!module) fail();
      const context = makeContext(uuidv4(), 'requestId');

      const service = module.get<UsersService>(UsersService);
      await service.getUserName(context, 'external_id');
      fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010204'));
      } else {
        fail();
      }
    }
  });
});

describe('UsersService.getRelations', () => {
  let source: DataSource | null = null;

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

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

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

  it('ユーザー関連情報を取得できる（Author）', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();

    const { account } = await makeTestAccount(source, {
      tier: 5,
    });
    const { id: user1, external_id } = await makeTestUser(source, {
      account_id: account.id,
      role: USER_ROLES.AUTHOR,
      author_id: 'AUTHOR_1',
      encryption: true,
      encryption_password: 'password',
      prompt: true,
    });
    const { id: user2 } = await makeTestUser(source, {
      account_id: account.id,
      role: USER_ROLES.AUTHOR,
      author_id: 'AUTHOR_2',
    });
    await makeTestUser(source, {
      account_id: account.id,
      role: USER_ROLES.AUTHOR,
      author_id: 'AUTHOR_3',
      email_verified: false,
    });

    const worktype1 = await createWorktype(
      source,
      account.id,
      'worktype1',
      undefined,
      true,
    );
    await createOptionItems(source, worktype1.id);

    const worktype2 = await createWorktype(source, account.id, 'worktype2');
    await createOptionItems(source, worktype2.id);

    await createWorkflow(source, account.id, user1, worktype1.id);
    await createWorkflow(source, account.id, user1, worktype2.id);
    await createWorkflow(source, account.id, user1);
    await createWorkflow(source, account.id, user2, worktype1.id);

    // 作成したデータを確認
    {
      const workflows = await getWorkflows(source, account.id);
      workflows.sort((a, b) => a.id - b.id);

      expect(workflows.length).toBe(4);
      expect(workflows[0].worktype_id).toBe(worktype1.id);
      expect(workflows[0].author_id).toBe(user1);
      expect(workflows[1].worktype_id).toBe(worktype2.id);
      expect(workflows[1].author_id).toBe(user1);
      expect(workflows[2].worktype_id).toBe(null);
      expect(workflows[2].author_id).toBe(user1);
      expect(workflows[3].worktype_id).toBe(worktype1.id);
      expect(workflows[3].author_id).toBe(user2);
    }

    const context = makeContext(external_id, 'requestId');

    const service = module.get<UsersService>(UsersService);
    const relations = await service.getRelations(context, external_id);

    // レスポンスを確認
    {
      expect(relations.authorId).toBe('AUTHOR_1');
      expect(relations.authorIdList.length).toBe(2);
      expect(relations.authorIdList[0]).toBe('AUTHOR_1');
      expect(relations.authorIdList[1]).toBe('AUTHOR_2');

      const workTypeList = relations.workTypeList;
      expect(relations.workTypeList.length).toBe(2);
      expect(workTypeList[0].workTypeId).toBe(worktype1.custom_worktype_id);
      expect(workTypeList[0].optionItemList.length).toBe(10);
      expect(workTypeList[0].optionItemList[0].label).toBe('');
      expect(workTypeList[0].optionItemList[0].initialValueType).toBe(2);
      expect(workTypeList[0].optionItemList[0].defaultValue).toBe('');
      expect(workTypeList[1].workTypeId).toBe(worktype2.custom_worktype_id);
      expect(workTypeList[1].optionItemList.length).toBe(10);

      expect(relations.isEncrypted).toBe(true);
      expect(relations.encryptionPassword).toBe('password');
      expect(relations.activeWorktype).toBe(worktype1.custom_worktype_id);
      expect(relations.audioFormat).toBe(USER_AUDIO_FORMAT);
      expect(relations.prompt).toBe(true);
    }
  });

  it('ユーザー関連情報を取得できる（Author以外）', async () => {
    if (!source) fail();
    const module = await makeTestingModule(source);
    if (!module) fail();

    const { account } = await makeTestAccount(source, {
      tier: 5,
    });
    const { external_id } = await makeTestUser(source, {
      account_id: account.id,
      role: USER_ROLES.TYPIST,
      encryption: false,
      prompt: false,
    });
    const { id: user2 } = await makeTestUser(source, {
      account_id: account.id,
      role: USER_ROLES.AUTHOR,
      author_id: 'AUTHOR_2',
    });

    const worktype1 = await createWorktype(source, account.id, 'worktype1');
    await createOptionItems(source, worktype1.id);

    await createWorkflow(source, account.id, user2, worktype1.id);

    // 作成したデータを確認
    {
      const workflows = await getWorkflows(source, account.id);
      expect(workflows.length).toBe(1);
      expect(workflows[0].worktype_id).toBe(worktype1.id);
      expect(workflows[0].author_id).toBe(user2);
    }

    const context = makeContext(external_id, 'requestId');

    const service = module.get<UsersService>(UsersService);
    const relations = await service.getRelations(context, external_id);

    // レスポンスを確認
    {
      expect(relations.authorId).toBe(undefined);
      expect(relations.authorIdList.length).toBe(1);
      expect(relations.authorIdList[0]).toBe('AUTHOR_2');
      expect(relations.workTypeList.length).toBe(0);

      expect(relations.isEncrypted).toBe(false);
      expect(relations.encryptionPassword).toBe(undefined);
      expect(relations.activeWorktype).toBe('');
      expect(relations.audioFormat).toBe(USER_AUDIO_FORMAT);
      expect(relations.prompt).toBe(false);
    }
  });

  it('ユーザーが存在しない場合は、ユーザー未存在エラー', async () => {
    if (!source) fail();

    try {
      const module = await makeTestingModule(source);
      if (!module) fail();
      const context = makeContext(uuidv4(), 'requestId');

      const service = module.get<UsersService>(UsersService);
      await service.getRelations(context, 'external_id');
      fail();
    } catch (e) {
      if (e instanceof HttpException) {
        expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
        expect(e.getResponse()).toEqual(makeErrorResponse('E010204'));
      } else {
        fail();
      }
    }
  });
});
