Merged PR 326: テストを最新化(パートナー追加)

## 概要
[Task2401: テストを最新化(パートナー追加)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2401)

- DBテストに修正
- Utilityを追加
- 実装側の変数名やコメント等を修正
- テストに使用するメールアドレスを一般的に使用するべきドメインに修正
  - 参考:
    - https://zenn.dev/progfay/articles/email-example-com
    - https://qiita.com/suzutsuki0220/items/4ad83ed2e2adbb6507a4
- `Promise<void>` となっていた部分をテスト用に `Promise<{accountId: number}>` に修正
  - 返り値を使用しているのはテスト側のみ

## レビューポイント
- 将来的にBlobStorageやSendMailで失敗したケース等も必要だが、それは異常系実装タスク内でテストが追加される想定なので今回追加していないが認識は合っているか
- 各種修正に対して、疑問点や問題点はないか

## 動作確認状況
- npm run testで成功
This commit is contained in:
湯本 開 2023-08-18 02:11:09 +00:00
parent 502c31bcac
commit 9803ba4e46
11 changed files with 396 additions and 155 deletions

View File

@ -18,9 +18,10 @@ import {
createUser,
createLicenseSetExpiryDateAndStatus,
getAccount,
getUser,
getUserFromExternalID,
getAccounts,
getUsers,
createAccountAndAdminUser,
} from './test/utility';
import { DataSource } from 'typeorm';
import { makeTestingModule } from '../../common/test/modules';
@ -33,7 +34,6 @@ import {
overrideBlobstorageService,
overrideSendgridService,
} from '../../common/test/overrides';
import { createAccountAndAdminUser } from '../users/test/utility';
describe('createAccount', () => {
let source: DataSource = null;
@ -61,7 +61,7 @@ describe('createAccount', () => {
const companyName = 'test_company_name';
const country = 'US';
const dealerAccountId = 1;
const email = 'dummy@dummy.dummy';
const email = 'dummy@example.com';
const password = 'dummy_password';
const username = 'dummy_username';
const role = 'none';
@ -82,7 +82,9 @@ describe('createAccount', () => {
},
});
overrideBlobstorageService(service, {
createContainer: async () => {},
createContainer: async () => {
return;
},
});
const { accountId, externalUserId, userId } = await service.createAccount(
@ -103,7 +105,7 @@ describe('createAccount', () => {
// DB内が想定通りになっているか確認
const account = await getAccount(source, accountId);
const user = await getUser(source, externalUserId);
const user = await getUserFromExternalID(source, externalUserId);
expect(account.company_name).toBe(companyName);
expect(account.country).toBe(country);
expect(account.parent_account_id).toBe(dealerAccountId);
@ -137,7 +139,7 @@ describe('createAccount', () => {
const companyName = 'test_company_name';
const country = 'US';
const dealerAccountId = 1;
const email = 'dummy@dummy.dummy';
const email = 'dummy@example.com';
const password = 'dummy_password';
const username = 'dummy_username';
const role = 'admin none';
@ -202,7 +204,7 @@ describe('createAccount', () => {
const companyName = 'test_company_name';
const country = 'US';
const dealerAccountId = 1;
const email = 'dummy@dummy.dummy';
const email = 'dummy@example.com';
const password = 'dummy_password';
const username = 'dummy_username';
const role = 'admin none';
@ -253,7 +255,7 @@ describe('createAccount', () => {
const companyName = 'test_company_name';
const country = 'US';
const dealerAccountId = 1;
const email = 'dummy@dummy.dummy';
const email = 'dummy@example.com';
const password = 'dummy_password';
const username = 'dummy_username';
const role = 'none';
@ -294,6 +296,240 @@ describe('createAccount', () => {
});
});
describe('createPartnerAccount', () => {
let source: DataSource = null;
beforeEach(async () => {
source = new DataSource({
type: 'sqlite',
database: ':memory:',
logging: false,
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
});
return source.initialize();
});
afterEach(async () => {
await source.destroy();
source = null;
});
it('パートナーを追加できる', async () => {
const module = await makeTestingModule(source);
const service = module.get<AccountsService>(AccountsService);
const adminExternalId = 'ADMIN0001';
// 第一階層のアカウントを作成
const { accountId: parentAccountId, tier } =
await createAccountAndAdminUser(source, adminExternalId);
const context = makeContext('uuid');
const companyName = 'test_company_name';
const country = 'US';
const email = 'partner@example.com';
const adminName = 'partner_admin';
const pertnerExternalId = 'PARTNER0001';
overrideAdB2cService(service, {
createUser: async () => {
return { sub: pertnerExternalId };
},
});
overrideSendgridService(service, {
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
sendMail: async () => {
return;
},
});
overrideBlobstorageService(service, {
createContainer: async () => {
return;
},
});
// DB上に容易されたデータが想定通りであるか確認
{
const accounts = await getAccounts(source);
expect(accounts.length).toBe(1);
}
const { accountId } = await service.createPartnerAccount(
context,
companyName,
country,
email,
adminName,
adminExternalId,
tier,
);
// DB上に作成されたデータが想定通りであるか確認
{
const accounts = await getAccounts(source);
expect(accounts.length).toBe(2);
const createdUser = await getUserFromExternalID(
source,
pertnerExternalId,
);
const createdAccount = await getAccount(source, accountId);
expect(createdAccount.company_name).toBe(companyName);
expect(createdAccount.country).toBe(country);
expect(createdAccount.parent_account_id).toBe(parentAccountId);
expect(createdAccount.tier).toBe(2);
expect(createdAccount.primary_admin_user_id).toBe(createdUser.id);
expect(createdAccount.secondary_admin_user_id).toBe(null);
}
});
it('Azure AD B2Cへの接続に失敗した結果アカウントの追加に失敗した場合、エラーとなる(500エラー)', async () => {
const module = await makeTestingModule(source);
const service = module.get<AccountsService>(AccountsService);
const adminExternalId = 'ADMIN0001';
// 第一階層のアカウントを作成
const { tier } = await createAccountAndAdminUser(source, adminExternalId);
const context = makeContext('uuid');
const companyName = 'test_company_name';
const country = 'US';
const email = 'partner@example.com';
const adminName = 'partner_admin';
overrideAdB2cService(service, {
createUser: async () => {
throw new Error();
},
});
overrideSendgridService(service, {
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
sendMail: async () => {
return;
},
});
overrideBlobstorageService(service, {
createContainer: async () => {
return;
},
});
// DB上に容易されたデータが想定通りであるか確認
{
const accounts = await getAccounts(source);
expect(accounts.length).toBe(1);
const users = await getUsers(source);
expect(users.length).toBe(1);
}
try {
await service.createPartnerAccount(
context,
companyName,
country,
email,
adminName,
adminExternalId,
tier,
);
} catch (e) {
if (e instanceof HttpException) {
expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR);
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
} else {
fail();
}
}
// DB上に作成されたデータが想定通りであるか確認
{
const accounts = await getAccounts(source);
expect(accounts.length).toBe(1);
const users = await getUsers(source);
expect(users.length).toBe(1);
}
});
it('既に登録済みのメールアドレスが原因でアカウントの追加に失敗した場合、エラーとなる(400エラー)', async () => {
const module = await makeTestingModule(source);
const service = module.get<AccountsService>(AccountsService);
const adminExternalId = 'ADMIN0001';
// 第一階層のアカウントを作成
const { tier } = await createAccountAndAdminUser(source, adminExternalId);
const context = makeContext('uuid');
const companyName = 'test_company_name';
const country = 'US';
const email = 'partner@example.com';
const adminName = 'partner_admin';
overrideAdB2cService(service, {
createUser: async () => {
// Conflictエラーを返す
return { reason: 'email', message: 'dummy' };
},
});
overrideSendgridService(service, {
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
sendMail: async () => {
return;
},
});
overrideBlobstorageService(service, {
createContainer: async () => {
return;
},
});
// DB上に容易されたデータが想定通りであるか確認
{
const accounts = await getAccounts(source);
expect(accounts.length).toBe(1);
const users = await getUsers(source);
expect(users.length).toBe(1);
}
try {
await service.createPartnerAccount(
context,
companyName,
country,
email,
adminName,
adminExternalId,
tier,
);
} catch (e) {
if (e instanceof HttpException) {
expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
expect(e.getResponse()).toEqual(makeErrorResponse('E010301'));
} else {
fail();
}
}
// DB上に作成されたデータが想定通りであるか確認
{
const accounts = await getAccounts(source);
expect(accounts.length).toBe(1);
const users = await getUsers(source);
expect(users.length).toBe(1);
}
});
});
describe('AccountsService', () => {
it('アカウントに紐づくライセンス情報を取得する', async () => {
const accountId = 1;
@ -540,92 +776,6 @@ describe('AccountsService', () => {
),
);
});
it('パートナーを追加できる', async () => {
const companyName = 'TEST_COMPANY';
const country = 'US';
const email = 'xxx@example.com';
const adminName = 'ADMIN';
const userId = '100';
const tier = 3;
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const userGroupsRepositoryMockValue =
makeDefaultUserGroupsRepositoryMockValue();
const adb2cParam = makeDefaultAdB2cMockValue();
const accountsRepositoryMockValue =
makeDefaultAccountsRepositoryMockValue();
const configMockValue = makeDefaultConfigValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const blobStorageMockValue = makeBlobStorageServiceMockValue();
const licensesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
userGroupsRepositoryMockValue,
adb2cParam,
configMockValue,
sendGridMockValue,
blobStorageMockValue,
licensesRepositoryMockValue,
);
expect(
await service.createPartnerAccount(
makeContext('uuid'),
companyName,
country,
email,
adminName,
userId,
tier + 1,
),
).toEqual(undefined);
});
it('アカウントの追加に失敗した場合、エラーとなる', async () => {
const companyName = 'TEST_COMPANY';
const country = 'US';
const email = 'xxx@example.com';
const adminName = 'ADMIN';
const userId = '100';
const tier = 3;
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const userGroupsRepositoryMockValue =
makeDefaultUserGroupsRepositoryMockValue();
const adb2cParam = makeDefaultAdB2cMockValue();
const accountsRepositoryMockValue =
makeDefaultAccountsRepositoryMockValue();
accountsRepositoryMockValue.createAccount = new Error('DB failed');
const configMockValue = makeDefaultConfigValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const blobStorageMockValue = makeBlobStorageServiceMockValue();
const licensesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
userGroupsRepositoryMockValue,
adb2cParam,
configMockValue,
sendGridMockValue,
blobStorageMockValue,
licensesRepositoryMockValue,
);
await expect(
service.createPartnerAccount(
makeContext('external_id'),
companyName,
country,
email,
adminName,
userId,
tier + 1,
),
).rejects.toEqual(
new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
),
);
});
});
const expectedAccountLisenceCounts = {
@ -1296,7 +1446,9 @@ describe('createPartnerAccount', () => {
},
});
overrideBlobstorageService(service, {
createContainer: async () => {},
createContainer: async () => {
return;
},
});
{
@ -1318,7 +1470,10 @@ describe('createPartnerAccount', () => {
const accounts = await getAccounts(source);
expect(accounts.length).toBe(2);
const partnerUser = await getUser(source, partnerExternalId);
const partnerUser = await getUserFromExternalID(
source,
partnerExternalId,
);
const partnerAccount = await getAccount(source, partnerUser.account_id);
expect(partnerAccount.company_name).toBe(companyName);
expect(partnerAccount.country).toBe(country);
@ -1358,7 +1513,9 @@ describe('createPartnerAccount', () => {
},
});
overrideBlobstorageService(service, {
createContainer: async () => {},
createContainer: async () => {
return;
},
});
try {
@ -1411,7 +1568,9 @@ describe('createPartnerAccount', () => {
},
});
overrideBlobstorageService(service, {
createContainer: async () => {},
createContainer: async () => {
return;
},
});
try {

View File

@ -389,12 +389,12 @@ export class AccountsService {
/**
*
* @param companyName
* @param country
* @param email
* @param adminName
* @param userId
* @param tier
* @param companyName
* @param country
* @param email
* @param adminName
* @param creatorUserId ID
* @param creatorAccountTier
*/
async createPartnerAccount(
context: Context,
@ -402,11 +402,11 @@ export class AccountsService {
country: string,
email: string,
adminName: string,
userId: string,
tier: number,
): Promise<void> {
creatorUserId: string,
creatorAccountTier: number,
): Promise<{ accountId: number }> {
this.logger.log(
`[IN] [${context.trackingId}] ${this.createPartnerAccount.name}`,
`[IN] [${context.trackingId}] ${this.createPartnerAccount.name} | params: { creatorUserId: ${creatorUserId}, creatorAccountTier: ${creatorAccountTier} };`,
);
try {
@ -414,8 +414,9 @@ export class AccountsService {
try {
// アクセストークンからユーザーIDを取得する
myAccountId = (await this.usersRepository.findUserByExternalId(userId))
.account_id;
myAccountId = (
await this.usersRepository.findUserByExternalId(creatorUserId)
).account_id;
} catch (e) {
this.logger.error(`error=${e}`);
if (e instanceof UserNotFoundError) {
@ -465,7 +466,7 @@ export class AccountsService {
companyName,
country,
myAccountId,
tier + 1,
creatorAccountTier + 1,
externalUser.sub,
USER_ROLES.NONE,
null,
@ -493,6 +494,7 @@ export class AccountsService {
text,
html,
);
return { accountId: newAccount.id };
} catch (e) {
this.logger.error(`error=${e}`);
this.logger.error('create partner account failed');

View File

@ -6,6 +6,73 @@ import {
LicenseOrder,
} from '../../../repositories/licenses/entity/license.entity';
// TODO: [PBI 2379] 他のUtilityからコピペしてきたもの。後日整理される前提。
export const createAccountAndAdminUser = async (
datasource: DataSource,
adminExternalId: string,
): Promise<{
accountId: number;
adminId: number;
role: string;
tier: number;
}> => {
const { identifiers: account_idf } = await datasource
.getRepository(Account)
.insert({
tier: 1,
country: 'JP',
delegation_permission: false,
locked: false,
company_name: 'test inc.',
verified: true,
deleted_at: '',
created_by: 'test_runner',
created_at: new Date(),
updated_by: 'updater',
updated_at: new Date(),
});
const account = account_idf.pop() as Account;
const { identifiers: user_idf } = await datasource
.getRepository(User)
.insert({
account_id: account.id,
external_id: adminExternalId,
role: 'admin none',
accepted_terms_version: '1.0',
email_verified: true,
auto_renew: true,
license_alert: true,
notification: true,
encryption: true,
encryption_password: 'password',
prompt: true,
created_by: 'test_runner',
created_at: new Date(),
updated_by: 'updater',
updated_at: new Date(),
});
const user = user_idf.pop() as User;
// Accountの管理者を設定する
await datasource.getRepository(Account).update(
{ id: user.account_id },
{
primary_admin_user_id: user.id,
},
);
const accountResult = await getAccount(datasource, account.id);
const userResult = await getUser(datasource, user.id);
return {
accountId: account.id,
adminId: user.id,
role: userResult.role,
tier: accountResult.tier,
};
};
export const createAccount = async (
datasource: DataSource,
parentAccountId: number,
@ -54,17 +121,38 @@ export const getAccounts = async (
};
/**
* ユーティリティ: 指定ExternalIdのアカウントを取得する
* ユーティリティ: 指定ExternalIdのユーザーを取得する
* @param dataSource
* @param externalId ID
* @returns
*/
export const getUser = async (dataSource: DataSource, externalId: string) => {
export const getUserFromExternalID = async (
dataSource: DataSource,
externalId: string,
) => {
return await dataSource.getRepository(User).findOne({
where: { external_id: externalId },
});
};
/**
* ユーティリティ: 指定Idのユーザーを取得する
* @param dataSource
* @param externalId ID
* @returns
*/
export const getUser = async (
datasource: DataSource,
id: number,
): Promise<User> => {
const user = await datasource.getRepository(User).findOne({
where: {
id: id,
},
});
return user;
};
/**
* ユーティリティ: すべてのユーザーを取得する
* @param dataSource

View File

@ -389,7 +389,7 @@ describe('音声ファイルダウンロードURL取得', () => {
it('Typistの場合、自身が担当するタスクでない場合エラー', async () => {
const { accountId } = await createAccount(source);
const { externalId, userId } = await createUser(
const { externalId } = await createUser(
source,
accountId,
'typist-user-external-id',

View File

@ -232,7 +232,7 @@ export class FilesService {
try {
// 国に応じたリージョンのBlobストレージにコンテナが存在するか確認
const isContainerExist = await this.blobStorageService.containerExists(
await this.blobStorageService.containerExists(
context,
accountId,
country,

View File

@ -199,7 +199,9 @@ export class LicensesController {
)
@Get('/allocatable')
async getAllocatableLicenses(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@Req() req: Request,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@Body() body: GetAllocatableLicensesRequest,
): Promise<GetAllocatableLicensesResponse> {
// TODO 仮の戻り値

View File

@ -297,7 +297,7 @@ export const makeDefaultSendGridlValue = (): SendGridMockValue => {
export const makeDefaultConfigValue = (): ConfigMockValue => {
return {
get: `test@example.co.jp`,
get: `test@example.com`,
};
};

View File

@ -93,11 +93,14 @@ export const createAccountAndAdminUser = async (
},
);
const accountResult = await getAccount(datasource, account.id);
const userResult = await getUser(datasource, user.id);
return {
accountId: account.id,
adminId: user.id,
role: user.role,
tier: account.tier,
role: userResult.role,
tier: accountResult.tier,
};
};
@ -155,6 +158,18 @@ export const createUser = async (
return { userId: user.id, externalId: external_id };
};
/**
* ユーティリティ: 指定IDのアカウントを取得する
* @param dataSource
* @param id ID
* @returns
*/
export const getAccount = async (dataSource: DataSource, id: number) => {
return await dataSource.getRepository(Account).findOne({
where: { id: id },
});
};
export const getUser = async (
datasource: DataSource,
id: number,

View File

@ -395,7 +395,7 @@ describe('UsersService.createUser', () => {
const name = 'test_user1';
const role = USER_ROLES.NONE;
const email = 'test1@example.co.jp';
const email = 'test1@example.com';
const autoRenew = true;
const licenseAlert = true;
const notification = true;
@ -475,7 +475,7 @@ describe('UsersService.createUser', () => {
const name = 'test_user2';
const role = USER_ROLES.AUTHOR;
const email = 'test2@example.co.jp';
const email = 'test2@example.com';
const autoRenew = true;
const licenseAlert = true;
const notification = true;
@ -563,7 +563,7 @@ describe('UsersService.createUser', () => {
const name = 'test_user2';
const role = USER_ROLES.AUTHOR;
const email = 'test2@example.co.jp';
const email = 'test2@example.com';
const autoRenew = true;
const licenseAlert = true;
const notification = true;
@ -650,7 +650,7 @@ describe('UsersService.createUser', () => {
const name = 'test_user3';
const role = USER_ROLES.TYPIST;
const email = 'test3@example.co.jp';
const email = 'test3@example.com';
const autoRenew = true;
const licenseAlert = true;
const notification = true;
@ -729,7 +729,7 @@ describe('UsersService.createUser', () => {
const name = 'test_user1';
const role = USER_ROLES.NONE;
const email = 'test1@example.co.jp';
const email = 'test1@example.com';
const autoRenew = true;
const licenseAlert = true;
const notification = true;
@ -804,7 +804,7 @@ describe('UsersService.createUser', () => {
const name = 'test_user1';
const role = USER_ROLES.NONE;
const email = 'test1@example.co.jp';
const email = 'test1@example.com';
const autoRenew = true;
const licenseAlert = true;
const notification = true;
@ -874,7 +874,7 @@ describe('UsersService.createUser', () => {
const name = 'test_user1';
const role = USER_ROLES.NONE;
const email = 'test1@example.co.jp';
const email = 'test1@example.com';
const autoRenew = true;
const licenseAlert = true;
const notification = true;
@ -956,7 +956,7 @@ describe('UsersService.createUser', () => {
const prompt = true;
const encryptionPassword = 'testPassword';
const email_1 = 'test_1@example.co.jp';
const email_1 = 'test_1@example.com';
const externalId_1 = '0001';
overrideAdB2cService(service, {
@ -1006,7 +1006,7 @@ describe('UsersService.createUser', () => {
}
// Azure Ad B2CのMockをユーザー2用に切り替える
const email_2 = 'test_1@example.co.jp';
const email_2 = 'test_1@example.com';
const externalId_2 = '0001';
overrideAdB2cService(service, {
createUser: async (
@ -1071,7 +1071,7 @@ describe('UsersService.createUser', () => {
const name = 'test_user2';
const role = USER_ROLES.AUTHOR;
const email = 'test2@example.co.jp';
const email = 'test2@example.com';
const autoRenew = true;
const licenseAlert = true;
const notification = true;

View File

@ -1,5 +1,5 @@
import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { DataSource, FindManyOptions, In } from 'typeorm';
import { Injectable, Logger } from '@nestjs/common';
import { DataSource, In } from 'typeorm';
import {
LicenseOrder,
License,

View File

@ -27,31 +27,6 @@ import { ExpirationThresholdDate } from '../../features/licenses/types/types';
export class UsersRepositoryService {
constructor(private dataSource: DataSource) {}
async create(
accountId: number,
externalUserId: string,
role: string,
acceptedTermsVersion: string,
): Promise<User> {
const user = new User();
{
user.account_id = accountId;
user.external_id = externalUserId;
user.role = role;
user.accepted_terms_version = acceptedTermsVersion;
}
const createdEntity = await this.dataSource.transaction(
async (entityManager) => {
const repo = entityManager.getRepository(User);
const newUser = repo.create(user);
const persisted = await repo.save(newUser);
return persisted;
},
);
return createdEntity;
}
/**
*
* @param user