import { NewAllocatedLicenseExpirationDate, NewTrialLicenseExpirationDate, } from './types/types'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; import { HttpException, HttpStatus } from '@nestjs/common'; import { LicensesService } from './licenses.service'; import { makeTestingModule } from '../../common/test/modules'; import { DataSource } from 'typeorm'; import { createCardLicense, createLicense, createCardLicenseIssue, createLicenseAllocationHistory, selectCardLicensesCount, selectCardLicense, selectLicense, selectLicenseAllocationHistory, createOrder, selectOrderLicense, selectIssuedLicensesAndLicenseOrders, } from './test/utility'; import { UsersService } from '../users/users.service'; import { Context, makeContext } from '../../common/log'; import { ADB2C_SIGN_IN_TYPE, LICENSE_ALLOCATED_STATUS, LICENSE_ISSUE_STATUS, LICENSE_TYPE, } from '../../constants'; import { makeHierarchicalAccounts, makeTestAccount, makeTestSimpleAccount, makeTestUser, } from '../../common/test/utility'; import { LicensesRepositoryService } from '../../repositories/licenses/licenses.repository.service'; import { overrideAdB2cService, overrideSendgridService, } from '../../common/test/overrides'; import { truncateAllTable } from '../../common/test/init'; import { TestLogger } from '../../common/test/logger'; import { SendGridService } from '../../gateways/sendgrid/sendgrid.service'; describe('ライセンス注文', () => { 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); 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 { id: accountId, parent_account_id: parentAccountId } = await makeTestSimpleAccount(source, { parent_account_id: 2, }); const { external_id: externalId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', }); const poNumber = 'test1'; const orderCount = 100; const service = module.get(LicensesService); const context = makeContext(`uuidv4`, 'requestId'); await service.licenseOrders(context, externalId, poNumber, orderCount); const dbSelectResult = await selectOrderLicense( source, accountId, poNumber, ); expect(dbSelectResult.orderLicense?.po_number).toEqual(poNumber); expect(dbSelectResult.orderLicense?.quantity).toEqual(orderCount); expect(dbSelectResult.orderLicense?.from_account_id).toEqual(accountId); expect(dbSelectResult.orderLicense?.to_account_id).toEqual(parentAccountId); expect(dbSelectResult.orderLicense?.status).toEqual('Issue Requesting'); // ライセンス種別のデフォルト値が埋まっていること expect(dbSelectResult.orderLicense?.type).toEqual(LICENSE_TYPE.NORMAL); }); it('POナンバー重複時、エラーとなる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source, { parent_account_id: 2, }); const { external_id: externalId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', }); const poNumber = 'test1'; const orderCount = 100; const service = module.get(LicensesService); const context = makeContext(`uuidv4`, 'requestId'); try { await service.licenseOrders(context, externalId, poNumber, orderCount); await service.licenseOrders(context, externalId, poNumber, orderCount); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E010401')); } else { fail(); } } }); it('ユーザID取得できなかった場合、エラーとなる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source, { parent_account_id: 2, }); const { external_id: externalId } = await makeTestUser(source, { account_id: accountId, external_id: 'useId', role: 'admin', }); const poNumber = 'test1'; const orderCount = 100; // 存在しないのユーザーIDを作成 const notExistUserId = 'notExistId'; const service = module.get(LicensesService); const context = makeContext(`uuidv4`, 'requestId'); try { await service.licenseOrders( context, notExistUserId, poNumber, orderCount, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E010204')); } else { fail(); } } }); it('親ユーザID取得できなかった場合、エラーとなる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source, { parent_account_id: undefined, }); const { external_id: externalId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', }); const poNumber = 'test1'; const orderCount = 100; const service = module.get(LicensesService); const context = makeContext(`uuidv4`, 'requestId'); try { await service.licenseOrders(context, externalId, poNumber, orderCount); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E010501')); } else { fail(); } } }); }); describe('カードライセンス発行', () => { 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); 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 { id: accountId } = await makeTestSimpleAccount(source); const { external_id: externalId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); const service = module.get(LicensesService); const issueCount = 500; const context = makeContext(`uuidv4`, 'requestId'); await service.issueCardLicenseKeys(context, externalId, issueCount); const dbSelectResult = await selectCardLicensesCount(source); expect(dbSelectResult.count).toEqual(issueCount); }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source); const { external_id: externalId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); const service = module.get(LicensesService); const issueCount = 500; const context = makeContext(`uuidv4`, 'requestId'); //DBアクセスに失敗するようにする const licensesService = module.get( LicensesRepositoryService, ); licensesService.createCardLicenses = jest .fn() .mockRejectedValue('DB failed'); try { await service.issueCardLicenseKeys(context, externalId, issueCount); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('カードライセンスを取り込む', () => { 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); 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 { id: accountId } = await makeTestSimpleAccount(source); const { external_id: externalId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); 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, null, null, null, ); await createCardLicense(source, license_id, issueId, cardLicenseKey); await createCardLicenseIssue(source, issueId); const service = module.get(LicensesService); const context = makeContext(`uuidv4`, 'requestId'); await service.activateCardLicenseKey(context, 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 () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 明日の日付を取得 // ミリ秒以下は切り捨てる const tommorow = new Date(); tommorow.setDate(tommorow.getDate() + 1); tommorow.setMilliseconds(0); const { id: accountId } = await makeTestSimpleAccount(source); const { external_id: externalId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); // ライセンスを作成する // 1件目 await createLicense( source, 1, new Date(tommorow.getTime() + 60 * 60 * 1000), accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); // 2件目、expiry_dateがnull(OneYear) await createLicense( source, 2, null, accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); // 3件目、1件目と同じ有効期限 await createLicense( source, 3, new Date(tommorow.getTime() + 60 * 60 * 1000), accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); // 4件目、expiry_dateが一番遠いデータ await createLicense( source, 4, new Date(tommorow.getTime() + 60 * 60 * 1000 * 2), accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); // 5件目、expiry_dateがnull(OneYear) await createLicense( source, 5, null, accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); // 6件目、ライセンス状態が割当済 await createLicense( source, 6, new Date(tommorow.getTime() + 60 * 60 * 1000 * 2), accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.ALLOCATED, null, null, null, null, ); // 7件目、ライセンス状態が削除済 await createLicense( source, 7, new Date(tommorow.getTime() + 60 * 60 * 1000 * 2), accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.DELETED, null, null, null, null, ); // 8件目、別アカウントの未割当のライセンス await createLicense( source, 8, new Date(tommorow.getTime() + 60 * 60 * 1000), accountId + 1, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); // 9件目、有効期限切れのライセンス await createLicense( source, 9, new Date(tommorow.getTime() - 60 * 60 * 1000 * 24), accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); const service = module.get(LicensesService); const context = makeContext('userId', 'requestId'); 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); }); it('カードライセンス取り込みに失敗した場合、エラーになる(ライセンスが存在しないエラー)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source); const { external_id: externalId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); //存在しないライセンスキーを作成 const notExistCardLicenseKey = 'XXCETXC0Z9PQZ9GKRGGY'; const service = module.get(LicensesService); const context = makeContext(`uuidv4`, 'requestId'); try { await service.activateCardLicenseKey( context, externalId, notExistCardLicenseKey, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E010801')); } else { fail(); } } }); it('カードライセンス取り込みに失敗した場合、エラーになる(ライセンスが既に取り込まれているエラー)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source); const { external_id: externalId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); 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, null, null, null, ); await createCardLicense(source, license_id, issueId, cardLicenseKey); await createCardLicenseIssue(source, issueId); const service = module.get(LicensesService); const context = makeContext(`uuidv4`, 'requestId'); try { await service.activateCardLicenseKey(context, externalId, cardLicenseKey); await service.activateCardLicenseKey(context, externalId, cardLicenseKey); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E010802')); } else { fail(); } } }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source); const { external_id: externalId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); const service = module.get(LicensesService); const context = makeContext(`uuidv4`, 'requestId'); const cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY'; //DBアクセスに失敗するようにする const licensesService = module.get( LicensesRepositoryService, ); licensesService.activateCardLicense = jest .fn() .mockRejectedValue('DB failed'); try { await service.activateCardLicenseKey(context, externalId, cardLicenseKey); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('ライセンス割り当て', () => { 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); 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 { id: dealerId } = await makeTestSimpleAccount(source, { company_name: 'DEALER_COMPANY', tier: 4, }); const { id: dealerAdminId } = await makeTestUser(source, { account_id: dealerId, external_id: 'userId_admin', role: 'admin', author_id: undefined, }); const { id: accountId } = await makeTestSimpleAccount(source, { parent_account_id: dealerId, tier: 5, }); const { id: userId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); await createLicense( source, 1, null, accountId, LICENSE_TYPE.CARD, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); await createLicenseAllocationHistory( source, 1, userId, 1, accountId, 'NONE', ); const service = module.get(UsersService); let _subject = ''; let _url: string | undefined = ''; overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'TEMP' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, getUsers: async (context, externalIds) => { return externalIds.map((x) => ({ displayName: 'admin', id: x, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: `mail+${x}@example.com`, }, ], })); }, }); 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; }, }); const expiry_date = new NewAllocatedLicenseExpirationDate(); await service.allocateLicense( makeContext('trackingId', 'requestId'), 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); expect(licenseAllocationHistory.licenseAllocationHistory?.account_id).toBe( accountId, ); expect(_subject).toBe('License Assigned Notification [U-108]'); expect(_url).toBe('http://localhost:8081/'); }); it('再割り当て可能なライセンスに対して、ライセンス割り当てが完了する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source); const { id: userId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); const date = new Date(); date.setDate(date.getDate() + 30); date.setMilliseconds(0); await createLicense( source, 1, date, accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.REUSABLE, null, null, null, null, ); await createLicenseAllocationHistory( source, 1, userId, 1, accountId, 'NONE', ); const service = module.get(UsersService); overrideSendgridService(service, {}); await service.allocateLicense( makeContext('trackingId', 'requestId'), 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); expect(licenseAllocationHistory.licenseAllocationHistory?.account_id).toBe( accountId, ); }); it('未割当のライセンスに対して、別のライセンスが割り当てられているユーザーの割り当てが完了する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source); const { id: userId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); const date = new Date(); date.setDate(date.getDate() + 30); date.setMilliseconds(0); await createLicense( source, 1, date, accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.ALLOCATED, userId, null, null, null, ); await createLicense( source, 2, null, accountId, LICENSE_TYPE.CARD, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); await createLicenseAllocationHistory( source, 1, userId, 1, accountId, 'NONE', ); const service = module.get(UsersService); overrideSendgridService(service, {}); const expiry_date = new NewAllocatedLicenseExpirationDate(); await service.allocateLicense( makeContext('trackingId', 'requestId'), 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); expect(licenseAllocationHistory.licenseAllocationHistory?.account_id).toBe( accountId, ); // 新たに割り当てたライセンスの状態確認 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); expect( newlicenseAllocationHistory.licenseAllocationHistory?.account_id, ).toBe(accountId); }); it('割り当て時にライセンス履歴テーブルへの登録が完了する(元がNORMALのとき)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source); const { id: userId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); const date = new Date(); date.setDate(date.getDate() + 30); date.setMilliseconds(0); await createLicense( source, 1, date, accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.ALLOCATED, userId, null, null, null, ); await createLicense( source, 2, null, accountId, LICENSE_TYPE.CARD, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); await createLicenseAllocationHistory( source, 1, userId, 1, accountId, 'NONE', ); const service = module.get(UsersService); overrideSendgridService(service, {}); await service.allocateLicense( makeContext('trackingId', 'requestId'), userId, 2, ); const licenseAllocationHistory = await selectLicenseAllocationHistory( source, userId, 2, ); expect( licenseAllocationHistory.licenseAllocationHistory?.switch_from_type, ).toBe('NONE'); }); it('割り当て時にライセンス履歴テーブルへの登録が完了する(元がCARDのとき)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source); const { id: userId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); const date = new Date(); date.setDate(date.getDate() + 30); date.setMilliseconds(0); await createLicense( source, 1, date, accountId, LICENSE_TYPE.CARD, LICENSE_ALLOCATED_STATUS.ALLOCATED, userId, null, null, null, ); await createLicense( source, 2, null, accountId, LICENSE_TYPE.CARD, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); await createLicenseAllocationHistory( source, 1, userId, 1, accountId, 'CARD', ); const service = module.get(UsersService); overrideSendgridService(service, {}); await service.allocateLicense( makeContext('trackingId', 'requestId'), userId, 2, ); const licenseAllocationHistory = await selectLicenseAllocationHistory( source, userId, 2, ); expect( licenseAllocationHistory.licenseAllocationHistory?.switch_from_type, ).toBe('CARD'); }); it('割り当て時にライセンス履歴テーブルへの登録が完了する(元がTRIALのとき)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source); const { id: userId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); const date = new Date(); date.setDate(date.getDate() + 30); date.setMilliseconds(0); await createLicense( source, 1, date, accountId, LICENSE_TYPE.TRIAL, LICENSE_ALLOCATED_STATUS.ALLOCATED, userId, null, null, null, ); await createLicense( source, 2, null, accountId, LICENSE_TYPE.CARD, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); await createLicenseAllocationHistory( source, 1, userId, 1, accountId, 'TRIAL', ); const service = module.get(UsersService); overrideSendgridService(service, {}); await service.allocateLicense( makeContext('trackingId', 'requestId'), userId, 2, ); const licenseAllocationHistory = await selectLicenseAllocationHistory( source, userId, 2, ); expect( licenseAllocationHistory.licenseAllocationHistory?.switch_from_type, ).toBe('TRIAL'); }); it('有効期限が切れているライセンスを割り当てようとした場合、エラーになる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source); const { id: userId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); const date = new Date(); date.setDate(date.getDate() - 30); date.setMilliseconds(0); await createLicense( source, 1, date, accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.REUSABLE, null, null, null, null, ); const service = module.get(UsersService); overrideSendgridService(service, {}); await expect( service.allocateLicense( makeContext('trackingId', 'requestId'), userId, 1, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010805'), HttpStatus.BAD_REQUEST), ); }); it('割り当て不可なライセンスを割り当てようとした場合、エラーになる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source); const { id: userId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); const date = new Date(); date.setDate(date.getDate() + 30); date.setMilliseconds(0); await createLicense( source, 1, null, accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.ALLOCATED, null, null, null, null, ); await createLicense( source, 2, null, accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.DELETED, null, null, null, null, ); const service = module.get(UsersService); overrideSendgridService(service, {}); await expect( service.allocateLicense( makeContext('trackingId', 'requestId'), userId, 1, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010806'), HttpStatus.BAD_REQUEST), ); await expect( service.allocateLicense( makeContext('trackingId', 'requestId'), userId, 2, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010806'), HttpStatus.BAD_REQUEST), ); }); }); describe('ライセンス割り当て解除', () => { 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); 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 { id: accountId } = await makeTestSimpleAccount(source); const { id: userId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); const date = new Date(); date.setDate(date.getDate() + 30); date.setMilliseconds(0); await createLicense( source, 1, date, accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.ALLOCATED, userId, null, null, null, ); await createLicenseAllocationHistory( source, 1, userId, 1, accountId, 'NONE', ); const service = module.get(UsersService); overrideSendgridService(service, {}); await service.deallocateLicense( makeContext('trackingId', 'requestId'), 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?.account_id).toBe( accountId, ); expect( licenseAllocationHistory.licenseAllocationHistory?.switch_from_type, ).toBe('NONE'); }); it('ライセンスが既に割り当て解除されていた場合、エラーとなる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source); const { id: userId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); await makeTestUser(source, { account_id: accountId, external_id: 'userId2', role: 'admin', author_id: undefined, }); const date = new Date(); date.setDate(date.getDate() + 30); date.setMilliseconds(0); await createLicense( source, 1, date, accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.ALLOCATED, 2, null, null, null, ); await createLicense( source, 2, date, accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.REUSABLE, userId, null, null, null, ); await createLicenseAllocationHistory( source, 1, userId, 1, accountId, 'NONE', ); const service = module.get(UsersService); overrideSendgridService(service, {}); await expect( service.deallocateLicense(makeContext('trackingId', 'requestId'), userId), ).rejects.toEqual( new HttpException(makeErrorResponse('E010807'), HttpStatus.BAD_REQUEST), ); }); }); describe('ライセンス注文キャンセル', () => { 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); 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 { tier2Accounts: tier2Accounts } = await makeHierarchicalAccounts( source, ); const poNumber = 'CANCEL_TEST'; await createOrder( source, poNumber, tier2Accounts[0].account.id, tier2Accounts[0].account.parent_account_id ?? 0, null, 10, 'Issue Requesting', ); // キャンセル済みの同名poNumoberが存在しても正常に動作することの確認用order await createOrder( source, poNumber, tier2Accounts[0].account.id, tier2Accounts[0].account.parent_account_id ?? 0, null, 10, 'Order Canceled', ); const service = module.get(LicensesService); overrideSendgridService(service, {}); await service.cancelOrder( makeContext('trackingId', 'requestId'), tier2Accounts[0].users[0].external_id, poNumber, ); // 割り当て解除したライセンスの状態確認 const orderRecord = await selectOrderLicense( source, tier2Accounts[0].account.id, poNumber, ); expect(orderRecord.orderLicense?.canceled_at).toBeDefined(); expect(orderRecord.orderLicense?.status).toBe('Order Canceled'); }); it('ライセンスが既に発行済みの場合、エラーとなる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { tier2Accounts: tier2Accounts } = await makeHierarchicalAccounts( source, ); const poNumber = 'CANCEL_TEST'; await createOrder( source, poNumber, tier2Accounts[0].account.id, tier2Accounts[0].account.parent_account_id ?? 0, null, 10, 'Issued', ); const service = module.get(LicensesService); overrideSendgridService(service, {}); await expect( service.cancelOrder( makeContext('trackingId', 'requestId'), tier2Accounts[0].users[0].external_id, poNumber, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010808'), HttpStatus.BAD_REQUEST), ); }); it('ライセンスが既にキャンセル済みの場合、エラーとなる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { tier2Accounts: tier2Accounts } = await makeHierarchicalAccounts( source, ); const poNumber = 'CANCEL_TEST'; await createOrder( source, poNumber, tier2Accounts[0].account.id, tier2Accounts[0].account.parent_account_id ?? 0, null, 10, 'Order Canceled', ); const service = module.get(LicensesService); overrideSendgridService(service, {}); await expect( service.cancelOrder( makeContext('trackingId', 'requestId'), tier2Accounts[0].users[0].external_id, poNumber, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010808'), HttpStatus.BAD_REQUEST), ); }); }); describe('割り当て可能なライセンス取得', () => { 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); 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 { account, admin } = await makeTestAccount(source, { company_name: 'AAA', tier: 5, }); // ライセンスを作成 // 有効期限が当日のライセンスを1つ、有効期限が翌日のライセンスを1つ、有効期限が翌々日のライセンスを1つ作成 // ミリ秒以下は切り捨てる DBで扱っていないため const today = new Date(); today.setMilliseconds(0); const tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setMilliseconds(0); const dayAfterTomorrow = new Date(); dayAfterTomorrow.setDate(dayAfterTomorrow.getDate() + 2); dayAfterTomorrow.setMilliseconds(0); await createLicense( source, 1, today, account.id, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); await createLicense( source, 2, tomorrow, account.id, LICENSE_TYPE.CARD, LICENSE_ALLOCATED_STATUS.REUSABLE, null, null, null, null, ); await createLicense( source, 3, dayAfterTomorrow, account.id, LICENSE_TYPE.TRIAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); // ライセンス作成したデータを確認 { const license1 = await selectLicense(source, 1); expect(license1.license?.id).toBe(1); expect(license1.license?.expiry_date).toEqual(today); expect(license1.license?.allocated_user_id).toBe(null); expect(license1.license?.status).toBe( LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); expect(license1.license?.account_id).toBe(account.id); expect(license1.license?.type).toBe(LICENSE_TYPE.NORMAL); const license2 = await selectLicense(source, 2); expect(license2.license?.id).toBe(2); expect(license2.license?.expiry_date).toEqual(tomorrow); expect(license2.license?.allocated_user_id).toBe(null); expect(license2.license?.status).toBe(LICENSE_ALLOCATED_STATUS.REUSABLE); expect(license2.license?.account_id).toBe(account.id); expect(license2.license?.type).toBe(LICENSE_TYPE.CARD); const license3 = await selectLicense(source, 3); expect(license3.license?.id).toBe(3); expect(license3.license?.expiry_date).toEqual(dayAfterTomorrow); expect(license3.license?.allocated_user_id).toBe(null); expect(license3.license?.status).toBe( LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); expect(license3.license?.account_id).toBe(account.id); expect(license3.license?.type).toBe(LICENSE_TYPE.TRIAL); } const service = module.get(LicensesService); const context = makeContext('trackingId', 'requestId'); const response = await service.getAllocatableLicenses( context, admin.external_id, ); // 有効期限が当日のライセンスは取得されない // 有効期限が長い順に取得される expect(response.allocatableLicenses.length).toBe(2); expect(response.allocatableLicenses[0].licenseId).toBe(3); expect(response.allocatableLicenses[0].expiryDate).toEqual( dayAfterTomorrow, ); expect(response.allocatableLicenses[1].licenseId).toBe(2); expect(response.allocatableLicenses[1].expiryDate).toEqual(tomorrow); }); it('割り当て可能なライセンスが存在しない場合、空の配列を返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { account, admin } = await makeTestAccount(source, { company_name: 'AAA', tier: 5, }); // ライセンスを作成 // 有効期限が当日のライセンスを3つ、有効期限が昨日のライセンスを1つ作成 // ミリ秒以下は切り捨てる DBで扱っていないため const today = new Date(); today.setMilliseconds(0); const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); yesterday.setMilliseconds(0); for (let i = 1; i <= 3; i++) { await createLicense( source, i, today, account.id, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); } await createLicense( source, 4, yesterday, account.id, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); // ライセンス作成したデータを確認 { const license1 = await selectLicense(source, 1); expect(license1.license?.id).toBe(1); expect(license1.license?.expiry_date).toEqual(today); expect(license1.license?.allocated_user_id).toBe(null); expect(license1.license?.status).toBe( LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); expect(license1.license?.account_id).toBe(account.id); expect(license1.license?.type).toBe(LICENSE_TYPE.NORMAL); const license2 = await selectLicense(source, 2); expect(license2.license?.id).toBe(2); expect(license2.license?.expiry_date).toEqual(today); expect(license2.license?.allocated_user_id).toBe(null); expect(license2.license?.status).toBe( LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); expect(license2.license?.account_id).toBe(account.id); expect(license2.license?.type).toBe(LICENSE_TYPE.NORMAL); const license3 = await selectLicense(source, 3); expect(license3.license?.id).toBe(3); expect(license3.license?.expiry_date).toEqual(today); expect(license3.license?.allocated_user_id).toBe(null); expect(license3.license?.status).toBe( LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); expect(license3.license?.account_id).toBe(account.id); expect(license3.license?.type).toBe(LICENSE_TYPE.NORMAL); const license4 = await selectLicense(source, 4); expect(license4.license?.id).toBe(4); expect(license4.license?.expiry_date).toEqual(yesterday); expect(license4.license?.allocated_user_id).toBe(null); expect(license4.license?.status).toBe( LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); expect(license4.license?.account_id).toBe(account.id); expect(license4.license?.type).toBe(LICENSE_TYPE.NORMAL); } const service = module.get(LicensesService); const context = makeContext('trackingId', 'requestId'); const response = await service.getAllocatableLicenses( context, admin.external_id, ); // 有効期限が当日のライセンスは取得されない // 有効期限が切れているライセンスは取得されない expect(response.allocatableLicenses.length).toBe(0); expect(response.allocatableLicenses).toEqual([]); }); it('割り当て可能なライセンスを100件取得できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { account, admin } = await makeTestAccount(source, { company_name: 'AAA', tier: 5, }); // ライセンスを作成 // 有効期限が30日後のライセンスを100件作成 // ミリ秒以下は切り捨てる DBで扱っていないため const date = new Date(); date.setDate(date.getDate() + 30); date.setMilliseconds(0); for (let i = 1; i <= 100; i++) { await createLicense( source, i, date, account.id, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); } // ライセンス作成したデータを確認 for (let i = 1; i <= 100; i++) { const license = await selectLicense(source, i); expect(license.license?.id).toBe(i); expect(license.license?.expiry_date).toEqual(date); expect(license.license?.allocated_user_id).toBe(null); expect(license.license?.status).toBe( LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); expect(license.license?.account_id).toBe(account.id); expect(license.license?.type).toBe(LICENSE_TYPE.NORMAL); } const service = module.get(LicensesService); const context = makeContext('trackingId', 'requestId'); const response = await service.getAllocatableLicenses( context, admin.external_id, ); // 100件取得できる expect(response.allocatableLicenses.length).toBe(100); }); it('既に割り当てられているライセンスは取得されない', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { account, admin } = await makeTestAccount(source, { company_name: 'AAA', tier: 5, }); // ライセンスを作成 // ライセンスを5件作成(10日後から1日ずつ有効期限を設定) // 有効期限が設定されていないライセンス(新規ライセンス)を1件作成 // 既に割り当てられているライセンスを1件作成 // ミリ秒以下は切り捨てる DBで扱っていないため const date = new Date(); date.setMinutes(0); date.setSeconds(0); date.setDate(date.getDate() + 10); date.setMilliseconds(0); for (let i = 1; i <= 5; i++) { await createLicense( source, i, date, account.id, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, admin.id, null, null, null, ); date.setDate(date.getDate() + 1); } // 新規ライセンス await createLicense( source, 6, null, account.id, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, null, null, ); // 既に割り当てられているライセンス await createLicense( source, 7, date, account.id, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.ALLOCATED, admin.id, null, null, null, ); // ライセンス作成したデータを確認 { const date = new Date(); date.setMinutes(0); date.setSeconds(0); date.setDate(date.getDate() + 10); date.setMilliseconds(0); for (let i = 1; i <= 5; i++) { const license = await selectLicense(source, i); expect(license.license?.id).toBe(i); expect(license.license?.expiry_date).toEqual(date); expect(license.license?.allocated_user_id).toBe(admin.id); expect(license.license?.status).toBe( LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); expect(license.license?.account_id).toBe(account.id); expect(license.license?.type).toBe(LICENSE_TYPE.NORMAL); date.setDate(date.getDate() + 1); } const newLicense = await selectLicense(source, 6); expect(newLicense.license?.id).toBe(6); expect(newLicense.license?.expiry_date).toBe(null); expect(newLicense.license?.allocated_user_id).toBe(null); expect(newLicense.license?.status).toBe( LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); const allocatedLicense = await selectLicense(source, 7); expect(allocatedLicense.license?.id).toBe(7); expect(allocatedLicense.license?.expiry_date).toEqual(date); expect(allocatedLicense.license?.allocated_user_id).toBe(admin.id); expect(allocatedLicense.license?.status).toBe( LICENSE_ALLOCATED_STATUS.ALLOCATED, ); expect(allocatedLicense.license?.account_id).toBe(account.id); expect(allocatedLicense.license?.type).toBe(LICENSE_TYPE.NORMAL); } const service = module.get(LicensesService); const context = makeContext('trackingId', 'requestId'); const response = await service.getAllocatableLicenses( context, admin.external_id, ); // 既に割り当てられているライセンスは取得されない // 新規ライセンスは取得される // 有効期限が長い順に取得される expect(response.allocatableLicenses.length).toBe(6); expect(response.allocatableLicenses[0].licenseId).toBe(6); expect(response.allocatableLicenses[0].expiryDate).toBe(undefined); expect(response.allocatableLicenses[1].licenseId).toBe(5); // 有効期限が最も長い expect(response.allocatableLicenses[2].licenseId).toBe(4); expect(response.allocatableLicenses[3].licenseId).toBe(3); expect(response.allocatableLicenses[4].licenseId).toBe(2); expect(response.allocatableLicenses[5].licenseId).toBe(1); }); }); describe('第五階層へのトライアルライセンス発行', () => { 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); 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 { tier1Accounts, tier4Accounts } = await makeHierarchicalAccounts( source, ); const tier1AccountId = tier1Accounts[0].account.id; const tier1ExternalId = tier1Accounts[0].users[0].external_id; const tier4AccountId = tier4Accounts[0].account.id; const { id: tier5AccountId } = await makeTestSimpleAccount(source, { tier: 5, parent_account_id: tier4AccountId, }); await makeTestUser(source, { account_id: tier5AccountId, external_id: 'tier5UserId', role: 'admin', author_id: undefined, }); const usersService = module.get(UsersService); const licenseService = module.get(LicensesService); const sendGridService = module.get(SendGridService); // メール送信サービスのモックによって初期化される値たち let _subject = ''; let _url: string | undefined = ''; let addressToTier5Admin = ''; let addressCcDealerList: string[] = []; // ユーザー取得処理をモック化 overrideAdB2cService(usersService, { getUsers: async (context, externalIds) => { if (externalIds.includes('tier5UserId')) { // 第五階層のユーザーの場合 return externalIds.map((x) => ({ displayName: `tier5Admin${x}`, id: x, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: `tier5Admin+${x}@example.com`, }, ], })); } // 第五階層以外の場合 return externalIds.map((x) => ({ displayName: 'upperTieradmin', id: x, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: `upperTierAdmin+${x}@example.com`, }, ], })); }, }); // メール送信サービスをモック化 overrideSendgridService(licenseService, { sendMail: jest.fn( 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 = url; // 第5階層の宛先 addressToTier5Admin = to[0]; // ディーラーの宛先 addressCcDealerList = cc; }, ), }); const context = makeContext(`uuidv4`, 'requestId'); await licenseService.issueTrialLicense( context, tier1ExternalId, tier5AccountId, ); const dbSelectResult = await selectIssuedLicensesAndLicenseOrders( source, tier5AccountId, tier1AccountId, ); if (dbSelectResult === null) fail(); const { order, licenses } = dbSelectResult; // 注文の確認 expect(order.from_account_id).toEqual(tier5AccountId); expect(order.to_account_id).toEqual(tier1AccountId); expect(order.po_number).toBeNull(); expect(order.type).toEqual(LICENSE_TYPE.TRIAL); expect(order.status).toEqual(LICENSE_ISSUE_STATUS.ISSUED); // 10個注文されている expect(order.quantity).toEqual(10); // ライセンスの確認 // 値のチェックは最初と最後の1つのみ // 10個発行されている expect(licenses.length).toEqual(10); // 1レコード目 const firstLicense = licenses[0]; expect(firstLicense.account_id).toEqual(tier5AccountId); expect(firstLicense.order_id).toEqual(order.id); expect(firstLicense.type).toEqual(LICENSE_TYPE.TRIAL); expect(firstLicense.status).toEqual(LICENSE_ALLOCATED_STATUS.UNALLOCATED); expect(firstLicense.expiry_date).toEqual( new NewTrialLicenseExpirationDate(), ); // 10レコード目 const lastLicense = licenses.slice(-1)[0]; expect(lastLicense?.account_id).toEqual(tier5AccountId); expect(lastLicense?.order_id).toEqual(order.id); expect(lastLicense?.type).toEqual(LICENSE_TYPE.TRIAL); expect(lastLicense?.status).toEqual(LICENSE_ALLOCATED_STATUS.UNALLOCATED); expect(lastLicense?.expiry_date).toEqual( new NewTrialLicenseExpirationDate(), ); // メールが期待通り送信されていること // 件名 expect(_subject).toBe('Issued Trial License Notification [U-125]'); // URL expect(_url).toBe('http://localhost:8081/'); // 第五階層の宛先 expect(addressToTier5Admin).toBeTruthy(); // ディーラーの宛先(第一階層宛のみ) expect(addressCcDealerList).toHaveLength(1); // メール送信が呼ばれた回数を検査(第五階層と第一階層に同じメールを送信) expect(sendGridService.sendMail).toBeCalledTimes(1); }); it('第二階層が第五階層へのトライアルライセンス発行が完了する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // アカウントの階層構造を作成。 const { tier2Accounts, tier4Accounts } = await makeHierarchicalAccounts( source, ); const tier2AccountId = tier2Accounts[0].account.id; const tier2ExternalId = tier2Accounts[0].users[0].external_id; const tier4AccountId = tier4Accounts[0].account.id; const { id: tier5AccountId } = await makeTestSimpleAccount(source, { tier: 5, parent_account_id: tier4AccountId, }); await makeTestUser(source, { account_id: tier5AccountId, external_id: 'tier5UserId', role: 'admin', author_id: undefined, }); const usersService = module.get(UsersService); const licenseService = module.get(LicensesService); const sendGridService = module.get(SendGridService); // メール送信サービスのモックによって初期化される値たち let _subject = ''; let _url: string | undefined = ''; let expirationDate: string | undefined = ''; let addressToTier5Admin = ''; let addressCcDealerList: string[] = []; // ユーザー取得処理をモック化 overrideAdB2cService(usersService, { getUsers: async (context, externalIds) => { if (externalIds.includes('tier5UserId')) { // 第五階層のユーザーの場合 return externalIds.map((x) => ({ displayName: `tier5Admin${x}`, id: x, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: `tier5Admin+${x}@example.com`, }, ], })); } // 第五階層以外の場合 return externalIds.map((x) => ({ displayName: 'upperTieradmin', id: x, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: `upperTierAdmin+${x}@example.com`, }, ], })); }, }); // メール送信サービスをモック化 overrideSendgridService(licenseService, { sendMail: jest.fn( 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 = url; // 第5階層の宛先 addressToTier5Admin = to[0]; // ディーラーの宛先 addressCcDealerList = cc; }, ), }); const context = makeContext(`uuidv4`, 'requestId'); await licenseService.issueTrialLicense( context, tier2ExternalId, tier5AccountId, ); const dbSelectResult = await selectIssuedLicensesAndLicenseOrders( source, tier5AccountId, tier2AccountId, ); if (dbSelectResult === null) fail(); const { order, licenses } = dbSelectResult; // 注文の確認 expect(order.from_account_id).toEqual(tier5AccountId); expect(order.to_account_id).toEqual(tier2AccountId); expect(order.po_number).toBeNull(); expect(order.type).toEqual(LICENSE_TYPE.TRIAL); expect(order.status).toEqual(LICENSE_ISSUE_STATUS.ISSUED); // 10個注文されている expect(order.quantity).toEqual(10); // ライセンスの確認 // 値のチェックは最初と最後の1つのみ // 10個発行されている expect(licenses.length).toEqual(10); // 1レコード目 const firstLicense = licenses[0]; expect(firstLicense.account_id).toEqual(tier5AccountId); expect(firstLicense.order_id).toEqual(order.id); expect(firstLicense.type).toEqual(LICENSE_TYPE.TRIAL); expect(firstLicense.status).toEqual(LICENSE_ALLOCATED_STATUS.UNALLOCATED); expect(firstLicense.expiry_date).toEqual( new NewTrialLicenseExpirationDate(), ); // 10レコード目 const lastLicense = licenses.slice(-1)[0]; expect(lastLicense?.account_id).toEqual(tier5AccountId); expect(lastLicense?.order_id).toEqual(order.id); expect(lastLicense?.type).toEqual(LICENSE_TYPE.TRIAL); expect(lastLicense?.status).toEqual(LICENSE_ALLOCATED_STATUS.UNALLOCATED); expect(lastLicense?.expiry_date).toEqual( new NewTrialLicenseExpirationDate(), ); // メールが期待通り送信されていること // 件名 expect(_subject).toBe('Issued Trial License Notification [U-125]'); // URL expect(_url).toBe('http://localhost:8081/'); // 第五階層の宛先 expect(addressToTier5Admin).toBeTruthy(); // ディーラーの宛先(第一階層宛と第二階層宛) expect(addressCcDealerList).toHaveLength(2); // メール送信が呼ばれた回数を検査(第五階層と第一階層に同じメールを送信) expect(sendGridService.sendMail).toBeCalledTimes(1); }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source); const { external_id: externalId } = await makeTestUser(source, { account_id: accountId, external_id: 'userId', role: 'admin', author_id: undefined, }); const service = module.get(LicensesService); const context = makeContext(`uuidv4`, 'requestId'); //DBアクセスに失敗するようにする const licensesService = module.get( LicensesRepositoryService, ); licensesService.issueTrialLicense = jest .fn() .mockRejectedValue('DB failed'); try { await service.issueTrialLicense(context, externalId, accountId); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); });