import { AccessToken } from '../../common/token';
import {
  CreateOrdersRequest,
  IssueCardLicensesRequest,
  IssueCardLicensesResponse,
  ActivateCardLicensesRequest,
  NewAllocatedLicenseExpirationDate,
} from './types/types';
import {
  makeDefaultAccountsRepositoryMockValue,
  makeDefaultLicensesRepositoryMockValue,
  makeDefaultUsersRepositoryMockValue,
  makeLicensesServiceMock,
} from './test/liscense.service.mock';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { HttpException, HttpStatus } from '@nestjs/common';
import {
  PoNumberAlreadyExistError,
  LicenseKeyAlreadyActivatedError,
  LicenseNotExistError,
} from '../../repositories/licenses/errors/types';
import { LicensesService } from './licenses.service';
import { makeTestingModule } from '../../common/test/modules';
import { DataSource } from 'typeorm';
import {
  createAccount,
  createUser,
  createCardLicense,
  createLicense,
  createCardLicenseIssue,
  createLicenseAllocationHistory,
  selectCardLicensesCount,
  selectCardLicense,
  selectLicense,
  selectLicenseAllocationHistory,
} from './test/utility';
import { UsersService } from '../users/users.service';
import { makeContext } from '../../common/log';
import { IsNotIn } from 'class-validator';
import { LICENSE_ALLOCATED_STATUS, LICENSE_TYPE } from '../../constants';

describe('LicensesService', () => {
  it('ライセンス注文が完了する', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new CreateOrdersRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.orderCount = 1000;
    body.poNumber = '1';
    expect(
      await service.licenseOrders(token, body.poNumber, body.orderCount),
    ).toEqual(undefined);
  });
  it('ユーザID取得できなかった場合、エラーとなる', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    usersRepositoryMockValue.findUserByExternalId = new Error(
      'User not Found Error.',
    );
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new CreateOrdersRequest();
    const token: AccessToken = { userId: '', role: '', tier: 5 };
    body.orderCount = 1000;
    body.poNumber = '1';
    await expect(
      service.licenseOrders(token, body.poNumber, body.orderCount),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('親ユーザID取得できなかった場合、エラーとなる', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    usersRepositoryMockValue.findUserByExternalId = new Error(
      'Account not Found Error.',
    );
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new CreateOrdersRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.orderCount = 1000;
    body.poNumber = '1';
    await expect(
      service.licenseOrders(token, body.poNumber, body.orderCount),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('POナンバー重複時、エラーとなる', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    lisencesRepositoryMockValue.order = new PoNumberAlreadyExistError();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new CreateOrdersRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.orderCount = 1000;
    body.poNumber = '1';
    await expect(
      service.licenseOrders(token, body.poNumber, body.orderCount),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E010401'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('カードライセンス発行が完了する', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new IssueCardLicensesRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.createCount = 10;
    const issueCardLicensesResponse: IssueCardLicensesResponse = {
      cardLicenseKeys: [
        'WZCETXC0Z9PQZ9GKRGGY',
        'F0JD7EZEDBH4PQRQ83YF',
        'H0HXBP5K9RW7T7JSVDJV',
        'HKIWX54EESYL4X132223',
        '363E81JR460UBHXGFXFI',
        '70IKAPV9K6YMEVLTOXBY',
        '1RJY1TRRYYTGF1LL9WLU',
        'BXM0HKFO7IULTL0A1B36',
        'XYLEWNY2LR6Q657CZE41',
        'AEJWRFFSWRQYQQJ6WVLV',
      ],
    };
    expect(
      await service.issueCardLicenseKeys(token.userId, body.createCount),
    ).toEqual(issueCardLicensesResponse);
  });
  it('カードライセンス発行に失敗した場合、エラーになる', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    lisencesRepositoryMockValue.createCardLicenses = new Error('DB failed');
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new IssueCardLicensesRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.createCount = 1000;
    await expect(
      service.issueCardLicenseKeys(token.userId, body.createCount),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('カードライセンス取り込みが完了する', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new ActivateCardLicensesRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
    expect(
      await service.activateCardLicenseKey(token.userId, body.cardLicenseKey),
    ).toEqual(undefined);
  });
  it('カードライセンス取り込みに失敗した場合、エラーになる（DBエラー）', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    lisencesRepositoryMockValue.activateCardLicense = new Error('DB failed');
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new ActivateCardLicensesRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
    await expect(
      service.activateCardLicenseKey(token.userId, body.cardLicenseKey),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('カードライセンス取り込みに失敗した場合、エラーになる（ライセンスが存在しないエラー）', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    lisencesRepositoryMockValue.activateCardLicense =
      new LicenseNotExistError();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new ActivateCardLicensesRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
    await expect(
      service.activateCardLicenseKey(token.userId, body.cardLicenseKey),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010801'), HttpStatus.BAD_REQUEST),
    );
  });
  it('カードライセンス取り込みに失敗した場合、エラーになる（ライセンスが既に取り込まれているエラー）', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    lisencesRepositoryMockValue.activateCardLicense =
      new LicenseKeyAlreadyActivatedError();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new ActivateCardLicensesRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
    await expect(
      service.activateCardLicenseKey(token.userId, body.cardLicenseKey),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010802'), HttpStatus.BAD_REQUEST),
    );
  });
});

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

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

  it('カードライセンス発行が完了する(発行数が合っているか確認)', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { externalId } = await createUser(
      source,
      accountId,
      'userId',
      'admin',
    );

    const service = module.get<LicensesService>(LicensesService);
    const issueCount = 1000;
    await service.issueCardLicenseKeys(externalId, issueCount);
    const dbSelectResult = await selectCardLicensesCount(source);
    expect(dbSelectResult.count).toEqual(issueCount);
  });

  it('カードライセンス取り込みが完了する', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { externalId } = await createUser(
      source,
      accountId,
      'userId',
      'admin',
    );

    const cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
    const defaultAccountId = 150;
    const license_id = 50;
    const issueId = 100;

    await createLicense(
      source,
      license_id,
      null,
      defaultAccountId,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.UNALLOCATED,
      null,
    );
    await createCardLicense(source, license_id, issueId, cardLicenseKey);
    await createCardLicenseIssue(source, issueId);

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

    await service.activateCardLicenseKey(externalId, cardLicenseKey);
    const dbSelectResultFromCardLicense = await selectCardLicense(
      source,
      cardLicenseKey,
    );
    const dbSelectResultFromLicense = await selectLicense(source, license_id);
    expect(
      dbSelectResultFromCardLicense.cardLicense.activated_at,
    ).toBeDefined();
    expect(dbSelectResultFromLicense.license.account_id).toEqual(accountId);
  });

  it('取込可能なライセンスのみが取得できる', async () => {
    const module = await makeTestingModule(source);

    const now = new Date();
    const { accountId } = await createAccount(source);
    const { externalId } = await createUser(
      source,
      accountId,
      'userId',
      'admin',
    );

    // ライセンスを作成する
    // 1件目
    await createLicense(
      source,
      1,
      new Date(now.getTime() + 60 * 60 * 1000),
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.UNALLOCATED,
      null,
    );
    // 2件目、expiry_dateがnull(OneYear)
    await createLicense(
      source,
      2,
      null,
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.UNALLOCATED,
      null,
    );
    // 3件目、1件目と同じ有効期限
    await createLicense(
      source,
      3,
      new Date(now.getTime() + 60 * 60 * 1000),
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.UNALLOCATED,
      null,
    );
    // 4件目、expiry_dateが一番遠いデータ
    await createLicense(
      source,
      4,
      new Date(now.getTime() + 60 * 60 * 1000 * 2),
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.UNALLOCATED,
      null,
    );
    // 5件目、expiry_dateがnull(OneYear)
    await createLicense(
      source,
      5,
      null,
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.UNALLOCATED,
      null,
    );
    // 6件目、ライセンス状態が割当済
    await createLicense(
      source,
      6,
      new Date(now.getTime() + 60 * 60 * 1000 * 2),
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      null,
    );
    // 7件目、ライセンス状態が削除済
    await createLicense(
      source,
      7,
      new Date(now.getTime() + 60 * 60 * 1000 * 2),
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.DELETED,
      null,
    );
    // 8件目、別アカウントの未割当のライセンス
    await createLicense(
      source,
      8,
      new Date(now.getTime() + 60 * 60 * 1000),
      accountId + 1,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.UNALLOCATED,
      null,
    );
    // 9件目、有効期限切れのライセンス
    await createLicense(
      source,
      9,
      new Date(now.getTime() - 60 * 60 * 1000 * 24),
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.UNALLOCATED,
      null,
    );
    const service = module.get<LicensesService>(LicensesService);
    const context = makeContext('userId');
    const response = await service.getAllocatableLicenses(context, externalId);
    // 対象外のデータは取得していないことを確認する
    expect(response.allocatableLicenses.length).toBe(5);
    // ソートして取得されていることを確認する
    // （expiry_dateがnullを最優先、次に有効期限が遠い順(同じ有効期限の場合はID昇順)
    expect(response.allocatableLicenses[0].licenseId).toBe(2);
    expect(response.allocatableLicenses[1].licenseId).toBe(5);
    expect(response.allocatableLicenses[2].licenseId).toBe(4);
    expect(response.allocatableLicenses[3].licenseId).toBe(1);
    expect(response.allocatableLicenses[4].licenseId).toBe(3);
  });
});

describe('ライセンス割り当て', () => {
  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 module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { userId } = await createUser(source, accountId, 'userId', 'admin');
    await createLicense(
      source,
      1,
      null,
      accountId,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.UNALLOCATED,
      null,
    );
    await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');

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

    const expiry_date = new NewAllocatedLicenseExpirationDate();

    await service.allocateLicense(makeContext('trackingId'), userId, 1);
    const resultLicense = await selectLicense(source, 1);
    expect(resultLicense.license.allocated_user_id).toBe(userId);
    expect(resultLicense.license.status).toBe(
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
    );
    expect(resultLicense.license.expiry_date.setMilliseconds(0)).toEqual(
      expiry_date.setMilliseconds(0),
    );
    const licenseAllocationHistory = await selectLicenseAllocationHistory(
      source,
      userId,
      1,
    );
    expect(licenseAllocationHistory.licenseAllocationHistory.user_id).toBe(
      userId,
    );
    expect(licenseAllocationHistory.licenseAllocationHistory.license_id).toBe(
      1,
    );
    expect(licenseAllocationHistory.licenseAllocationHistory.is_allocated).toBe(
      true,
    );
  });

  it('再割り当て可能なライセンスに対して、ライセンス割り当てが完了する', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { userId } = await createUser(source, accountId, 'userId', 'admin');
    const date = new Date();
    date.setDate(date.getDate() + 30);
    await createLicense(
      source,
      1,
      date,
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.REUSABLE,
      null,
    );
    await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');

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

    await service.allocateLicense(makeContext('trackingId'), userId, 1);
    const result = await selectLicense(source, 1);
    expect(result.license.allocated_user_id).toBe(userId);
    expect(result.license.status).toBe(LICENSE_ALLOCATED_STATUS.ALLOCATED);
    expect(result.license.expiry_date).toEqual(date);
    const licenseAllocationHistory = await selectLicenseAllocationHistory(
      source,
      userId,
      1,
    );
    expect(licenseAllocationHistory.licenseAllocationHistory.user_id).toBe(
      userId,
    );
    expect(licenseAllocationHistory.licenseAllocationHistory.license_id).toBe(
      1,
    );
    expect(licenseAllocationHistory.licenseAllocationHistory.is_allocated).toBe(
      true,
    );
  });

  it('未割当のライセンスに対して、別のライセンスが割り当てられているユーザーの割り当てが完了する', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { userId } = await createUser(source, accountId, 'userId', 'admin');
    const date = new Date();
    date.setDate(date.getDate() + 30);
    await createLicense(
      source,
      1,
      date,
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      userId,
    );
    await createLicense(
      source,
      2,
      null,
      accountId,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.UNALLOCATED,
      null,
    );
    await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');

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

    const expiry_date = new NewAllocatedLicenseExpirationDate();

    await service.allocateLicense(makeContext('trackingId'), userId, 2);

    // もともと割り当てられていたライセンスの状態確認
    const result1 = await selectLicense(source, 1);
    expect(result1.license.allocated_user_id).toBe(null);
    expect(result1.license.status).toBe(LICENSE_ALLOCATED_STATUS.REUSABLE);
    expect(result1.license.expiry_date).toEqual(date);
    const licenseAllocationHistory = await selectLicenseAllocationHistory(
      source,
      userId,
      1,
    );
    expect(licenseAllocationHistory.licenseAllocationHistory.user_id).toBe(
      userId,
    );
    expect(licenseAllocationHistory.licenseAllocationHistory.license_id).toBe(
      1,
    );
    expect(licenseAllocationHistory.licenseAllocationHistory.is_allocated).toBe(
      false,
    );

    // 新たに割り当てたライセンスの状態確認
    const result2 = await selectLicense(source, 2);
    expect(result2.license.allocated_user_id).toBe(userId);
    expect(result2.license.status).toBe(LICENSE_ALLOCATED_STATUS.ALLOCATED);
    expect(result2.license.expiry_date.setMilliseconds(0)).toEqual(
      expiry_date.setMilliseconds(0),
    );
    const newlicenseAllocationHistory = await selectLicenseAllocationHistory(
      source,
      userId,
      2,
    );
    expect(newlicenseAllocationHistory.licenseAllocationHistory.user_id).toBe(
      userId,
    );
    expect(
      newlicenseAllocationHistory.licenseAllocationHistory.license_id,
    ).toBe(2);
    expect(
      newlicenseAllocationHistory.licenseAllocationHistory.is_allocated,
    ).toBe(true);
  });

  it('割り当て時にライセンス履歴テーブルへの登録が完了する（元がNORMALのとき）', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { userId } = await createUser(source, accountId, 'userId', 'admin');
    const date = new Date();
    date.setDate(date.getDate() + 30);
    await createLicense(
      source,
      1,
      date,
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      userId,
    );
    await createLicense(
      source,
      2,
      null,
      accountId,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.UNALLOCATED,
      null,
    );
    await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');

    const service = module.get<UsersService>(UsersService);
    await service.allocateLicense(makeContext('trackingId'), userId, 2);

    const licenseAllocationHistory = await selectLicenseAllocationHistory(
      source,
      userId,
      2,
    );
    expect(
      licenseAllocationHistory.licenseAllocationHistory.switch_from_type,
    ).toBe('NONE');
  });

  it('割り当て時にライセンス履歴テーブルへの登録が完了する（元がCARDのとき）', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { userId } = await createUser(source, accountId, 'userId', 'admin');
    const date = new Date();
    date.setDate(date.getDate() + 30);
    await createLicense(
      source,
      1,
      date,
      accountId,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      userId,
    );
    await createLicense(
      source,
      2,
      null,
      accountId,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.UNALLOCATED,
      null,
    );
    await createLicenseAllocationHistory(source, 1, userId, 1, 'CARD');

    const service = module.get<UsersService>(UsersService);
    await service.allocateLicense(makeContext('trackingId'), userId, 2);

    const licenseAllocationHistory = await selectLicenseAllocationHistory(
      source,
      userId,
      2,
    );
    expect(
      licenseAllocationHistory.licenseAllocationHistory.switch_from_type,
    ).toBe('CARD');
  });

  it('割り当て時にライセンス履歴テーブルへの登録が完了する（元がTRIALのとき）', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { userId } = await createUser(source, accountId, 'userId', 'admin');
    const date = new Date();
    date.setDate(date.getDate() + 30);
    await createLicense(
      source,
      1,
      date,
      accountId,
      LICENSE_TYPE.TRIAL,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      userId,
    );
    await createLicense(
      source,
      2,
      null,
      accountId,
      LICENSE_TYPE.CARD,
      LICENSE_ALLOCATED_STATUS.UNALLOCATED,
      null,
    );
    await createLicenseAllocationHistory(source, 1, userId, 1, 'TRIAL');

    const service = module.get<UsersService>(UsersService);
    await service.allocateLicense(makeContext('trackingId'), userId, 2);

    const licenseAllocationHistory = await selectLicenseAllocationHistory(
      source,
      userId,
      2,
    );
    expect(
      licenseAllocationHistory.licenseAllocationHistory.switch_from_type,
    ).toBe('TRIAL');
  });

  it('有効期限が切れているライセンスを割り当てようとした場合、エラーになる', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { userId } = await createUser(source, accountId, 'userId', 'admin');
    const date = new Date();
    date.setDate(date.getDate() - 30);
    await createLicense(
      source,
      1,
      date,
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.REUSABLE,
      null,
    );

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

    await expect(
      service.allocateLicense(makeContext('trackingId'), userId, 1),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010805'), HttpStatus.BAD_REQUEST),
    );
  });

  it('割り当て不可なライセンスを割り当てようとした場合、エラーになる', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { userId } = await createUser(source, accountId, 'userId', 'admin');
    const date = new Date();
    date.setDate(date.getDate() + 30);
    await createLicense(
      source,
      1,
      null,
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      null,
    );
    await createLicense(
      source,
      2,
      null,
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.DELETED,
      null,
    );

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

    await expect(
      service.allocateLicense(makeContext('trackingId'), userId, 1),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010806'), HttpStatus.BAD_REQUEST),
    );
    await expect(
      service.allocateLicense(makeContext('trackingId'), userId, 2),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010806'), HttpStatus.BAD_REQUEST),
    );
  });
});

describe('ライセンス割り当て解除', () => {
  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 module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { userId } = await createUser(source, accountId, 'userId', 'admin');
    const date = new Date();
    date.setDate(date.getDate() + 30);
    await createLicense(
      source,
      1,
      date,
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      userId,
    );
    await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');

    const service = module.get<UsersService>(UsersService);
    await service.deallocateLicense(makeContext('trackingId'), userId);

    // 割り当て解除したライセンスの状態確認
    const deallocatedLicense = await selectLicense(source, 1);
    expect(deallocatedLicense.license.allocated_user_id).toBe(null);
    expect(deallocatedLicense.license.status).toBe(
      LICENSE_ALLOCATED_STATUS.REUSABLE,
    );
    expect(deallocatedLicense.license.expiry_date).toEqual(date);

    // ライセンス履歴テーブルの状態確認
    const licenseAllocationHistory = await selectLicenseAllocationHistory(
      source,
      userId,
      1,
    );
    expect(licenseAllocationHistory.licenseAllocationHistory.user_id).toBe(
      userId,
    );
    expect(licenseAllocationHistory.licenseAllocationHistory.license_id).toBe(
      1,
    );
    expect(licenseAllocationHistory.licenseAllocationHistory.is_allocated).toBe(
      false,
    );
    expect(
      licenseAllocationHistory.licenseAllocationHistory.switch_from_type,
    ).toBe('NONE');
  });

  it('ライセンスが既に割り当て解除されていた場合、エラーとなる', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { userId } = await createUser(source, accountId, 'userId', 'admin');
    await createUser(source, accountId, 'userId2', 'admin');
    const date = new Date();
    date.setDate(date.getDate() + 30);
    await createLicense(
      source,
      1,
      date,
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.ALLOCATED,
      2,
    );
    await createLicense(
      source,
      2,
      date,
      accountId,
      LICENSE_TYPE.NORMAL,
      LICENSE_ALLOCATED_STATUS.REUSABLE,
      userId,
    );
    await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');

    const service = module.get<UsersService>(UsersService);
    await expect(
      service.deallocateLicense(makeContext('trackingId'), userId),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010807'), HttpStatus.BAD_REQUEST),
    );
  });
});
