OMDSCloud/dictation_server/src/features/licenses/licenses.service.spec.ts
2025-01-21 05:23:54 +00:00

2402 lines
70 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>(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>(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>(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>(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>(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>(LicensesService);
const issueCount = 500;
const context = makeContext(`uuidv4`, 'requestId');
//DBアクセスに失敗するようにする
const licensesService = module.get<LicensesRepositoryService>(
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>(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>(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>(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>(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>(LicensesService);
const context = makeContext(`uuidv4`, 'requestId');
const cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
//DBアクセスに失敗するようにする
const licensesService = module.get<LicensesRepositoryService>(
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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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つ作成
// ミリ秒以下は切り捨てる 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>(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>(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>(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>(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>(UsersService);
const licenseService = module.get<LicensesService>(LicensesService);
const sendGridService = module.get<SendGridService>(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>(UsersService);
const licenseService = module.get<LicensesService>(LicensesService);
const sendGridService = module.get<SendGridService>(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>(LicensesService);
const context = makeContext(`uuidv4`, 'requestId');
//DBアクセスに失敗するようにする
const licensesService = module.get<LicensesRepositoryService>(
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();
}
}
});
});