import { HttpException, HttpStatus } from '@nestjs/common'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; import { makeAccountsServiceMock, makeBlobStorageServiceMockValue, makeDefaultAccountsRepositoryMockValue, makeDefaultAdB2cMockValue, makeDefaultLicensesRepositoryMockValue, makeDefaultSendGridlValue, makeDefaultUserGroupsRepositoryMockValue, makeDefaultUsersRepositoryMockValue, makeDefaultWorktypesRepositoryMockValue, } from './test/accounts.service.mock'; import { makeDefaultConfigValue } from '../users/test/users.service.mock'; import { createLicense, createLicenseOrder, createLicenseSetExpiryDateAndStatus, createOptionItems, createWorktype, getOptionItems, getSortCriteria, getTypistGroup, getTypistGroupMember, getWorktypes, } from './test/utility'; import { DataSource } from 'typeorm'; import { makeTestingModule } from '../../common/test/modules'; import { makeTestAccount, getAccount, getAccounts, getUserFromExternalId, getUsers, makeTestUser, makeHierarchicalAccounts, getUser, getLicenses, getUserArchive, } from '../../common/test/utility'; import { AccountsService } from './accounts.service'; import { Context, makeContext } from '../../common/log'; import { ADB2C_SIGN_IN_TYPE, LICENSE_ALLOCATED_STATUS, LICENSE_ISSUE_STATUS, LICENSE_TYPE, OPTION_ITEM_VALUE_TYPE, TIERS, USER_ROLES, WORKTYPE_MAX_COUNT, } from '../../constants'; import { License, LicenseAllocationHistory, LicenseOrder, } from '../../repositories/licenses/entity/license.entity'; import { overrideAccountsRepositoryService, overrideAdB2cService, overrideBlobstorageService, overrideSendgridService, } from '../../common/test/overrides'; import { AdB2cService } from '../../gateways/adb2c/adb2c.service'; import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service'; import { UserGroupsRepositoryService } from '../../repositories/user_groups/user_groups.repository.service'; import { createOrder, getLicenseArchive, getLicenseAllocationHistoryArchive, selectLicense, selectOrderLicense, } from '../licenses/test/utility'; import { WorktypesRepositoryService } from '../../repositories/worktypes/worktypes.repository.service'; import { AdB2cUser } from '../../gateways/adb2c/types/types'; import { Worktype } from '../../repositories/worktypes/entity/worktype.entity'; import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service'; import { UsersRepositoryService } from '../../repositories/users/users.repository.service'; import { createWorkflow, getWorkflows } from '../workflows/test/utility'; import { UsersService } from '../users/users.service'; import { truncateAllTable } from '../../common/test/init'; describe('createAccount', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウントを作成できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const externalId = 'test_external_id'; const companyName = 'test_company_name'; const country = 'US'; const dealerAccountId = 1; const email = 'dummy@example.com'; const password = 'dummy_password'; const username = 'dummy_username'; const role = 'none'; const acceptedEulaVersion = '1.0.0'; const acceptedPrivacyNoticeVersion = '1.0.0'; const acceptedDpaVersion = '1.0.0'; overrideAdB2cService(service, { createUser: async () => { return { sub: externalId }; }, }); overrideSendgridService(service, {}); overrideBlobstorageService(service, { createContainer: async () => { return; }, }); const { accountId, externalUserId, userId } = await service.createAccount( makeContext('uuid', 'requestId'), companyName, country, dealerAccountId, email, password, username, role, acceptedEulaVersion, acceptedPrivacyNoticeVersion, acceptedDpaVersion, ); // 作成したアカウントのIDが返ってくるか確認 expect(accountId).toBe(1); expect(externalUserId).toBe(externalId); expect(userId).toBe(1); // DB内が想定通りになっているか確認 const account = await getAccount(source, accountId); const user = await getUserFromExternalId(source, externalUserId); expect(account?.company_name).toBe(companyName); expect(account?.country).toBe(country); expect(account?.parent_account_id).toBe(dealerAccountId); expect(account?.tier).toBe(TIERS.TIER5); expect(account?.primary_admin_user_id).toBe(user?.id); expect(account?.secondary_admin_user_id).toBe(null); expect(user?.accepted_eula_version).toBe(acceptedEulaVersion); expect(user?.accepted_privacy_notice_version).toBe( acceptedPrivacyNoticeVersion, ); expect(user?.accepted_dpa_version).toBe(acceptedDpaVersion); expect(user?.account_id).toBe(accountId); expect(user?.role).toBe(role); }); it('アカウントを作成がAzure AD B2Cへの通信失敗によって失敗すると500エラーが発生する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideAdB2cService(service, { createUser: async () => { throw new Error(); }, }); overrideSendgridService(service, {}); const companyName = 'test_company_name'; const country = 'US'; const dealerAccountId = 1; const email = 'dummy@example.com'; const password = 'dummy_password'; const username = 'dummy_username'; const role = 'admin none'; const acceptedEulaVersion = '1.0.0'; const acceptedPrivacyNoticeVersion = '1.0.0'; const acceptedDpaVersion = '1.0.0'; overrideAdB2cService(service, { createUser: async () => { throw new Error(); }, }); overrideSendgridService(service, {}); overrideBlobstorageService(service, {}); try { await service.createAccount( makeContext('uuid', 'requestId'), companyName, country, dealerAccountId, email, password, username, role, acceptedEulaVersion, acceptedPrivacyNoticeVersion, acceptedDpaVersion, ); } 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(0); const users = await getUsers(source); expect(users.length).toBe(0); }); it('アカウントを作成がメールアドレス重複によって失敗すると400エラーが発生する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideAdB2cService(service, { createUser: async () => { // EmailのConflictエラーを返す return { reason: 'email', message: 'dummy' }; }, }); overrideSendgridService(service, {}); const companyName = 'test_company_name'; const country = 'US'; const dealerAccountId = 1; const email = 'dummy@example.com'; const password = 'dummy_password'; const username = 'dummy_username'; const role = 'admin none'; const acceptedEulaVersion = '1.0.0'; const acceptedPrivacyNoticeVersion = '1.0.0'; const acceptedDpaVersion = '1.0.0'; overrideAdB2cService(service, { createUser: async () => { // EmailのConflictエラーを返す return { reason: 'email', message: 'dummy' }; }, }); overrideSendgridService(service, {}); overrideBlobstorageService(service, {}); try { await service.createAccount( makeContext('uuid', 'requestId'), companyName, country, dealerAccountId, email, password, username, role, acceptedEulaVersion, acceptedPrivacyNoticeVersion, acceptedDpaVersion, ); } 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(0); const users = await getUsers(source); expect(users.length).toBe(0); }); it('アカウントを作成がDBへの通信失敗によって500エラーが発生した場合、リカバリ処理としてADB2Cユーザーを削除され、500エラーが返却される', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const externalId = 'test_external_id'; const companyName = 'test_company_name'; const country = 'US'; const dealerAccountId = 1; const email = 'dummy@example.com'; const password = 'dummy_password'; const username = 'dummy_username'; const role = 'none'; const acceptedEulaVersion = '1.0.0'; const acceptedPrivacyNoticeVersion = '1.0.0'; const acceptedDpaVersion = '1.0.0'; overrideAdB2cService(service, { createUser: async () => { return { sub: externalId }; }, deleteUser: jest.fn(), }); overrideSendgridService(service, {}); overrideAccountsRepositoryService(service, { createAccount: async () => { throw new Error(); }, }); try { await service.createAccount( makeContext('uuid', 'requestId'), companyName, country, dealerAccountId, email, password, username, role, acceptedEulaVersion, acceptedPrivacyNoticeVersion, acceptedDpaVersion, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } // DB内が想定通りになっているか確認 // DBのデータ作成で失敗しているので、DB内は空 const accounts = await getAccounts(source); expect(accounts.length).toBe(0); const users = await getUsers(source); expect(users.length).toBe(0); const sortCriteria = await getSortCriteria(source); expect(sortCriteria.length).toBe(0); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith( externalId, makeContext('uuid', 'requestId'), ); }); it('アカウントを作成がDBへの通信失敗によって500エラーが発生した場合、リカバリ処理が実行されるが、ADB2Cユーザー削除で失敗した場合、500エラーが返却される', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const externalId = 'test_external_id'; const companyName = 'test_company_name'; const country = 'US'; const dealerAccountId = 1; const email = 'dummy@dummy.dummy'; const password = 'dummy_password'; const username = 'dummy_username'; const role = 'none'; const acceptedEulaVersion = '1.0.0'; const acceptedPrivacyNoticeVersion = '1.0.0'; const acceptedDpaVersion = '1.0.0'; overrideAdB2cService(service, { createUser: async () => { return { sub: externalId }; }, deleteUser: jest.fn().mockRejectedValue(new Error()), }); overrideSendgridService(service, {}); overrideAccountsRepositoryService(service, { createAccount: async () => { throw new Error(); }, }); try { await service.createAccount( makeContext('uuid', 'requestId'), companyName, country, dealerAccountId, email, password, username, role, acceptedEulaVersion, acceptedPrivacyNoticeVersion, acceptedDpaVersion, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } // DB内が想定通りになっているか確認 // DBのデータ作成で失敗しているので、DB内は空 const accounts = await getAccounts(source); expect(accounts.length).toBe(0); const users = await getUsers(source); expect(users.length).toBe(0); const sortCriteria = await getSortCriteria(source); expect(sortCriteria.length).toBe(0); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith( externalId, makeContext('uuid', 'requestId'), ); }); it('アカウントを作成がBlobStorageへの通信失敗によって500エラーが発生した場合、リカバリ処理としてADB2C,DB上のデータが削除され、500エラーが返却される', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); b2cService.deleteUser = jest.fn(); // リカバリ処理の確認のため、deleteUserをモック化 const externalId = 'test_external_id'; const companyName = 'test_company_name'; const country = 'US'; const dealerAccountId = 1; const email = 'dummy@dummy.dummy'; const password = 'dummy_password'; const username = 'dummy_username'; const role = 'none'; const acceptedEulaVersion = '1.0.0'; const acceptedPrivacyNoticeVersion = '1.0.0'; const acceptedDpaVersion = '1.0.0'; overrideAdB2cService(service, { createUser: async () => { return { sub: externalId }; }, deleteUser: async () => { return; }, }); overrideSendgridService(service, {}); overrideBlobstorageService(service, { createContainer: async () => { throw new Error(); }, }); try { await service.createAccount( makeContext('uuid', 'requestId'), companyName, country, dealerAccountId, email, password, username, role, acceptedEulaVersion, acceptedPrivacyNoticeVersion, acceptedDpaVersion, ); } 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(0); const users = await getUsers(source); expect(users.length).toBe(0); const sortCriteria = await getSortCriteria(source); expect(sortCriteria.length).toBe(0); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith( externalId, makeContext('uuid', 'requestId'), ); }); it('アカウントを作成がBlobStorageへの通信失敗によって500エラーが発生した場合、リカバリ処理が実行されるが、そのリカバリ処理に失敗した場合、500エラーが返却される', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const externalId = 'test_external_id'; const companyName = 'test_company_name'; const country = 'US'; const dealerAccountId = 1; const email = 'dummy@dummy.dummy'; const password = 'dummy_password'; const username = 'dummy_username'; const role = 'none'; const acceptedEulaVersion = '1.0.0'; const acceptedPrivacyNoticeVersion = '1.0.0'; const acceptedDpaVersion = '1.0.0'; overrideAdB2cService(service, { createUser: async () => { return { sub: externalId }; }, deleteUser: jest.fn().mockRejectedValue(new Error()), }); overrideSendgridService(service, {}); overrideBlobstorageService(service, { createContainer: async () => { throw new Error(); }, }); overrideAccountsRepositoryService(service, { deleteAccount: async () => { throw new Error(); }, }); try { await service.createAccount( makeContext('uuid', 'requestId'), companyName, country, dealerAccountId, email, password, username, role, acceptedEulaVersion, acceptedPrivacyNoticeVersion, acceptedDpaVersion, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } // DB内が想定通りになっているか確認 // DB上のデータのリカバリ処理に失敗したため、DB上のデータは削除されない const accounts = await getAccounts(source); expect(accounts.length).toBe(1); const users = await getUsers(source); expect(users.length).toBe(1); const sortCriteria = await getSortCriteria(source); expect(sortCriteria.length).toBe(1); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith( externalId, makeContext('uuid', 'requestId'), ); }); it('アカウントを作成がSendGridへの通信失敗によって500エラーが発生した場合、リカバリ処理としてADB2C,DB上のデータとBlobストレージのコンテナが削除され、500エラーが返却される', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const blobstorageService = module.get(BlobstorageService); const externalId = 'test_external_id'; const companyName = 'test_company_name'; const country = 'US'; const dealerAccountId = 1; const email = 'dummy@dummy.dummy'; const password = 'dummy_password'; const username = 'dummy_username'; const role = 'none'; const acceptedEulaVersion = '1.0.0'; const acceptedPrivacyNoticeVersion = '1.0.0'; const acceptedDpaVersion = '1.0.0'; overrideAdB2cService(service, { createUser: async ( _context: Context, _email: string, _password: string, _username: string, ) => { // ユーザー作成時に指定したパラメータが正しく渡されていることを確認 expect(email).toEqual(_email); expect(username).toEqual(_username); return { sub: externalId }; }, deleteUser: jest.fn(), }); overrideSendgridService(service, { sendMail: async () => { throw new Error(); }, }); overrideBlobstorageService(service, { createContainer: async () => { return; }, deleteContainer: jest.fn(), }); overrideAccountsRepositoryService(service, {}); try { await service.createAccount( makeContext('uuid', 'requestId'), companyName, country, dealerAccountId, email, password, username, role, acceptedEulaVersion, acceptedPrivacyNoticeVersion, acceptedDpaVersion, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } // DB内が想定通りになっているか確認 // リカバリ処理によってADB2C,DB上のデータとBlobストレージのコンテナが削除される const accounts = await getAccounts(source); expect(accounts.length).toBe(0); const users = await getUsers(source); expect(users.length).toBe(0); const sortCriteria = await getSortCriteria(source); expect(sortCriteria.length).toBe(0); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith( externalId, makeContext('uuid', 'requestId'), ); // Blobストレージのコンテナ削除メソッドが呼ばれているか確認 expect(blobstorageService.deleteContainer).toBeCalledWith( makeContext('uuid', 'requestId'), 1, //新規作成したアカウントのID country, ); }); it('アカウントを作成がSendGridへの通信失敗によって500エラーが発生した場合、リカバリ処理が実行されるが、そのリカバリ処理に失敗した場合、500エラーが返却される', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const blobstorageService = module.get(BlobstorageService); const externalId = 'test_external_id'; const companyName = 'test_company_name'; const country = 'US'; const dealerAccountId = 1; const email = 'dummy@dummy.dummy'; const password = 'dummy_password'; const username = 'dummy_username'; const role = 'none'; const acceptedEulaVersion = '1.0.0'; const acceptedPrivacyNoticeVersion = '1.0.0'; const acceptedDpaVersion = '1.0.0'; overrideAdB2cService(service, { createUser: async () => { return { sub: externalId }; }, deleteUser: jest.fn().mockRejectedValue(new Error()), }); overrideSendgridService(service, { sendMail: async () => { throw new Error(); }, }); overrideBlobstorageService(service, { createContainer: async () => { return; }, deleteContainer: jest .fn() .mockRejectedValue(new Error('BlobStorage Error')), }); overrideAccountsRepositoryService(service, { deleteAccount: async () => { throw new Error(); }, }); try { await service.createAccount( makeContext('uuid', 'requestId'), companyName, country, dealerAccountId, email, password, username, role, acceptedEulaVersion, acceptedPrivacyNoticeVersion, acceptedDpaVersion, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } // DB内が想定通りになっているか確認 // リカバリ処理によってADB2C,DB上のデータとBlobストレージのコンテナが削除されない const accounts = await getAccounts(source); expect(accounts.length).toBe(1); const users = await getUsers(source); expect(users.length).toBe(1); const sortCriteria = await getSortCriteria(source); expect(sortCriteria.length).toBe(1); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith( externalId, makeContext('uuid', 'requestId'), ); // Blobストレージのコンテナ削除メソッドが呼ばれているか確認 expect(blobstorageService.deleteContainer).toBeCalledWith( makeContext('uuid', 'requestId'), 1, //新規作成したアカウントのID country, ); }); }); describe('createPartnerAccount', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('パートナーを追加できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const adminExternalId = 'ADMIN0001'; // 第一階層のアカウントを作成 const { account: parent } = await makeTestAccount( source, { tier: 1 }, { external_id: adminExternalId }, ); const context = makeContext('uuid', 'requestId'); 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, {}); 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, parent.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(parent.id); 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 () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const adminExternalId = 'ADMIN0001'; // 第一階層のアカウントを作成 const { account: parent } = await makeTestAccount( source, { tier: 1 }, { external_id: adminExternalId }, ); const context = makeContext('uuid', 'requestId'); 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, {}); 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, parent.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('DBへの通信失敗によって500エラーが発生した場合、リカバリ処理としてADB2Cユーザーを削除され、500エラーが返却される', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const adminExternalId = 'ADMIN0001'; // 第一階層のアカウントを作成 const { account: parent } = await makeTestAccount( source, { tier: 1 }, { external_id: adminExternalId }, ); const context = makeContext('uuid', 'requestId'); const companyName = 'test_company_name'; const country = 'US'; const email = 'partner@example.com'; const adminName = 'partner_admin'; const partnerExternalId = 'partner_external_id'; overrideAdB2cService(service, { createUser: async () => { return { sub: partnerExternalId }; }, deleteUser: jest.fn(), }); overrideSendgridService(service, {}); overrideAccountsRepositoryService(service, { createAccount: async () => { throw new Error('DB Error'); }, }); 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, parent.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); expect(accounts[0].tier).toBe(1); const users = await getUsers(source); expect(users.length).toBe(1); expect(users[0].external_id).toBe(adminExternalId); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith(partnerExternalId, context); } }); it('DBへの通信失敗によって500エラーが発生した場合、リカバリ処理が実行されるが、ADB2Cユーザー削除で失敗した場合、500エラーが返却される', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const adminExternalId = 'ADMIN0001'; // 第一階層のアカウントを作成 const { account: parent } = await makeTestAccount( source, { tier: 1 }, { external_id: adminExternalId }, ); const context = makeContext('uuid', 'requestId'); const companyName = 'test_company_name'; const country = 'US'; const email = 'partner@example.com'; const adminName = 'partner_admin'; const partnerExternalId = 'partner_external_id'; overrideAdB2cService(service, { createUser: async () => { return { sub: partnerExternalId }; }, deleteUser: jest.fn().mockRejectedValue(new Error('ADB2C Error')), }); overrideSendgridService(service, {}); overrideAccountsRepositoryService(service, { createAccount: async () => { throw new Error('DB Error'); }, }); 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, parent.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); expect(accounts[0].tier).toBe(1); const users = await getUsers(source); expect(users.length).toBe(1); expect(users[0].external_id).toBe(adminExternalId); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith(partnerExternalId, context); } }); it('BlobStorageへの通信失敗が原因でアカウントの追加に失敗した場合、リカバリ処理としてADB2C,DB上のデータが削除され、500エラーが返却される', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const parentExternalId = 'parent_external_id'; // 第一階層のアカウントを作成 const { account: parent } = await makeTestAccount( source, { tier: 1 }, { external_id: parentExternalId }, ); const context = makeContext(parentExternalId, 'requestId'); const partnerExternalId = 'partner_external_id'; const companyName = 'partner_company_name'; const country = 'US'; const email = 'partner@example.com'; const username = 'partner_username'; overrideAdB2cService(service, { createUser: async () => { return { sub: partnerExternalId }; }, deleteUser: jest.fn(), }); overrideSendgridService(service, {}); overrideBlobstorageService(service, { createContainer: async () => { throw new Error(); }, }); // DB上に用意されたデータが想定通りであるか確認 { const accounts = await getAccounts(source); expect(accounts.length).toBe(1); } try { await service.createPartnerAccount( context, companyName, country, email, username, parentExternalId, TIERS.TIER1, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } { const accounts = await getAccounts(source); expect(accounts.length).toBe(1); expect(accounts[0].tier).toBe(parent.tier); const users = await getUsers(source); expect(users.length).toBe(1); expect(users[0].external_id).toBe(parentExternalId); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith(partnerExternalId, context); } }); it('BlobStorageへの通信失敗が原因でアカウントの追加に失敗した場合、リカバリ処理が実行されるが、そのリカバリ処理に失敗した場合、500エラーが返却される', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const parentExternalId = 'parent_external_id'; // 第一階層のアカウントを作成 const { account: parent } = await makeTestAccount( source, { tier: 1 }, { external_id: parentExternalId }, ); const context = makeContext(parentExternalId, 'requestId'); const partnerExternalId = 'partner_external_id'; const companyName = 'partner_company_name'; const country = 'US'; const email = 'partner@example.com'; const username = 'partner_username'; overrideAdB2cService(service, { createUser: async () => { return { sub: partnerExternalId }; }, deleteUser: jest.fn().mockRejectedValue(new Error('ADB2C Error')), }); overrideSendgridService(service, {}); overrideAccountsRepositoryService(service, { deleteAccount: jest.fn().mockRejectedValue(new Error('DB Error')), }); overrideBlobstorageService(service, { createContainer: async () => { throw new Error(); }, }); // DB上に用意されたデータが想定通りであるか確認 { const accounts = await getAccounts(source); expect(accounts.length).toBe(1); } try { await service.createPartnerAccount( context, companyName, country, email, username, parentExternalId, TIERS.TIER1, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } { // DB上に作成されたデータが想定通りであるか確認 // リカバリ処理が失敗した場合、DB上のデータは削除されない const accounts = await getAccounts(source); expect(accounts.length).toBe(2); expect(accounts[0].tier).toBe(parent.tier); expect(accounts[1].tier).toBe(parent.tier + 1); const users = await getUsers(source); expect(users.length).toBe(2); expect(users[0].external_id).toBe(parentExternalId); expect(users[1].external_id).toBe(partnerExternalId); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith(partnerExternalId, context); } }); it('SendGridへの通信失敗によって500エラーが発生した場合、リカバリ処理としてADB2C,DB上のデータ,コンテナが削除され、500エラーが返却される', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const blobstorageService = module.get(BlobstorageService); const parentExternalId = 'parent_external_id'; // 第一階層のアカウントを作成 const { account: parent } = await makeTestAccount( source, { tier: 1 }, { external_id: parentExternalId }, ); const context = makeContext(parentExternalId, 'requestId'); const partnerExternalId = 'partner_external_id'; const companyName = 'partner_company_name'; const country = 'US'; const email = 'partner@example.com'; const username = 'partner_username'; overrideAdB2cService(service, { createUser: async () => { return { sub: partnerExternalId }; }, deleteUser: jest.fn(), }); overrideSendgridService(service, { sendMail: async () => { throw new Error(); }, }); overrideBlobstorageService(service, { createContainer: async () => { return; }, deleteContainer: jest.fn(), }); // DB上に用意されたデータが想定通りであるか確認 { const accounts = await getAccounts(source); expect(accounts.length).toBe(1); } try { await service.createPartnerAccount( context, companyName, country, email, username, parentExternalId, TIERS.TIER1, ); } 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); expect(accounts[0].tier).toBe(parent.tier); const users = await getUsers(source); expect(users.length).toBe(1); expect(users[0].external_id).toBe(parentExternalId); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith(partnerExternalId, context); // コンテナ削除メソッドが呼ばれているか確認 expect(blobstorageService.deleteContainer).toBeCalledWith( context, 2, //新規作成したパートナーのアカウントID country, ); } }); it('SendGridへの通信失敗によって500エラーが発生した場合、リカバリ処理が実行されるが、そのリカバリ処理に失敗した場合、500エラーが返却される', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const blobstorageService = module.get(BlobstorageService); const parentExternalId = 'parent_external_id'; // 第一階層のアカウントを作成 const { account: parent } = await makeTestAccount( source, { tier: 1 }, { external_id: parentExternalId }, ); const context = makeContext(parentExternalId, 'requestId'); const partnerExternalId = 'partner_external_id'; const companyName = 'partner_company_name'; const country = 'US'; const email = 'partner@example.com'; const username = 'partner_username'; overrideAdB2cService(service, { createUser: async () => { return { sub: partnerExternalId }; }, deleteUser: jest.fn().mockRejectedValue(new Error('ADB2C Error')), }); overrideSendgridService(service, { sendMail: async () => { throw new Error(); }, }); overrideAccountsRepositoryService(service, { deleteAccount: jest.fn().mockRejectedValue(new Error('DB Error')), }); overrideBlobstorageService(service, { createContainer: async () => { return; }, deleteContainer: jest.fn().mockRejectedValue(new Error('Blob Error')), }); // DB上に用意されたデータが想定通りであるか確認 { const accounts = await getAccounts(source); expect(accounts.length).toBe(1); } try { await service.createPartnerAccount( context, companyName, country, email, username, parentExternalId, TIERS.TIER1, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } { // DB上に作成されたデータが想定通りであるか確認 // リカバリ処理が失敗したため、DB上のデータは削除されていない const accounts = await getAccounts(source); expect(accounts.length).toBe(2); expect(accounts[0].tier).toBe(parent.tier); expect(accounts[1].tier).toBe(parent.tier + 1); const users = await getUsers(source); expect(users.length).toBe(2); expect(users[0].external_id).toBe(parentExternalId); expect(users[1].external_id).toBe(partnerExternalId); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith(partnerExternalId, context); // コンテナ削除メソッドが呼ばれているか確認 expect(blobstorageService.deleteContainer).toBeCalledWith( context, 2, //新規作成したパートナーのアカウントID country, ); } }); it('既に登録済みのメールアドレスが原因でアカウントの追加に失敗した場合、エラーとなる(400エラー)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const adminExternalId = 'ADMIN0001'; // 第一階層のアカウントを作成 const { account: parent } = await makeTestAccount( source, { tier: 1 }, { external_id: adminExternalId }, ); const context = makeContext('uuid', 'requestId'); 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, {}); 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, parent.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; const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const userGroupsRepositoryMockValue = makeDefaultUserGroupsRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const accountsRepositoryMockValue = makeDefaultAccountsRepositoryMockValue(); accountsRepositoryMockValue.getLicenseSummaryInfo = new Error(); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const worktypesRepositoryMockValue = makeDefaultWorktypesRepositoryMockValue(); const service = await makeAccountsServiceMock( accountsRepositoryMockValue, usersRepositoryMockValue, userGroupsRepositoryMockValue, adb2cParam, configMockValue, sendGridMockValue, blobStorageMockValue, licensesRepositoryMockValue, worktypesRepositoryMockValue, ); const context = makeContext(`uuidv4`, 'requestId'); await expect(service.getLicenseSummary(context, accountId)).rejects.toEqual( new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, ), ); }); it('アカウントに紐づくタイピストユーザーを取得できる', async () => { const externalId = 'externalId'; 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 worktypesRepositoryMockValue = makeDefaultWorktypesRepositoryMockValue(); const service = await makeAccountsServiceMock( accountsRepositoryMockValue, usersRepositoryMockValue, userGroupsRepositoryMockValue, adb2cParam, configMockValue, sendGridMockValue, blobStorageMockValue, licensesRepositoryMockValue, worktypesRepositoryMockValue, ); const context = makeContext(`uuidv4`, 'requestId'); expect(await service.getTypists(context, externalId)).toEqual([ { id: 1, name: 'Typist1' }, { id: 2, name: 'Typist2' }, { id: 3, name: 'Typist3' }, ]); }); it('ユーザー取得に失敗した場合、エラーとなる', async () => { const externalId = 'externalId'; const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); usersRepositoryMockValue.findTypistUsers = new Error(); const userGroupsRepositoryMockValue = makeDefaultUserGroupsRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const accountsRepositoryMockValue = makeDefaultAccountsRepositoryMockValue(); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const worktypesRepositoryMockValue = makeDefaultWorktypesRepositoryMockValue(); const service = await makeAccountsServiceMock( accountsRepositoryMockValue, usersRepositoryMockValue, userGroupsRepositoryMockValue, adb2cParam, configMockValue, sendGridMockValue, blobStorageMockValue, licensesRepositoryMockValue, worktypesRepositoryMockValue, ); const context = makeContext(`uuidv4`, 'requestId'); await expect(service.getTypists(context, externalId)).rejects.toEqual( new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, ), ); }); it('ADB2Cからのユーザーの取得に失敗した場合、エラーとなる', async () => { const externalId = 'externalId'; const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const userGroupsRepositoryMockValue = makeDefaultUserGroupsRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); adb2cParam.getUsers = new Error(); const accountsRepositoryMockValue = makeDefaultAccountsRepositoryMockValue(); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const worktypesRepositoryMockValue = makeDefaultWorktypesRepositoryMockValue(); const service = await makeAccountsServiceMock( accountsRepositoryMockValue, usersRepositoryMockValue, userGroupsRepositoryMockValue, adb2cParam, configMockValue, sendGridMockValue, blobStorageMockValue, licensesRepositoryMockValue, worktypesRepositoryMockValue, ); const context = makeContext(`uuidv4`, 'requestId'); await expect(service.getTypists(context, externalId)).rejects.toEqual( new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, ), ); }); it('アカウントに紐づくユーザーグループを取得できる', async () => { const externalId = 'externalId'; const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const accountsRepositoryMockValue = makeDefaultAccountsRepositoryMockValue(); const userGroupsRepositoryMockValue = makeDefaultUserGroupsRepositoryMockValue(); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const worktypesRepositoryMockValue = makeDefaultWorktypesRepositoryMockValue(); const service = await makeAccountsServiceMock( accountsRepositoryMockValue, usersRepositoryMockValue, userGroupsRepositoryMockValue, adb2cParam, configMockValue, sendGridMockValue, blobStorageMockValue, licensesRepositoryMockValue, worktypesRepositoryMockValue, ); const context = makeContext(`uuidv4`, 'requestId'); expect(await service.getTypistGroups(context, externalId)).toEqual([ { id: 1, name: 'GroupA' }, { id: 2, name: 'GroupB' }, ]); }); it('ユーザーの取得に失敗した場合、エラーとなること', async () => { const externalId = 'externalId'; const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); usersRepositoryMockValue.findUserByExternalId = new Error('DB failed'); const adb2cParam = makeDefaultAdB2cMockValue(); const accountsRepositoryMockValue = makeDefaultAccountsRepositoryMockValue(); const userGroupsRepositoryMockValue = makeDefaultUserGroupsRepositoryMockValue(); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const worktypesRepositoryMockValue = makeDefaultWorktypesRepositoryMockValue(); const service = await makeAccountsServiceMock( accountsRepositoryMockValue, usersRepositoryMockValue, userGroupsRepositoryMockValue, adb2cParam, configMockValue, sendGridMockValue, blobStorageMockValue, licensesRepositoryMockValue, worktypesRepositoryMockValue, ); const context = makeContext(`uuidv4`, 'requestId'); await expect(service.getTypistGroups(context, externalId)).rejects.toEqual( new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, ), ); }); it('ユーザーグループの取得に失敗した場合、エラーとなること', async () => { const externalId = 'externalId'; const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const accountsRepositoryMockValue = makeDefaultAccountsRepositoryMockValue(); const userGroupsRepositoryMockValue = makeDefaultUserGroupsRepositoryMockValue(); userGroupsRepositoryMockValue.getUserGroups = new Error('DB failed'); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const worktypesRepositoryMockValue = makeDefaultWorktypesRepositoryMockValue(); const service = await makeAccountsServiceMock( accountsRepositoryMockValue, usersRepositoryMockValue, userGroupsRepositoryMockValue, adb2cParam, configMockValue, sendGridMockValue, blobStorageMockValue, licensesRepositoryMockValue, worktypesRepositoryMockValue, ); const context = makeContext(`uuidv4`, 'requestId'); await expect(service.getTypistGroups(context, externalId)).rejects.toEqual( new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, ), ); }); }); describe('getLicenseSummary', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('第五階層のライセンス情報を取得する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 親アカウントと子アカウント2つ作成 const { id: parentAccountId } = ( await makeTestAccount(source, { parent_account_id: 0, tier: 4, company_name: 'PARENTCORP', }) ).account; const { id: childAccountId1 } = ( await makeTestAccount(source, { parent_account_id: parentAccountId, tier: 5, company_name: 'CHILDCORP1', }) ).account; const { id: childAccountId2 } = ( await makeTestAccount(source, { parent_account_id: parentAccountId, tier: 5, company_name: 'CHILDCORP2', }) ).account; // 有効期限が14日後のライセンスを追加(5ライセンス) const expiryDate = new Date(); expiryDate.setDate(expiryDate.getDate() + 14); expiryDate.setHours(23, 59, 59, 0); for (let i = 0; i < 5; i++) { await createLicenseSetExpiryDateAndStatus( source, childAccountId1, expiryDate, 'Allocated', 1, ); await createLicenseSetExpiryDateAndStatus( source, childAccountId2, expiryDate, 'Allocated', 1, ); } // 有効期限が迫っていないライセンスを追加(子1:各ステータスのライセンスを1つずつ、計4つ) const status = ['Unallocated', 'Allocated', 'Reusable', 'Deleted']; status.forEach(async (element) => { if (!source) fail(); await createLicenseSetExpiryDateAndStatus( source, childAccountId1, new Date(2037, 1, 1, 23, 59, 59), element, 1, ); }); // 有効期限が迫っていないライセンスを追加(子2:Unallocatedを10件) for (let i = 0; i < 10; i++) { await createLicenseSetExpiryDateAndStatus( source, childAccountId2, new Date(2037, 1, 1, 23, 59, 59), 'Unallocated', ); } // 有効期限未設定のライセンスを1件追加(子1) await createLicenseSetExpiryDateAndStatus( source, childAccountId1, null, 'Unallocated', ); // childAccountId1にallocatedLicenseを追加 await createLicenseSetExpiryDateAndStatus( source, childAccountId1, null, 'Allocated', 1, ); // childAccountId1からライセンスの注文を100件を追加 await createLicenseOrder(source, childAccountId1, parentAccountId, 100); const service = module.get(AccountsService); const context = makeContext(`uuidv4`, 'xxx-xxx-xxx-xxx', 'requestId'); const resultChild1 = await service.getLicenseSummary( context, childAccountId1, ); const resultChild2 = await service.getLicenseSummary( context, childAccountId2, ); expect(resultChild1).toEqual({ totalLicense: 3, allocatedLicense: 7, reusableLicense: 1, freeLicense: 2, expiringWithin14daysLicense: 5, issueRequesting: 100, numberOfRequesting: 1, storageSize: 0, usedSize: 0, shortage: 2, isStorageAvailable: false, }); expect(resultChild2).toEqual({ totalLicense: 10, allocatedLicense: 5, reusableLicense: 0, freeLicense: 10, expiringWithin14daysLicense: 5, issueRequesting: 0, numberOfRequesting: 0, storageSize: 0, usedSize: 0, shortage: 0, isStorageAvailable: false, }); }); }); describe('getPartnerAccount', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('パラメータのアカウント自身と子アカウントに紐つくライセンス情報を取得する(第五のshortage確認)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 親アカウントと子アカウント2つ作成 const { id: parentAccountId } = ( await makeTestAccount(source, { parent_account_id: 0, tier: 4, company_name: 'PARENTCORP', }) ).account; const { id: childAccountId1 } = ( await makeTestAccount(source, { parent_account_id: parentAccountId, tier: 5, company_name: 'CHILDCORP1', }) ).account; const { id: childAccountId2 } = ( await makeTestAccount(source, { parent_account_id: parentAccountId, tier: 5, company_name: 'CHILDCORP2', }) ).account; const { id: childAccountId3 } = ( await makeTestAccount(source, { parent_account_id: parentAccountId, tier: 5, company_name: 'CHILDCORP3', }) ).account; // 有効期限が14日後のライセンスを追加(5ライセンス) const expiryDate = new Date(); expiryDate.setDate(expiryDate.getDate() + 14); expiryDate.setHours(23, 59, 59, 0); for (let i = 0; i < 5; i++) { await createLicenseSetExpiryDateAndStatus( source, childAccountId1, expiryDate, 'Allocated', ); await createLicenseSetExpiryDateAndStatus( source, childAccountId2, expiryDate, 'Allocated', ); } // 有効期限が15日後のライセンスを追加 expiryDate.setDate(expiryDate.getDate() + 15); expiryDate.setHours(23, 59, 59, 0); await createLicenseSetExpiryDateAndStatus( source, childAccountId3, expiryDate, 'Allocated', ); // 有効期限が迫っていないライセンスを追加(子1:各ステータスのライセンスを1つずつ、計4つ) const status = ['Unallocated', 'Allocated', 'Reusable', 'Deleted']; status.forEach(async (element) => { if (!source) fail(); await createLicenseSetExpiryDateAndStatus( source, childAccountId1, new Date(2037, 1, 1, 23, 59, 59), element, ); }); // 有効期限が迫っていないライセンスを追加(子2:Unallocatedを10件) for (let i = 0; i < 10; i++) { await createLicenseSetExpiryDateAndStatus( source, childAccountId2, new Date(2037, 1, 1, 23, 59, 59), 'Unallocated', ); } // 有効期限未設定のライセンスを1件追加(子1) await createLicenseSetExpiryDateAndStatus( source, childAccountId1, null, 'Unallocated', ); const service = module.get(AccountsService); const accountId = parentAccountId; const offset = 0; const limit = 20; const context = makeContext(`uuidv4`, 'requestId'); const response = await service.getPartnerLicenses( context, limit, offset, accountId, ); // 有効期限間近(5件)+ 有効期限間近でない(3件)'Unallocated', 'Allocated', 'Reusable'+ 有効期限未設定(1件) → 3件 expect(response.childrenPartnerLicenses[0].stockLicense).toBe(3); // 有効期限間近(5件) - {有効期限間近でない未割当(2件)'Unallocated', 'Reusable'+ 有効期限未設定(1件)} → 2件 expect(response.childrenPartnerLicenses[0].shortage).toBe(2); // 有効期限間近(5件)+ 有効期限間近でない(10件) → 10件 expect(response.childrenPartnerLicenses[1].stockLicense).toBe(10); // 有効期限間近(5件)- (有効期限間近でない未割当(10件)'Unallocated' + 有効期限未設定(1件)) → -5件 → 0件 expect(response.childrenPartnerLicenses[1].shortage).toBe(0); // 有効期限が15日後のライセンス(1件)'Allocated' expect(response.childrenPartnerLicenses[2].stockLicense).toBe(0); // 有効期限が15日後のものはshortageにカウントされない expect(response.childrenPartnerLicenses[2].shortage).toBe(0); }); }); describe('getOrderHistories', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('指定したアカウントIDの注文履歴情報を取得できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const targetAccountId = 10; const targetParentAccountId = 14; const now = new Date(); // 現在の時刻を取得 // 注文履歴情報の作成 await createLicenseOrder( source, targetAccountId, targetParentAccountId, 10, 'TEST001', new Date(now.getTime() + 60 * 60 * 1000), ); await createLicenseOrder( source, targetAccountId, targetParentAccountId, 10, 'TEST002', new Date(now.getTime() + 60 * 60 * 1000 * 2), ); await createLicenseOrder( source, targetAccountId, targetParentAccountId, 10, 'TEST003', new Date(now.getTime() + 60 * 60 * 1000 * 3), ); await createLicenseOrder( source, targetAccountId, targetParentAccountId, 10, 'TEST004', new Date(now.getTime() + 60 * 60 * 1000 * 4), ); await createLicenseOrder( source, targetAccountId, targetParentAccountId, 10, 'TEST005', new Date(now.getTime() + 60 * 60 * 1000 * 5), ); const service = module.get(AccountsService); const accountId = targetAccountId; const offset = 1; const limit = 2; const context = makeContext(`uuidv4`, 'requestId'); const response = await service.getOrderHistories( context, limit, offset, accountId, ); expect(response.total).toBe(5); expect(response.orderHistories[0].poNumber).toBe('TEST004'); expect(response.orderHistories[1].poNumber).toBe('TEST003'); }); it('注文履歴情報の取得に失敗した場合、エラーとなる', async () => { const limit = 0; const offset = 0; const accountId = 0; const worktypesRepositoryMockValue = makeDefaultWorktypesRepositoryMockValue(); const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); usersRepositoryMockValue.findTypistUsers = new Error(); const userGroupsRepositoryMockValue = makeDefaultUserGroupsRepositoryMockValue(); const adb2cParam = makeDefaultAdB2cMockValue(); const accountsRepositoryMockValue = makeDefaultAccountsRepositoryMockValue(); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); licensesRepositoryMockValue.getLicenseOrderHistoryInfo = new Error( 'DB failed', ); const service = await makeAccountsServiceMock( accountsRepositoryMockValue, usersRepositoryMockValue, userGroupsRepositoryMockValue, adb2cParam, configMockValue, sendGridMockValue, blobStorageMockValue, licensesRepositoryMockValue, worktypesRepositoryMockValue, ); const context = makeContext(`uuidv4`, 'requestId'); await expect( service.getOrderHistories(context, limit, offset, accountId), ).rejects.toEqual( new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, ), ); }); }); describe('issueLicense', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('指定した注文を発行済みにする', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideSendgridService(service, {}); const now = new Date(); // 親と子アカウントを作成する const { id: parentAccountId } = ( await makeTestAccount(source, { parent_account_id: 0, tier: 2, company_name: 'PARENTCORP', }) ).account; const { id: childAccountId } = ( await makeTestAccount(source, { parent_account_id: parentAccountId, tier: 3, company_name: 'CHILDCORP1', }) ).account; // 親と子のユーザーを作成する const user = await makeTestUser(source, { account_id: parentAccountId, external_id: 'userId-parent', role: 'admin', }); // 親のライセンスを作成する(3個) await createLicense( source, 1, null, parentAccountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, 1, null, null, ); await createLicense( source, 2, null, parentAccountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, 1, null, null, ); await createLicense( source, 3, null, parentAccountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, 1, null, null, ); // 子から親への注文を作成する(2個) await createLicenseOrder( source, childAccountId, parentAccountId, 2, 'TEST001', new Date(now.getTime() + 60 * 60 * 1000), ); const context = makeContext('userId-parent', 'requestId'); // 注文を発行済みにする await service.issueLicense( context, childAccountId, user?.external_id ?? '', 2, 'TEST001', ); const issuedLicenses = await source.getRepository(License).find({ where: { account_id: childAccountId, status: 'Unallocated', type: 'NORMAL', }, }); expect(issuedLicenses.length).toEqual(2); }); it('既に注文が発行済みの場合、エラーとなる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideSendgridService(service, {}); const now = new Date(); // 親と子アカウントを作成する const { id: parentAccountId } = ( await makeTestAccount(source, { parent_account_id: 0, tier: 2, company_name: 'PARENTCORP', }) ).account; const { id: childAccountId } = ( await makeTestAccount(source, { parent_account_id: parentAccountId, tier: 3, company_name: 'CHILDCORP1', }) ).account; // 親と子のユーザーを作成する const user = await makeTestUser(source, { account_id: parentAccountId, external_id: 'userId-parent', role: 'admin', }); // 親のライセンスを作成する(3個) await createLicense( source, 1, null, parentAccountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, 1, null, null, ); await createLicense( source, 2, null, parentAccountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, 1, null, null, ); await createLicense( source, 3, null, parentAccountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, 1, null, null, ); // 子から親への注文を作成する(2個) await createLicenseOrder( source, childAccountId, parentAccountId, 2, 'TEST001', new Date(now.getTime() + 60 * 60 * 1000), ); const context = makeContext('userId-parent', 'requestId'); // 注文を発行済みにする await service.issueLicense( context, childAccountId, user?.external_id ?? '', 2, 'TEST001', ); //再度同じ処理を行う await expect( service.issueLicense( context, childAccountId, user?.external_id ?? '', 2, 'TEST001', ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010803'), HttpStatus.BAD_REQUEST), ); }); it('ライセンスが不足している場合、エラーとなる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const now = new Date(); // 親と子アカウントを作成する const { id: parentAccountId } = ( await makeTestAccount(source, { parent_account_id: 0, tier: 2, company_name: 'PARENTCORP', }) ).account; const { id: childAccountId } = ( await makeTestAccount(source, { parent_account_id: parentAccountId, tier: 3, company_name: 'CHILDCORP1', }) ).account; // 親と子のユーザーを作成する const user = await makeTestUser(source, { account_id: parentAccountId, external_id: 'userId-parent', role: 'admin', }); // 親のライセンスを作成する(3個) await createLicense( source, 1, null, parentAccountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, 1, null, null, ); await createLicense( source, 2, null, parentAccountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, 1, null, null, ); await createLicense( source, 3, null, parentAccountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, 1, null, null, ); // 子から親への注文を作成する(4個) await createLicenseOrder( source, childAccountId, parentAccountId, 4, 'TEST001', new Date(now.getTime() + 60 * 60 * 1000), ); const context = makeContext('userId-parent', 'requestId'); // 注文を発行済みにする await expect( service.issueLicense( context, childAccountId, user?.external_id ?? '', 2, 'TEST001', ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010804'), HttpStatus.BAD_REQUEST), ); }); }); describe('getDealers', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('Dealerを取得できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { id: accountId_1 } = ( await makeTestAccount(source, { parent_account_id: 1, tier: TIERS.TIER4, country: 'JP', company_name: 'DEALER_1', }) ).account; const { id: accountId_2 } = ( await makeTestAccount(source, { parent_account_id: 2, tier: TIERS.TIER4, country: 'JP', company_name: 'DEALER_2', }) ).account; const { id: accountId_3 } = ( await makeTestAccount(source, { parent_account_id: 3, tier: TIERS.TIER4, country: 'JP', company_name: 'DEALER_3', }) ).account; const service = module.get(AccountsService); const context = makeContext(`uuidv4`, 'requestId'); expect(await service.getDealers(context)).toEqual({ dealers: [ { country: 'JP', id: accountId_1, name: 'DEALER_1', }, { country: 'JP', id: accountId_2, name: 'DEALER_2', }, { country: 'JP', id: accountId_3, name: 'DEALER_3', }, ], }); }); it('0件でもDealerを取得できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const context = makeContext(`uuidv4`, 'requestId'); expect(await service.getDealers(context)).toEqual({ dealers: [], }); }); }); describe('createTypistGroup', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('TypistGroupを作成できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const adminExternalId = 'admin-external-id'; // 第五階層のアカウント作成 const { id: accountId } = ( await makeTestAccount( source, { tier: 5 }, { external_id: adminExternalId }, ) ).account; // 作成したアカウントにユーザーを3名追加する const typiptUserExternalIds = [ 'typist-user-external-id1', 'typist-user-external-id2', 'typist-user-external-id3', ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { const user = await makeTestUser(source, { account_id: accountId, external_id: typiptUserExternalId, role: 'typist', }); userIds.push(user?.id ?? 0); } //作成したデータを確認 { const accounts = await getAccounts(source); expect(accounts.length).toBe(1); expect(accounts[0].id).toBe(accountId); const users = await getUsers(source); expect(users.length).toBe(4); } const service = module.get(AccountsService); const typistGroupName = 'typist-group-name'; const typistUserIds = userIds; const context = makeContext(adminExternalId, 'requestId'); await service.createTypistGroup( context, adminExternalId, typistGroupName, typistUserIds, ); //実行結果を確認 { const typistGroups = await getTypistGroup(source, accountId); expect(typistGroups.length).toBe(1); expect(typistGroups[0].name).toBe(typistGroupName); const typistGroupUsers = await getTypistGroupMember( source, typistGroups[0].id, ); expect(typistGroupUsers.length).toBe(3); expect(typistGroupUsers.map((user) => user.user_id)).toEqual(userIds); } }); it('typistIdsにRole:typist以外のユーザーが含まれていた場合、400エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const adminExternalId = 'admin-external-id'; // 第五階層のアカウント作成 const { id: accountId } = ( await makeTestAccount( source, { tier: 5 }, { external_id: adminExternalId }, ) ).account; // 作成したアカウントにユーザーを3名追加する const typiptUserExternalIds = [ 'typist-user-external-id1', 'typist-user-external-id2', 'typist-user-external-id3', ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { const user = await makeTestUser(source, { account_id: accountId, external_id: typiptUserExternalId, role: typiptUserExternalId === 'typist-user-external-id3' ? 'none' : 'typist', //typist-user-external-id3のみRole:none, }); userIds.push(user?.id ?? 0); } //作成したデータを確認 { const accounts = await getAccounts(source); expect(accounts.length).toBe(1); expect(accounts[0].id).toBe(accountId); const users = await getUsers(source); expect(users.length).toBe(4); expect(users.filter((user) => user.role === 'typist').length).toBe(2); } const service = module.get(AccountsService); const typistGroupName = 'typist-group-name'; const typistUserIds = userIds; const context = makeContext(adminExternalId, 'requestId'); await expect( service.createTypistGroup( context, adminExternalId, typistGroupName, typistUserIds, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010204'), HttpStatus.BAD_REQUEST), ); }); it('typistIdsに存在しないユーザーが含まれていた場合、400エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const adminExternalId = 'admin-external-id'; // 第五階層のアカウント作成 const { id: accountId } = ( await makeTestAccount( source, { tier: 5 }, { external_id: adminExternalId }, ) ).account; // 作成したアカウントにユーザーを3名追加する const typiptUserExternalIds = [ 'typist-user-external-id1', 'typist-user-external-id2', 'typist-user-external-id3', ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { const user = await makeTestUser(source, { account_id: accountId, external_id: typiptUserExternalId, role: 'typist', }); userIds.push(user?.id ?? 0); } //作成したデータを確認 { const accounts = await getAccounts(source); expect(accounts.length).toBe(1); expect(accounts[0].id).toBe(accountId); const users = await getUsers(source); expect(users.length).toBe(4); } const service = module.get(AccountsService); const typistGroupName = 'typist-group-name'; const typistUserIds = [...userIds, 9999]; //存在しないユーザーIDを追加 const context = makeContext(adminExternalId, 'requestId'); await expect( service.createTypistGroup( context, adminExternalId, typistGroupName, typistUserIds, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010204'), HttpStatus.BAD_REQUEST), ); }); it('typistIdsにメール未認証のユーザーが含まれていた場合、400エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const adminExternalId = 'admin-external-id'; // 第五階層のアカウント作成 const { id: accountId } = ( await makeTestAccount( source, { tier: 5 }, { external_id: adminExternalId }, ) ).account; // 作成したアカウントにユーザーを3名追加する const typiptUserExternalIds = [ 'typist-user-external-id1', 'typist-user-external-id2', 'typist-user-external-id3', ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { const user = await makeTestUser(source, { account_id: accountId, external_id: typiptUserExternalId, role: 'typist', email_verified: false, // メール未認証のユーザーを追加 }); userIds.push(user?.id ?? 0); } //作成したデータを確認 { const accounts = await getAccounts(source); expect(accounts.length).toBe(1); expect(accounts[0].id).toBe(accountId); const users = await getUsers(source); expect(users.length).toBe(4); } const service = module.get(AccountsService); const typistGroupName = 'typist-group-name'; const typistUserIds = [...userIds]; const context = makeContext(adminExternalId, 'requestId'); await expect( service.createTypistGroup( context, adminExternalId, typistGroupName, typistUserIds, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010204'), HttpStatus.BAD_REQUEST), ); }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const adminExternalId = 'admin-external-id'; // 第五階層のアカウント作成 const { id: accountId } = ( await makeTestAccount( source, { tier: 5 }, { external_id: adminExternalId }, ) ).account; // 作成したアカウントにユーザーを3名追加する const typiptUserExternalIds = [ 'typist-user-external-id1', 'typist-user-external-id2', 'typist-user-external-id3', ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { const user = await makeTestUser(source, { account_id: accountId, external_id: typiptUserExternalId, role: 'typist', }); userIds.push(user?.id ?? 0); } //作成したデータを確認 { const accounts = await getAccounts(source); expect(accounts.length).toBe(1); expect(accounts[0].id).toBe(accountId); const users = await getUsers(source); expect(users.length).toBe(4); } const service = module.get(AccountsService); const typistGroupName = 'typist-group-name'; const typistUserIds = userIds; const context = makeContext(adminExternalId, 'requestId'); //DBアクセスに失敗するようにする const typistGroupService = module.get( UserGroupsRepositoryService, ); typistGroupService.createTypistGroup = jest .fn() .mockRejectedValue('DB failed'); await expect( service.createTypistGroup( context, adminExternalId, typistGroupName, typistUserIds, ), ).rejects.toEqual( new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, ), ); }); }); describe('getTypistGroup', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('指定したIDのTypistGroupを取得できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを3名追加する const typiptUserExternalIds = [ 'typist-user-external-id1', 'typist-user-external-id2', 'typist-user-external-id3', ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, }); userIds.push(user?.id ?? 0); } // アカウントにタイピストグループを作成する const typistGroupName = 'typist-group-name'; const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); await service.createTypistGroup( context, admin.external_id, typistGroupName, userIds, ); //作成したデータを確認 const group = await getTypistGroup(source, account.id); { expect(group.length).toBe(1); expect(group[0].name).toBe(typistGroupName); const groupUsers = await getTypistGroupMember(source, group[0].id); expect(groupUsers.length).toBe(3); expect(groupUsers.map((user) => user.user_id)).toEqual(userIds); } const typistGroup = await service.getTypistGroup( context, admin.external_id, group[0].id, ); //実行結果を確認 { const typistGroups = await getTypistGroup(source, account.id); expect(typistGroups.length).toBe(1); expect(typistGroup.typistGroupName).toBe(typistGroupName); expect(typistGroup.typistIds.length).toBe(3); expect(typistGroup.typistIds).toEqual(userIds); } }); it('指定したタイピストグループIDのタイピストグループが存在しない場合、400エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを3名追加する const typiptUserExternalIds = [ 'typist-user-external-id1', 'typist-user-external-id2', 'typist-user-external-id3', ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, }); userIds.push(user?.id ?? 0); } // アカウントにタイピストグループを作成する const typistGroupName = 'typist-group-name'; const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); await service.createTypistGroup( context, admin.external_id, typistGroupName, userIds, ); //作成したデータを確認 { const group = await getTypistGroup(source, account.id); expect(group.length).toBe(1); expect(group[0].name).toBe(typistGroupName); const groupUsers = await getTypistGroupMember(source, group[0].id); expect(groupUsers.length).toBe(3); expect(groupUsers.map((user) => user.user_id)).toEqual(userIds); } try { await service.getTypistGroup(context, admin.external_id, 999); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E010908')); } else { fail(); } } }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを3名追加する const typiptUserExternalIds = [ 'typist-user-external-id1', 'typist-user-external-id2', 'typist-user-external-id3', ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, }); userIds.push(user?.id ?? 0); } // アカウントにタイピストグループを作成する const typistGroupName = 'typist-group-name'; const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); await service.createTypistGroup( context, admin.external_id, typistGroupName, userIds, ); //作成したデータを確認 const group = await getTypistGroup(source, account.id); { expect(group.length).toBe(1); expect(group[0].name).toBe(typistGroupName); const groupUsers = await getTypistGroupMember(source, group[0].id); expect(groupUsers.length).toBe(3); expect(groupUsers.map((user) => user.user_id)).toEqual(userIds); } //DBアクセスに失敗するようにする const typistGroupService = module.get( UserGroupsRepositoryService, ); typistGroupService.getTypistGroup = jest .fn() .mockRejectedValue('DB failed'); try { await service.getTypistGroup(context, admin.external_id, group[0].id); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('updateTypistGroup', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('TypistGroupを更新できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを3名追加する const typiptUserExternalIds = [ 'typist-user-external-id1', 'typist-user-external-id2', 'typist-user-external-id3', ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, }); userIds.push(user?.id ?? 0); } const service = module.get(AccountsService); const typistUserIds = [userIds[1]]; const context = makeContext(admin.external_id, 'requestId'); const typistGroupName = 'typist-group-name'; await service.createTypistGroup( context, admin.external_id, typistGroupName, [userIds[0]], ); //作成したデータを確認 const group = await getTypistGroup(source, account.id); { const group = await getTypistGroup(source, account.id); expect(group.length).toBe(1); expect(group[0].name).toBe(typistGroupName); const groupUsers = await getTypistGroupMember(source, group[0].id); expect(groupUsers.length).toBe(1); expect(groupUsers[0].user_group_id).toEqual(group[0].id); expect(groupUsers[0].user_id).toEqual(userIds[0]); } const updateTypistGroupName = 'typist-group-name-update'; await service.updateTypistGroup( context, admin.external_id, group[0].id, updateTypistGroupName, typistUserIds, ); //実行結果を確認 { const typistGroups = await getTypistGroup(source, account.id); expect(typistGroups.length).toBe(1); expect(typistGroups[0].name).toBe(updateTypistGroupName); const typistGroupUsers = await getTypistGroupMember( source, typistGroups[0].id, ); expect(typistGroupUsers.length).toBe(1); expect(typistGroupUsers[0].user_id).toEqual(userIds[1]); } }); it('typistIdsにRole:typist以外のユーザーが含まれていた場合、400エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを3名追加する const typiptUserExternalIds = [ 'typist-user-external-id1', 'typist-user-external-id2', 'typist-user-external-id3', ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: typiptUserExternalId === 'typist-user-external-id3' ? USER_ROLES.NONE : USER_ROLES.TYPIST, //typist-user-external-id3のみRole:none }); userIds.push(user?.id ?? 0); } const typistGroupName = 'typist-group-name'; const service = module.get(AccountsService); const typistUserIds = [userIds[2]]; const context = makeContext(admin.external_id, 'requestId'); await service.createTypistGroup( context, admin.external_id, typistGroupName, [userIds[0]], ); //作成したデータを確認 const group = await getTypistGroup(source, account.id); { expect(group.length).toBe(1); expect(group[0].name).toBe(typistGroupName); const groupUsers = await getTypistGroupMember(source, group[0].id); expect(groupUsers.length).toBe(1); expect(groupUsers[0].user_group_id).toEqual(group[0].id); expect(groupUsers[0].user_id).toEqual(userIds[0]); } const updateTypistGroupName = 'typist-group-name-update'; try { await service.updateTypistGroup( context, admin.external_id, group[0].id, updateTypistGroupName, typistUserIds, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E010204')); } else { fail(); } } }); it('typistIdsに存在しないユーザーが含まれていた場合、400エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを3名追加する const typiptUserExternalIds = [ 'typist-user-external-id1', 'typist-user-external-id2', 'typist-user-external-id3', ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, }); userIds.push(user?.id ?? 0); } const typistGroupName = 'typist-group-name'; const service = module.get(AccountsService); const typistUserIds = [999]; const context = makeContext(admin.external_id, 'requestId'); await service.createTypistGroup( context, admin.external_id, typistGroupName, [userIds[0]], ); //作成したデータを確認 const group = await getTypistGroup(source, account.id); { expect(group.length).toBe(1); expect(group[0].name).toBe(typistGroupName); const groupUsers = await getTypistGroupMember(source, group[0].id); expect(groupUsers.length).toBe(1); expect(groupUsers[0].user_group_id).toEqual(group[0].id); expect(groupUsers[0].user_id).toEqual(userIds[0]); } const updateTypistGroupName = 'typist-group-name-update'; try { await service.updateTypistGroup( context, admin.external_id, group[0].id, updateTypistGroupName, typistUserIds, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E010204')); } else { fail(); } } }); it('typistIdsにメール未認証のユーザーが含まれていた場合、400エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを3名追加する const typiptUserExternalIds = [ 'typist-user-external-id1', 'typist-user-external-id2', 'typist-user-external-id3', ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, email_verified: typiptUserExternalId !== 'typist-user-external-id3', //typist-user-external-id3のみメール未認証 }); userIds.push(user?.id ?? 0); } const typistGroupName = 'typist-group-name'; const service = module.get(AccountsService); const typistUserIds = [...userIds]; const context = makeContext(admin.external_id, 'requestId'); await service.createTypistGroup( context, admin.external_id, typistGroupName, [userIds[0]], ); //作成したデータを確認 const group = await getTypistGroup(source, account.id); { expect(group.length).toBe(1); expect(group[0].name).toBe(typistGroupName); const groupUsers = await getTypistGroupMember(source, group[0].id); expect(groupUsers.length).toBe(1); expect(groupUsers[0].user_group_id).toEqual(group[0].id); expect(groupUsers[0].user_id).toEqual(userIds[0]); } const updateTypistGroupName = 'typist-group-name-update'; try { await service.updateTypistGroup( context, admin.external_id, group[0].id, updateTypistGroupName, typistUserIds, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E010204')); } else { fail(); } } }); it('タイピストグループが存在しない場合、400エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを3名追加する const typiptUserExternalIds = [ 'typist-user-external-id1', 'typist-user-external-id2', 'typist-user-external-id3', ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, }); userIds.push(user?.id ?? 0); } const typistGroupName = 'typist-group-name'; const service = module.get(AccountsService); const typistUserIds = [userIds[1]]; const context = makeContext(admin.external_id, 'requestId'); await service.createTypistGroup( context, admin.external_id, typistGroupName, [userIds[0]], ); //作成したデータを確認 const group = await getTypistGroup(source, account.id); { expect(group.length).toBe(1); expect(group[0].name).toBe(typistGroupName); const groupUsers = await getTypistGroupMember(source, group[0].id); expect(groupUsers.length).toBe(1); expect(groupUsers[0].user_group_id).toEqual(group[0].id); expect(groupUsers[0].user_id).toEqual(userIds[0]); } const updateTypistGroupName = 'typist-group-name-update'; try { await service.updateTypistGroup( context, admin.external_id, 999, updateTypistGroupName, typistUserIds, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E010908')); } else { fail(); } } }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを3名追加する const typiptUserExternalIds = [ 'typist-user-external-id1', 'typist-user-external-id2', 'typist-user-external-id3', ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, }); userIds.push(user?.id ?? 0); } const typistGroupName = 'typist-group-name'; const service = module.get(AccountsService); const typistUserIds = [userIds[1]]; const context = makeContext(admin.external_id, 'requestId'); await service.createTypistGroup( context, admin.external_id, typistGroupName, [userIds[0]], ); //作成したデータを確認 const group = await getTypistGroup(source, account.id); { const group = await getTypistGroup(source, account.id); expect(group.length).toBe(1); expect(group[0].name).toBe(typistGroupName); const groupUsers = await getTypistGroupMember(source, group[0].id); expect(groupUsers.length).toBe(1); expect(groupUsers[0].user_group_id).toEqual(group[0].id); expect(groupUsers[0].user_id).toEqual(userIds[0]); } const updateTypistGroupName = 'typist-group-name-update'; //DBアクセスに失敗するようにする const typistGroupService = module.get( UserGroupsRepositoryService, ); typistGroupService.updateTypistGroup = jest .fn() .mockRejectedValue('DB failed'); try { await service.updateTypistGroup( context, admin.external_id, group[0].id, updateTypistGroupName, typistUserIds, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('getWorktypes', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウント内のWorktypeを取得できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); await createWorktype(source, account.id, 'worktype1', 'description1', true); await createWorktype(source, account.id, 'worktype2'); //作成したデータを確認 const worktypes = await getWorktypes(source, account.id); const accounts = await getAccounts(source); { expect(worktypes.length).toBe(2); expect(worktypes[0].custom_worktype_id).toBe('worktype1'); expect(worktypes[1].custom_worktype_id).toBe('worktype2'); expect(worktypes[0].description).toBe('description1'); expect(worktypes[1].description).toBeNull(); expect(accounts.length).toBe(1); expect(accounts[0].active_worktype_id).toBe(worktypes[0].id); } const resWorktypes = await service.getWorktypes(context, admin.external_id); //実行結果を確認 { expect(resWorktypes.worktypes.length).toBe(2); expect(resWorktypes.worktypes[0].worktypeId).toBe('worktype1'); expect(resWorktypes.worktypes[1].worktypeId).toBe('worktype2'); expect(resWorktypes.worktypes[0].description).toBe('description1'); expect(resWorktypes.worktypes[1].description).toBe(undefined); expect(resWorktypes.active).toBe(worktypes[0].id); } }); it('アカウント内のWorktypeを取得できる(0件)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const resWorktypes = await service.getWorktypes(context, admin.external_id); //実行結果を確認 { expect(resWorktypes.worktypes.length).toBe(0); } }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); await createWorktype(source, account.id, 'worktype1', 'description1'); await createWorktype(source, account.id, 'worktype2'); //作成したデータを確認 const worktypes = await getWorktypes(source, account.id); { expect(worktypes.length).toBe(2); expect(worktypes[0].custom_worktype_id).toBe('worktype1'); expect(worktypes[1].custom_worktype_id).toBe('worktype2'); expect(worktypes[0].description).toBe('description1'); expect(worktypes[1].description).toBeNull(); } //DBアクセスに失敗するようにする const worktypeService = module.get( WorktypesRepositoryService, ); worktypeService.getWorktypes = jest.fn().mockRejectedValue('DB failed'); try { await service.getWorktypes(context, admin.external_id); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('createWorktype', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('Worktypeを作成できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); // Worktypeが未登録であることを確認 { const worktypes = await getWorktypes(source, account.id); const optionItems = await getOptionItems(source); expect(worktypes.length).toBe(0); expect(optionItems.length).toBe(0); } await service.createWorktype( context, admin.external_id, 'worktype1', 'description1', ); //実行結果を確認 { const worktypes = await getWorktypes(source, account.id); const optionItems = await getOptionItems(source, worktypes[0].id); expect(worktypes.length).toBe(1); expect(worktypes[0].custom_worktype_id).toBe('worktype1'); expect(worktypes[0].description).toBe('description1'); expect(optionItems.length).toBe(10); expect(optionItems[0].item_label).toBe(''); expect(optionItems[0].default_value_type).toBe( OPTION_ITEM_VALUE_TYPE.DEFAULT, ); expect(optionItems[0].initial_value).toBe(''); } }); it('WorktypeIDが登録済みのWorktypeIDと重複した場合、400エラーとなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const worktypeId = 'worktype1'; await createWorktype(source, account.id, worktypeId); //作成したデータを確認 { const worktypes = await getWorktypes(source, account.id); expect(worktypes.length).toBe(1); expect(worktypes[0].custom_worktype_id).toBe(worktypeId); } try { await service.createWorktype(context, admin.external_id, worktypeId); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E011001')); } else { fail(); } } }); it('WorktypeIDがすでに最大登録数(20件)まで登録されている場合、400エラーとなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); // あらかじめ最大登録数分のWorktypeを登録する for (let i = 0; i < WORKTYPE_MAX_COUNT; i++) { await createWorktype(source, account.id, `worktype${i + 1}`); } //作成したデータを確認 { const worktypes = await getWorktypes(source, account.id); expect(worktypes.length).toBe(WORKTYPE_MAX_COUNT); } try { await service.createWorktype(context, admin.external_id, 'newWorktypeID'); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E011002')); } else { fail(); } } }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); //DBアクセスに失敗するようにする const worktypeService = module.get( WorktypesRepositoryService, ); worktypeService.createWorktype = jest.fn().mockRejectedValue('DB failed'); try { await service.createWorktype(context, admin.external_id, 'worktype1'); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('updateWorktype', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('Worktypeを更新できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const worktype = new Worktype(); worktype.custom_worktype_id = 'worktypeID1'; worktype.description = 'description1'; await createWorktype( source, account.id, worktype.custom_worktype_id, worktype.description, ); // Worktypeを確認 const worktypes = await getWorktypes(source, account.id); { expect(worktypes.length).toBe(1); expect(worktypes[0].custom_worktype_id).toBe(worktype.custom_worktype_id); expect(worktypes[0].description).toBe(worktype.description); } const updateWorktypeId = 'updateWorktypeID'; const updateDescription = 'updateDescription'; await service.updateWorktype( context, admin.external_id, worktypes[0].id, updateWorktypeId, updateDescription, ); //実行結果を確認 { const worktypes = await getWorktypes(source, account.id); expect(worktypes.length).toBe(1); expect(worktypes[0].custom_worktype_id).toBe(updateWorktypeId); expect(worktypes[0].description).toBe(updateDescription); } }); it('指定したIDが登録されていない場合、400エラーとなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const worktype = new Worktype(); worktype.custom_worktype_id = 'worktypeID1'; worktype.description = 'description1'; await createWorktype( source, account.id, worktype.custom_worktype_id, worktype.description, ); // Worktypeを確認 { const worktypes = await getWorktypes(source, account.id); expect(worktypes.length).toBe(1); expect(worktypes[0].custom_worktype_id).toBe(worktype.custom_worktype_id); expect(worktypes[0].description).toBe(worktype.description); } try { await service.updateWorktype( context, admin.external_id, 999, 'newWorktypeID', 'newDescription', ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E011003')); } else { fail(); } } }); it('WorktypeIDが登録済みのWorktypeIDと重複した場合、400エラーとなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const worktype1 = new Worktype(); worktype1.custom_worktype_id = 'worktypeID1'; worktype1.description = 'description1'; const worktype2 = new Worktype(); worktype2.custom_worktype_id = 'worktypeID2'; worktype2.description = 'description2'; await createWorktype( source, account.id, worktype1.custom_worktype_id, worktype1.description, ); await createWorktype( source, account.id, worktype2.custom_worktype_id, worktype2.description, ); //作成したデータを確認 const worktypes = await getWorktypes(source, account.id); { expect(worktypes.length).toBe(2); expect(worktypes[0].custom_worktype_id).toBe( worktype1.custom_worktype_id, ); expect(worktypes[0].description).toBe(worktype1.description); expect(worktypes[1].custom_worktype_id).toBe( worktype2.custom_worktype_id, ); expect(worktypes[1].description).toBe(worktype2.description); } try { await service.updateWorktype( context, admin.external_id, worktypes[0].id, worktype2.custom_worktype_id, worktype2.description, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E011001')); } else { fail(); } } }); it('WorktypeIDが登録済みの指定IDのWorktypeIDと重複した場合でも更新できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const worktype = new Worktype(); worktype.custom_worktype_id = 'worktypeID1'; worktype.description = 'description1'; await createWorktype( source, account.id, worktype.custom_worktype_id, worktype.description, ); // Worktypeを確認 const worktypes = await getWorktypes(source, account.id); { expect(worktypes.length).toBe(1); expect(worktypes[0].custom_worktype_id).toBe(worktype.custom_worktype_id); expect(worktypes[0].description).toBe(worktype.description); } const updateDescription = 'updateDescription'; await service.updateWorktype( context, admin.external_id, worktypes[0].id, worktype.custom_worktype_id, updateDescription, ); //実行結果を確認 { const worktypes = await getWorktypes(source, account.id); expect(worktypes.length).toBe(1); expect(worktypes[0].custom_worktype_id).toBe(worktype.custom_worktype_id); expect(worktypes[0].description).toBe(updateDescription); } }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const worktype = new Worktype(); worktype.custom_worktype_id = 'worktypeID1'; worktype.description = 'description1'; await createWorktype( source, account.id, worktype.custom_worktype_id, worktype.description, ); // Worktypeを確認 const worktypes = await getWorktypes(source, account.id); { expect(worktypes.length).toBe(1); expect(worktypes[0].custom_worktype_id).toBe(worktype.custom_worktype_id); expect(worktypes[0].description).toBe(worktype.description); } //DBアクセスに失敗するようにする const worktypeService = module.get( WorktypesRepositoryService, ); worktypeService.updateWorktype = jest.fn().mockRejectedValue('DB failed'); try { await service.updateWorktype( context, admin.external_id, worktypes[0].id, 'newWorktype', 'newDescription', ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('deleteWorktype', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('WorktypeIDを削除できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const { id: worktypeId1 } = await createWorktype( source, account.id, 'worktype1', ); const { id: worktypeId2 } = await createWorktype( source, account.id, 'worktype2', ); await createOptionItems(source, worktypeId1); await createOptionItems(source, worktypeId2); // 作成したデータを確認 { const worktypes = await getWorktypes(source, account.id); const optionItems = await getOptionItems(source); expect(worktypes.length).toBe(2); expect(worktypes[0].id).toBe(worktypeId1); expect(worktypes[0].custom_worktype_id).toBe('worktype1'); expect(worktypes[1].id).toBe(worktypeId2); expect(worktypes[1].custom_worktype_id).toBe('worktype2'); expect(optionItems.length).toBe(20); } await service.deleteWorktype(context, admin.external_id, worktypeId1); //実行結果を確認 { const worktypes = await getWorktypes(source, account.id); const optionItems = await getOptionItems(source); expect(worktypes.length).toBe(1); expect(worktypes[0].id).toBe(worktypeId2); expect(worktypes[0].custom_worktype_id).toBe('worktype2'); expect(optionItems.length).toBe(10); } }); it('指定されたWorktypeIDがアカウントのActiveWorktypeIDの場合、削除できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const { id: worktypeId1 } = await createWorktype( source, account.id, 'worktype1', 'description1', true, ); await createOptionItems(source, worktypeId1); // 作成したデータを確認 { const worktypes = await getWorktypes(source, account.id); const optionItems = await getOptionItems(source); const accounts = await getAccounts(source); expect(worktypes.length).toBe(1); expect(worktypes[0].id).toBe(worktypeId1); expect(worktypes[0].custom_worktype_id).toBe('worktype1'); expect(optionItems.length).toBe(10); expect(accounts.length).toBe(1); expect(accounts[0].active_worktype_id).toBe(worktypeId1); } await service.deleteWorktype(context, admin.external_id, worktypeId1); //実行結果を確認 { const worktypes = await getWorktypes(source, account.id); const optionItems = await getOptionItems(source); const accounts = await getAccounts(source); expect(worktypes.length).toBe(0); expect(optionItems.length).toBe(0); expect(accounts.length).toBe(1); expect(accounts[0].active_worktype_id).toBe(null); } }); it('指定したWorktypeIDが登録されていない場合、400エラーとなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const { id: worktypeId1 } = await createWorktype( source, account.id, 'worktype1', ); await createOptionItems(source, worktypeId1); // 作成したデータを確認 { const worktypes = await getWorktypes(source, account.id); const optionItems = await getOptionItems(source); expect(worktypes.length).toBe(1); expect(worktypes[0].id).toBe(worktypeId1); expect(worktypes[0].custom_worktype_id).toBe('worktype1'); expect(optionItems.length).toBe(10); } try { await service.deleteWorktype(context, admin.external_id, 9999); fail(); // 例外が発生しない場合はテスト失敗 } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E011003')); } else { fail(); } } }); it('指定したIDがWorkflowで使用されている場合、400エラーとなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const author = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.AUTHOR, }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const { id: worktypeId1 } = await createWorktype( source, account.id, 'worktype1', ); await createOptionItems(source, worktypeId1); await createWorkflow(source, account.id, author?.id ?? 0, worktypeId1); // 作成したデータを確認 { const worktypes = await getWorktypes(source, account.id); const optionItems = await getOptionItems(source); const workflows = await getWorkflows(source, account.id); expect(worktypes.length).toBe(1); expect(worktypes[0].id).toBe(worktypeId1); expect(worktypes[0].custom_worktype_id).toBe('worktype1'); expect(optionItems.length).toBe(10); expect(workflows.length).toBe(1); expect(workflows[0].worktype_id).toBe(worktypeId1); } try { await service.deleteWorktype(context, admin.external_id, worktypeId1); fail(); // 例外が発生しない場合はテスト失敗 } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E011004')); } else { fail(); } } }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const { id: worktypeId1 } = await createWorktype( source, account.id, 'worktype1', ); await createOptionItems(source, worktypeId1); // 作成したデータを確認 { const worktypes = await getWorktypes(source, account.id); const optionItems = await getOptionItems(source); expect(worktypes.length).toBe(1); expect(worktypes[0].id).toBe(worktypeId1); expect(worktypes[0].custom_worktype_id).toBe('worktype1'); expect(optionItems.length).toBe(10); } //DBアクセスに失敗するようにする const worktypeService = module.get( WorktypesRepositoryService, ); worktypeService.deleteWorktype = jest.fn().mockRejectedValue('DB failed'); try { await service.deleteWorktype(context, admin.external_id, worktypeId1); fail(); // 例外が発生しない場合はテスト失敗 } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('getOptionItems', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('指定WorktypeIDに紐づいたOptionItemを取得できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const worktype = await createWorktype(source, account.id, 'worktype1'); const optionItems = await createOptionItems(source, worktype.id); //作成したデータを確認 { expect(optionItems.length).toBe(10); expect(optionItems[0].item_label).toBe(''); expect(optionItems[0].default_value_type).toBe( OPTION_ITEM_VALUE_TYPE.DEFAULT, ); expect(optionItems[0].initial_value).toBe(''); } const resOptionItems = await service.getOptionItems( context, admin.external_id, worktype.id, ); //実行結果を確認 { expect(resOptionItems.optionItems.length).toBe(10); expect(resOptionItems.optionItems.map((x) => x.id)).toEqual( optionItems.map((x) => x.id), ); expect(resOptionItems.optionItems[0].itemLabel).toBe(''); expect(resOptionItems.optionItems[0].defaultValueType).toBe( OPTION_ITEM_VALUE_TYPE.DEFAULT, ); expect(resOptionItems.optionItems[0].initialValue).toBe(''); } }); it('WorktypeIDが存在しない場合、400エラーとなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const worktype = await createWorktype(source, account.id, 'worktype1'); const optionItems = await createOptionItems(source, worktype.id); //作成したデータを確認 { expect(optionItems.length).toBe(10); expect(optionItems[0].item_label).toBe(''); expect(optionItems[0].default_value_type).toBe( OPTION_ITEM_VALUE_TYPE.DEFAULT, ); expect(optionItems[0].initial_value).toBe(''); } try { await service.getOptionItems(context, admin.external_id, 999); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E011003')); } else { fail(); } } }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const worktype = await createWorktype(source, account.id, 'worktype1'); const optionItems = await createOptionItems(source, worktype.id); //作成したデータを確認 { expect(optionItems.length).toBe(10); expect(optionItems[0].item_label).toBe(''); expect(optionItems[0].default_value_type).toBe( OPTION_ITEM_VALUE_TYPE.DEFAULT, ); expect(optionItems[0].initial_value).toBe(''); } //DBアクセスに失敗するようにする const worktypesService = module.get( WorktypesRepositoryService, ); worktypesService.getOptionItems = jest.fn().mockRejectedValue('DB failed'); try { await service.getOptionItems(context, admin.external_id, worktype.id); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('updateOptionItems', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('指定WorktypeIDに紐づいたOptionItemを更新できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const worktype = await createWorktype(source, account.id, 'worktype1'); const optionItems = await createOptionItems(source, worktype.id); //作成したデータを確認 { expect(optionItems.length).toBe(10); expect(optionItems[0].item_label).toBe(''); expect(optionItems[0].default_value_type).toBe( OPTION_ITEM_VALUE_TYPE.DEFAULT, ); expect(optionItems[0].initial_value).toBe(''); expect(optionItems[1].item_label).toBe(''); expect(optionItems[1].default_value_type).toBe( OPTION_ITEM_VALUE_TYPE.DEFAULT, ); expect(optionItems[1].initial_value).toBe(''); expect(optionItems[2].item_label).toBe(''); expect(optionItems[2].default_value_type).toBe( OPTION_ITEM_VALUE_TYPE.DEFAULT, ); expect(optionItems[2].initial_value).toBe(''); expect(optionItems[3].item_label).toBe(''); expect(optionItems[3].default_value_type).toBe( OPTION_ITEM_VALUE_TYPE.DEFAULT, ); expect(optionItems[3].initial_value).toBe(''); } await service.updateOptionItems(context, admin.external_id, worktype.id, [ { itemLabel: 'itemLabel1', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: 'initialValue1', }, { itemLabel: 'itemLabel2', defaultValueType: OPTION_ITEM_VALUE_TYPE.BLANK, initialValue: '', }, { itemLabel: 'itemLabel3', defaultValueType: OPTION_ITEM_VALUE_TYPE.LAST_INPUT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.BLANK, initialValue: 'initialValue4', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, ]); //実行結果を確認 { const resOptionItems = await getOptionItems(source, worktype.id); expect(resOptionItems.length).toBe(10); expect(resOptionItems[0].item_label).toBe('itemLabel1'); expect(resOptionItems[0].default_value_type).toBe( OPTION_ITEM_VALUE_TYPE.DEFAULT, ); expect(resOptionItems[0].initial_value).toBe('initialValue1'); expect(resOptionItems[1].item_label).toBe('itemLabel2'); expect(resOptionItems[1].default_value_type).toBe( OPTION_ITEM_VALUE_TYPE.BLANK, ); expect(resOptionItems[1].initial_value).toBe(''); expect(resOptionItems[2].item_label).toBe('itemLabel3'); expect(resOptionItems[2].default_value_type).toBe( OPTION_ITEM_VALUE_TYPE.LAST_INPUT, ); expect(resOptionItems[2].initial_value).toBe(''); expect(resOptionItems[3].item_label).toBe(''); expect(resOptionItems[3].default_value_type).toBe( OPTION_ITEM_VALUE_TYPE.BLANK, ); expect(resOptionItems[3].initial_value).toBe(''); } }); it('WorktypeIDが存在しない場合、400エラーとなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const worktype = await createWorktype(source, account.id, 'worktype1'); const optionItems = await createOptionItems(source, worktype.id); //作成したデータを確認 { expect(optionItems.length).toBe(10); expect(optionItems[0].item_label).toBe(''); expect(optionItems[0].default_value_type).toBe( OPTION_ITEM_VALUE_TYPE.DEFAULT, ); expect(optionItems[0].initial_value).toBe(''); } try { await service.updateOptionItems(context, admin.external_id, 999, [ { itemLabel: 'itemLabel1', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: 'initialValue1', }, { itemLabel: 'itemLabel2', defaultValueType: OPTION_ITEM_VALUE_TYPE.BLANK, initialValue: '', }, { itemLabel: 'itemLabel3', defaultValueType: OPTION_ITEM_VALUE_TYPE.LAST_INPUT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, ]); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E011003')); } else { fail(); } } }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const worktype = await createWorktype(source, account.id, 'worktype1'); const optionItems = await createOptionItems(source, worktype.id); //作成したデータを確認 { expect(optionItems.length).toBe(10); expect(optionItems[0].item_label).toBe(''); expect(optionItems[0].default_value_type).toBe( OPTION_ITEM_VALUE_TYPE.DEFAULT, ); expect(optionItems[0].initial_value).toBe(''); } //DBアクセスに失敗するようにする const worktypesService = module.get( WorktypesRepositoryService, ); worktypesService.updateOptionItems = jest .fn() .mockRejectedValue('DB failed'); try { await service.updateOptionItems(context, admin.external_id, worktype.id, [ { itemLabel: 'itemLabel1', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: 'initialValue1', }, { itemLabel: 'itemLabel2', defaultValueType: OPTION_ITEM_VALUE_TYPE.BLANK, initialValue: '', }, { itemLabel: 'itemLabel3', defaultValueType: OPTION_ITEM_VALUE_TYPE.LAST_INPUT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, { itemLabel: '', defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT, initialValue: '', }, ]); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('updateActiveWorktype', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウントのActiveWorktypeIDを指定WorktypeIDに更新できる(NULL⇒ID設定)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const worktype = await createWorktype(source, account.id, 'worktype1'); //作成したデータを確認 { const beforeAccount = await getAccount(source, account.id); expect(beforeAccount?.active_worktype_id).toBe(null); } await service.updateActiveWorktype(context, admin.external_id, worktype.id); //実行結果を確認 { const resultsAccount = await getAccount(source, account.id); expect(resultsAccount?.active_worktype_id).toBe(worktype.id); } }); it('アカウントのActiveWorktypeIDを指定WorktypeIDに更新できる(別のWorkTypeIDを設定)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const worktype1 = await createWorktype( source, account.id, 'worktype1', 'description1', true, ); const worktype2 = await createWorktype(source, account.id, 'worktype2'); //作成したデータを確認 { const beforeAccount = await getAccount(source, account.id); expect(beforeAccount?.active_worktype_id).toBe(worktype1.id); } await service.updateActiveWorktype( context, admin.external_id, worktype2.id, ); //実行結果を確認 { const resultsAccount = await getAccount(source, account.id); expect(resultsAccount?.active_worktype_id).toBe(worktype2.id); } }); it('アカウントのActiveWorktypeIDをNULLに更新できる(WorkTypeID⇒NULL)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const worktype1 = await createWorktype( source, account.id, 'worktype1', 'description1', true, ); //作成したデータを確認 { const beforeAccount = await getAccount(source, account.id); expect(beforeAccount?.active_worktype_id).toBe(worktype1.id); } await service.updateActiveWorktype(context, admin.external_id, undefined); //実行結果を確認 { const resultsAccount = await getAccount(source, account.id); expect(resultsAccount?.active_worktype_id).toBe(null); } }); it('自アカウント内に指定されたIDのWorktypeIDが存在しない場合、400エラーとなること(WorkTypeIDが存在しない場合)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); await createWorktype(source, account.id, 'worktype1'); //作成したデータを確認 { const beforeAccount = await getAccount(source, account.id); expect(beforeAccount?.active_worktype_id).toBe(null); } try { await service.updateActiveWorktype(context, admin.external_id, 999); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E011003')); } else { fail(); } } }); it('自アカウント内に指定されたIDのWorktypeIDが存在しない場合、400エラーとなること(WorkTypeIDが別アカウントの場合)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { account: otherAccount } = await makeTestAccount(source, { tier: 5, }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); await createWorktype(source, account.id, 'worktype1'); await createWorktype(source, otherAccount.id, 'worktype2'); //作成したデータを確認 { const beforeAccount = await getAccount(source, account.id); const worktype1 = await getWorktypes(source, account.id); const worktype2 = await getWorktypes(source, otherAccount.id); expect(beforeAccount?.active_worktype_id).toBe(null); expect(worktype1.length).toBe(1); expect(worktype1[0].custom_worktype_id).toBe('worktype1'); expect(worktype2.length).toBe(1); expect(worktype2[0].custom_worktype_id).toBe('worktype2'); } try { await service.updateActiveWorktype(context, admin.external_id, 999); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E011003')); } else { fail(); } } }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); await createWorktype(source, account.id, 'worktype1'); //作成したデータを確認 { const beforeAccount = await getAccount(source, account.id); expect(beforeAccount?.active_worktype_id).toBe(null); } //DBアクセスに失敗するようにする const accountsRepositoryService = module.get( AccountsRepositoryService, ); accountsRepositoryService.updateActiveWorktypeId = jest .fn() .mockRejectedValue('DB failed'); try { await service.updateActiveWorktype(context, admin.external_id, 999); } 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が行われるため注意 }); 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: tier1Accounts, tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(source); const tier5Accounts = await makeTestAccount(source, { parent_account_id: tier4Accounts[0].account.id, tier: 5, }); const poNumber = 'CANCEL_TEST'; const date = new Date(); date.setDate(date.getDate() - 10); await createOrder( source, poNumber, tier5Accounts.account.id, tier5Accounts.account?.parent_account_id ?? 0, date, 1, LICENSE_ISSUE_STATUS.ISSUED, ); date.setDate(date.getDate() + 10); // 発行時に論理削除されたライセンス情報 await createLicense( source, 1, date, tier5Accounts.account.id, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, date, 1, ); const service = module.get(AccountsService); overrideSendgridService(service, {}); await service.cancelIssue( makeContext('trackingId', 'requestId'), tier1Accounts[0].users[0].external_id, poNumber, tier5Accounts.account.id, ); // 発行待ちに戻した注文の状態確認 const orderRecord = await selectOrderLicense( source, tier5Accounts.account.id, poNumber, ); expect(orderRecord.orderLicense?.issued_at).toBe(null); expect(orderRecord.orderLicense?.status).toBe( LICENSE_ISSUE_STATUS.ISSUE_REQUESTING, ); // 未割当に戻したライセンスの状態確認 const licenseRecord = await selectLicense(source, 1); expect(licenseRecord.license?.status).toBe( LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); expect(licenseRecord.license?.delete_order_id).toBe(null); expect(licenseRecord.license?.deleted_at).toBe(null); }); it('ライセンス発行のキャンセルが完了する(第二階層で実行)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { tier2Accounts: tier2Accounts, tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(source); const tier5Accounts = await makeTestAccount(source, { parent_account_id: tier4Accounts[0].account.id, tier: 5, }); const poNumber = 'CANCEL_TEST'; const date = new Date(); date.setDate(date.getDate() - 10); await createOrder( source, poNumber, tier5Accounts.account.id, tier5Accounts.account?.parent_account_id ?? 0, date, 1, LICENSE_ISSUE_STATUS.ISSUED, ); date.setDate(date.getDate() + 10); // 発行時に論理削除されたライセンス情報 await createLicense( source, 1, date, tier5Accounts.account.id, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, null, date, 1, ); const service = module.get(AccountsService); overrideSendgridService(service, {}); await service.cancelIssue( makeContext('trackingId', 'requestId'), tier2Accounts[0].users[0].external_id, poNumber, tier5Accounts.account.id, ); // 発行待ちに戻した注文の状態確認 const orderRecord = await selectOrderLicense( source, tier5Accounts.account.id, poNumber, ); expect(orderRecord.orderLicense?.issued_at).toBe(null); expect(orderRecord.orderLicense?.status).toBe( LICENSE_ISSUE_STATUS.ISSUE_REQUESTING, ); // 未割当に戻したライセンスの状態確認 const licenseRecord = await selectLicense(source, 1); expect(licenseRecord.license?.status).toBe( LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); expect(licenseRecord.license?.delete_order_id).toBe(null); expect(licenseRecord.license?.deleted_at).toBe(null); }); it('キャンセル対象の発行が存在しない場合エラー', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { tier1Accounts: tier1Accounts, tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(source); const tier5Accounts = await makeTestAccount(source, { parent_account_id: tier4Accounts[0].account.id, tier: 5, }); const poNumber = 'CANCEL_TEST'; const service = module.get(AccountsService); overrideSendgridService(service, {}); await expect( service.cancelIssue( makeContext('trackingId', 'requestId'), tier1Accounts[0].users[0].external_id, poNumber, tier5Accounts.account.id, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010809'), HttpStatus.BAD_REQUEST), ); }); it('キャンセル対象の発行が14日より経過していた場合エラー', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { tier1Accounts: tier1Accounts, tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(source); const tier5Accounts = await makeTestAccount(source, { parent_account_id: tier4Accounts[0].account.id, tier: 5, }); const poNumber = 'CANCEL_TEST'; const date = new Date(); date.setDate(date.getDate() - 15); await createOrder( source, poNumber, tier5Accounts.account.id, tier5Accounts.account?.parent_account_id ?? 0, date, 1, LICENSE_ISSUE_STATUS.ISSUED, ); await createLicense( source, 1, date, tier5Accounts.account.id, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, 1, null, null, ); const service = module.get(AccountsService); overrideSendgridService(service, {}); await expect( service.cancelIssue( makeContext('trackingId', 'requestId'), tier1Accounts[0].users[0].external_id, poNumber, tier5Accounts.account.id, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010810'), HttpStatus.BAD_REQUEST), ); }); it('キャンセル対象の発行のライセンスが使われていた場合エラー', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { tier1Accounts: tier1Accounts, tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(source); const tier5Accounts = await makeTestAccount(source, { parent_account_id: tier4Accounts[0].account.id, tier: 5, }); const poNumber = 'CANCEL_TEST'; const date = new Date(); date.setDate(date.getDate() - 14); await createOrder( source, poNumber, tier5Accounts.account.id, tier5Accounts.account?.parent_account_id ?? 0, date, 1, LICENSE_ISSUE_STATUS.ISSUED, ); await createLicense( source, 1, date, tier5Accounts.account.id, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.ALLOCATED, null, 1, null, null, ); const service = module.get(AccountsService); overrideSendgridService(service, {}); await expect( service.cancelIssue( makeContext('trackingId', 'requestId'), tier1Accounts[0].users[0].external_id, poNumber, tier5Accounts.account.id, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010811'), HttpStatus.BAD_REQUEST), ); }); it('自身のパートナー以外の発行をキャンセルしようとした場合、エラー', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { tier1Accounts: tier1Accounts } = await makeHierarchicalAccounts( source, ); const tier5Accounts = await makeTestAccount(source, { parent_account_id: 100, tier: 5, }); const poNumber = 'CANCEL_TEST'; const date = new Date(); date.setDate(date.getDate() - 14); await createOrder( source, poNumber, tier5Accounts.account.id, tier5Accounts.account?.parent_account_id ?? 0, date, 1, LICENSE_ISSUE_STATUS.ISSUED, ); await createLicense( source, 1, date, tier5Accounts.account.id, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, null, 1, null, null, ); const service = module.get(AccountsService); overrideSendgridService(service, {}); await expect( service.cancelIssue( makeContext('trackingId', 'requestId'), tier1Accounts[0].users[0].external_id, poNumber, tier5Accounts.account.id, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E000108'), HttpStatus.UNAUTHORIZED), ); }); }); 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が行われるため注意 }); 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 service = module.get(AccountsService); const { tier1Accounts, tier2Accounts, tier3Accounts, tier4Accounts } = await makeHierarchicalAccounts(source); const tier1Difference = await makeTestAccount(source, { tier: 1, }); const tier2_3 = await makeTestAccount( source, { parent_account_id: tier1Accounts[0].account.id, tier: 2, }, {}, true, false, ); const tier2_4 = await makeTestAccount( source, { parent_account_id: tier1Accounts[0].account.id, tier: 2, }, {}, false, true, ); await makeTestAccount(source, { parent_account_id: tier1Difference.account.id, tier: 2, }); const adb2cReturn = [ { id: tier2Accounts[0].users[0].external_id, displayName: 'partner1', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner1@example.com', }, ], }, { id: tier2Accounts[1].users[0].external_id, displayName: 'partner2', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner2@example.com', }, ], }, { id: tier2_3.admin.external_id, displayName: 'partner3', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner3@example.com', }, ], }, { id: tier2_4.admin.external_id, displayName: 'partner3', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner3@example.com', }, ], }, ] as AdB2cUser[]; overrideAdB2cService(service, { getUsers: async (_context: Context, _externalIds: string[]) => { return adb2cReturn; }, }); const partners = await service.getPartners( makeContext('trackingId', 'requestId'), tier1Accounts[0].users[0].external_id, 15, 0, ); // 違うアカウントのパートナーは取得していないこと expect(partners.total).toBe(4); // 会社名の昇順に取得できていること expect(partners.partners[0].name).toBe( tier2Accounts[1].account.company_name, ); expect(partners.partners[1].name).toBe( tier2Accounts[0].account.company_name, ); expect(partners.partners[2].name).toBe(tier2_3.account.company_name); expect(partners.partners[3].name).toBe(tier2_4.account.company_name); expect(partners.partners[0].email).toBe('partner2@example.com'); expect(partners.partners[1].email).toBe('partner1@example.com'); expect(partners.partners[2].email).toBeUndefined; expect(partners.partners[3].email).toBeUndefined; expect(partners.partners[0].tier).toBe(tier2Accounts[1].account.tier); expect(partners.partners[0].country).toBe(tier2Accounts[1].account.country); expect(partners.partners[0].accountId).toBe(tier2Accounts[1].account.id); expect(partners.partners[0].tier).toBe(tier2Accounts[1].account.tier); expect(partners.partners[0].primaryAdmin).toBe('partner2'); expect(partners.partners[0].dealerManagement).toBe( tier2Accounts[1].account.delegation_permission, ); }); it('パートナー一覧を取得する(パートナーが0件の場合)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); const account = await makeTestAccount(source, { tier: 1, }); const adb2cReturn = [{}] as AdB2cUser[]; overrideAdB2cService(service, { getUsers: async (_context: Context, _externalIds: string[]) => { return adb2cReturn; }, }); const partners = await service.getPartners( makeContext('trackingId', 'requestId'), account.admin.external_id, 15, 0, ); // 結果が0件で成功となること expect(partners.total).toBe(0); }); }); 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が行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウント情報を更新する(第五階層が実行/セカンダリ管理者ユーザがnull)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideSendgridService(service, { sendMail: async () => { return; }, }); const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts( source, ); const tier5Accounts = await makeTestAccount(source, { parent_account_id: tier4Accounts[0].account.id, tier: 5, }); await service.updateAccountInfo( makeContext('trackingId', 'requestId'), tier5Accounts.admin.external_id, tier5Accounts.account.tier, true, tier5Accounts.admin.id, tier4Accounts[0].account.id, undefined, ); // DB内が想定通りになっているか確認 const account = await getAccount(source, tier5Accounts.account.id); expect(account?.parent_account_id).toBe(tier4Accounts[0].account.id); expect(account?.delegation_permission).toBe(true); expect(account?.primary_admin_user_id).toBe(tier5Accounts.admin.id); expect(account?.secondary_admin_user_id).toBe(null); }); it('アカウント情報を更新する(第五階層以外が実行)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideSendgridService(service, { sendMail: async () => { return; }, }); const { tier3Accounts: tier3Accounts, tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(source); const adduser = await makeTestUser(source, { account_id: tier4Accounts[0].account.id, external_id: 'typist-user-external-id', role: 'typist', }); await service.updateAccountInfo( makeContext('trackingId', 'requestId'), tier4Accounts[0].users[0].external_id, tier4Accounts[0].account.tier, false, tier4Accounts[0].users[0].id, tier3Accounts[0].account.id, adduser?.id, ); // DB内が想定通りになっているか確認 const account = await getAccount(source, tier4Accounts[0].account.id); expect(account?.parent_account_id).toBe(tier3Accounts[0].account.id); expect(account?.delegation_permission).toBe(false); expect(account?.primary_admin_user_id).toBe(tier4Accounts[0].users[0].id); expect(account?.secondary_admin_user_id).toBe(adduser?.id); }); it('アカウント情報を更新する(ディーラーアカウントが未入力)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideSendgridService(service, { sendMail: async () => { return; }, }); const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts( source, ); const adduser = await makeTestUser(source, { account_id: tier4Accounts[0].account.id, external_id: 'typist-user-external-id', role: 'typist', }); await service.updateAccountInfo( makeContext('trackingId', 'requestId'), tier4Accounts[0].users[0].external_id, tier4Accounts[0].account.tier, false, tier4Accounts[0].users[0].id, undefined, adduser?.id, ); // DB内が想定通りになっているか確認 const account = await getAccount(source, tier4Accounts[0].account.id); expect(account?.parent_account_id).toBe(null); expect(account?.delegation_permission).toBe(false); expect(account?.primary_admin_user_id).toBe(tier4Accounts[0].users[0].id); expect(account?.secondary_admin_user_id).toBe(adduser?.id); }); it('アカウント情報の更新に失敗する(ディーラー未存在)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideSendgridService(service, { sendMail: async () => { return; }, }); const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts( source, ); const adduser = await makeTestUser(source, { account_id: tier4Accounts[0].account.id, external_id: 'typist-user-external-id', role: 'typist', }); await expect( service.updateAccountInfo( makeContext('trackingId', 'requestId'), tier4Accounts[0].users[0].external_id, tier4Accounts[0].account.tier, false, tier4Accounts[0].users[0].id, 123, adduser?.id, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010502'), HttpStatus.BAD_REQUEST), ); }); it('アカウント情報の更新に失敗する(プライマリ管理者ユーザ未存在)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideSendgridService(service, { sendMail: async () => { return; }, }); const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts( source, ); const tier5Accounts = await makeTestAccount(source, { parent_account_id: tier4Accounts[0].account.id, tier: 5, }); await expect( service.updateAccountInfo( makeContext('trackingId', 'requestId'), tier5Accounts.admin.external_id, tier5Accounts.account.tier, true, 999, tier4Accounts[0].account.id, tier4Accounts[1].users[0].id, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010502'), HttpStatus.BAD_REQUEST), ); }); it('アカウント情報の更新に失敗する(セカンダリ管理者ユーザ未存在)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideSendgridService(service, { sendMail: async () => { return; }, }); const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts( source, ); const tier5Accounts = await makeTestAccount(source, { parent_account_id: tier4Accounts[0].account.id, tier: 5, }); await expect( service.updateAccountInfo( makeContext('trackingId', 'requestId'), tier5Accounts.admin.external_id, tier5Accounts.account.tier, true, tier4Accounts[0].users[0].id, tier4Accounts[0].account.id, 999, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010502'), HttpStatus.BAD_REQUEST), ); }); }); describe('getAccountInfo', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('パラメータのユーザに対応するアカウント情報を取得できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts( source, ); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { parent_account_id: tier4Accounts[0].account.id, }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const accountResponse = await service.getAccountInfo( context, admin.external_id, ); //実行結果を確認 { expect(accountResponse.account.accountId).toBe(account.id); expect(accountResponse.account.companyName).toBe(account.company_name); expect(accountResponse.account.country).toBe(account.country); expect(accountResponse.account.delegationPermission).toBe( account.delegation_permission, ); expect(accountResponse.account.parentAccountId).toBe( account.parent_account_id, ); expect(accountResponse.account.primaryAdminUserId).toBe( account.primary_admin_user_id, ); expect(accountResponse.account.secondryAdminUserId).toBe(undefined); expect(accountResponse.account.tier).toBe(account.tier); expect(accountResponse.account.parentAccountName).toBe( tier4Accounts[0].account.company_name, ); } }); }); describe('getAuthors', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウント内のAuthorユーザーの一覧を取得できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const userId1 = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.AUTHOR, author_id: 'AUTHOR_ID_1', }); const userId2 = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.AUTHOR, author_id: 'AUTHOR_ID_2', }); const userId3 = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.TYPIST, }); // 作成したデータを確認 { const users = await getUsers(source); expect(users.length).toBe(4); expect(users[1].id).toBe(userId1.id); expect(users[2].id).toBe(userId2.id); expect(users[3].id).toBe(userId3.id); } const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const authors = await service.getAuthors(context, admin.external_id); //実行結果を確認 { expect(authors.length).toBe(2); expect(authors[0].id).toBe(userId1.id); expect(authors[0].authorId).toBe('AUTHOR_ID_1'); expect(authors[1].id).toBe(userId2.id); expect(authors[1].authorId).toBe('AUTHOR_ID_2'); } }); it('アカウント内のAuthorユーザーの一覧を取得できる(未認証ユーザー以外)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const userId1 = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.AUTHOR, author_id: 'AUTHOR_ID_1', }); const userId2 = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.AUTHOR, author_id: 'AUTHOR_ID_2', email_verified: false, }); // 作成したデータを確認 { const users = await getUsers(source); expect(users.length).toBe(3); expect(users[1].id).toBe(userId1.id); expect(users[2].id).toBe(userId2.id); } const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const authors = await service.getAuthors(context, admin.external_id); //実行結果を確認 { expect(authors.length).toBe(1); expect(authors[0].id).toBe(userId1.id); expect(authors[0].authorId).toBe('AUTHOR_ID_1'); } }); it('アカウント内のAuthorユーザーの一覧を取得できる(0件)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { admin } = await makeTestAccount(source, { tier: 5 }); // 作成したデータを確認 { const users = await getUsers(source); expect(users.length).toBe(1); } const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const authors = await service.getAuthors(context, admin.external_id); //実行結果を確認 { expect(authors.length).toBe(0); } }); it('DBアクセスに失敗した場合、500エラーとなる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); //DBアクセスに失敗するようにする const usersService = module.get( UsersRepositoryService, ); usersService.findAuthorUsers = jest.fn().mockRejectedValue('DB failed'); //実行結果を確認 try { await service.getAuthors(context, admin.external_id); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('getTypists', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウント内のTypistユーザーの一覧を取得できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const userId1 = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.TYPIST, external_id: 'typist1', }); const userId2 = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.TYPIST, external_id: 'typist2', }); const userId3 = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.AUTHOR, author_id: 'AUTHOR_ID_1', external_id: 'author1', }); // 作成したデータを確認 { const users = await getUsers(source); expect(users.length).toBe(4); expect(users[1].id).toBe(userId1.id); expect(users[2].id).toBe(userId2.id); expect(users[3].id).toBe(userId3.id); } const service = module.get(AccountsService); overrideAdB2cService(service, { getUsers: async () => [ { id: admin.external_id, displayName: '' }, { id: userId1.external_id, displayName: '' }, { id: userId2.external_id, displayName: '' }, { id: userId3.external_id, displayName: '' }, ], }); const context = makeContext(`uuidv4`, 'requestId'); const typists = await service.getTypists(context, admin.external_id); //実行結果を確認 { expect(typists.length).toBe(2); expect(typists[0].id).toBe(userId1.id); expect(typists[1].id).toBe(userId2.id); } }); it('アカウント内のTypistユーザーの一覧を取得できる(0件)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { admin } = await makeTestAccount(source, { tier: 5 }); // 作成したデータを確認 { const users = await getUsers(source); expect(users.length).toBe(1); } const service = module.get(AccountsService); overrideAdB2cService(service, { getUsers: async () => [{ id: admin.external_id, displayName: '' }], }); const context = makeContext(`uuidv4`, 'requestId'); const typists = await service.getTypists(context, admin.external_id); //実行結果を確認 { expect(typists.length).toBe(0); } }); it('アカウント内のTypistユーザーの一覧を取得できる(メール認証済みユーザーのみ)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const userId1 = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.TYPIST, }); const userId2 = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.TYPIST, }); const userId3 = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.TYPIST, email_verified: false, }); // 作成したデータを確認 { const users = await getUsers(source); expect(users.length).toBe(4); expect(users[1].id).toBe(userId1.id); expect(users[2].id).toBe(userId2.id); expect(users[3].id).toBe(userId3.id); } const service = module.get(AccountsService); overrideAdB2cService(service, { getUsers: async () => [ { id: admin.external_id, displayName: '' }, { id: userId1.external_id, displayName: '' }, { id: userId2.external_id, displayName: '' }, { id: userId3.external_id, displayName: '' }, ], }); const context = makeContext(`uuidv4`, 'requestId'); const typists = await service.getTypists(context, admin.external_id); //実行結果を確認 { expect(typists.length).toBe(2); expect(typists[0].id).toBe(userId1.id); expect(typists[1].id).toBe(userId2.id); } }); it('DBアクセスに失敗した場合、500エラーとなる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(AccountsService); overrideAdB2cService(service, { getUsers: async () => [{ id: admin.external_id, displayName: '' }], }); //DBアクセスに失敗するようにする const usersService = module.get( UsersRepositoryService, ); usersService.findTypistUsers = jest.fn().mockRejectedValue('DB failed'); const context = makeContext(`uuidv4`, 'requestId'); //実行結果を確認 try { await service.getTypists(context, admin.external_id); fail(); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('deleteAccountAndData', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウント情報が削除されること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideSendgridService(service, {}); // 第一~第四階層のアカウント作成 const { tier1Accounts: tier1Accounts, tier2Accounts: tier2Accounts, tier3Accounts: tier3Accounts, tier4Accounts: tier4Accounts, } = await makeHierarchicalAccounts(source); // 第五階層のアカウント作成(A) const tier5AccountsA = await makeTestAccount(source, { parent_account_id: tier4Accounts[0].account.id, tier: 5, }); // 第五階層のアカウント作成(B) const tier5AccountsB = await makeTestAccount(source, { parent_account_id: tier4Accounts[0].account.id, tier: 5, }); // ユーザの作成(A) const userA = await makeTestUser(source, { account_id: tier5AccountsA.account.id, }); // ユーザの作成(B) const userB = await makeTestUser(source, { account_id: tier5AccountsB.account.id, }); const context = makeContext(tier5AccountsA.admin.external_id, 'requestId'); // 第一階層~第五階層までのライセンス注文を作成 await createLicenseOrder( source, tier2Accounts[0].account.id, tier1Accounts[0].account.id, 100, 'PO001', ); await createLicenseOrder( source, tier3Accounts[0].account.id, tier2Accounts[0].account.id, 90, 'PO002', ); await createLicenseOrder( source, tier4Accounts[0].account.id, tier3Accounts[0].account.id, 80, 'PO003', ); await createLicenseOrder( source, tier5AccountsA.account.id, tier4Accounts[0].account.id, 40, 'PO004A', ); await createLicenseOrder( source, tier5AccountsB.account.id, tier4Accounts[0].account.id, 40, 'PO004B', ); // 第一階層~第五階層までのライセンス注文を発行済みにする await service.issueLicense( context, tier2Accounts[0].account.id, tier1Accounts[0].users[0].external_id, 1, 'PO001', ); await service.issueLicense( context, tier3Accounts[0].account.id, tier2Accounts[0].users[0].external_id, 2, 'PO002', ); await service.issueLicense( context, tier4Accounts[0].account.id, tier3Accounts[0].users[0].external_id, 3, 'PO003', ); await service.issueLicense( context, tier5AccountsA.account.id, tier4Accounts[0].users[0].external_id, 4, 'PO004A', ); await service.issueLicense( context, tier5AccountsB.account.id, tier4Accounts[0].users[0].external_id, 4, 'PO004B', ); // アカウントAのライセンスを取得する const licensesA = await getLicenses(source, tier5AccountsA.account.id); // アカウントAのライセンスを取得する const licensesB = await getLicenses(source, tier5AccountsB.account.id); const usersService = module.get(UsersService); // アカウントAのライセンスを割り当てる await usersService.allocateLicense( context, userA?.id ?? 0, licensesA[0].id, ); // アカウントBのライセンスを割り当てる await usersService.allocateLicense( context, userB?.id ?? 0, licensesB[0].id, ); // ADB2Cユーザーの削除成功 overrideAdB2cService(service, { deleteUsers: jest.fn(), }); // blobstorageコンテナの削除成功 overrideBlobstorageService(service, { deleteContainer: jest.fn(), }); // アカウント情報の削除 await service.deleteAccountAndData( context, tier5AccountsA.admin.external_id, tier5AccountsA.account.id, ); // DB内が想定通りになっているか確認 // 第五階層のアカウントAが削除されていること const accountRecordA = await getAccount(source, tier5AccountsA.account.id); expect(accountRecordA).toBe(null); const userRecordA = await getUser(source, userA?.id ?? 0); expect(userRecordA).toBe(null); // 第五階層のアカウントAのライセンスが削除されていること const licenseRecordA = await source.manager.find(License, { where: { account_id: tier5AccountsA.account.id }, }); expect(licenseRecordA.length).toBe(0); // 第五階層のアカウントAのライセンス注文履歴が削除されていること const licenseOrderRecordA = await source.manager.find(LicenseOrder, { where: { from_account_id: tier5AccountsA.account.id }, }); expect(licenseOrderRecordA.length).toBe(0); // 第五階層のアカウントAのライセンス割り当て履歴が削除されていること const LicenseAllocationHistoryRecordA = await source.manager.find( LicenseAllocationHistory, { where: { account_id: tier5AccountsA.account.id }, }, ); expect(LicenseAllocationHistoryRecordA.length).toBe(0); // 第五階層のアカウントBは削除されていないこと const accountRecordB = await getAccount(source, tier5AccountsB.account.id); expect(accountRecordB?.id).not.toBeNull(); const userRecordB = await getUser(source, userB?.id ?? 0); expect(userRecordB).not.toBeNull(); // 第五階層のアカウントBのライセンスが削除されていないこと const licenseRecordB = await source.manager.find(License, { where: { account_id: tier5AccountsB.account.id }, }); expect(licenseRecordB.length).not.toBe(0); // 第五階層のアカウントBのライセンス注文履歴が削除されていないこと const licenseOrderRecordB = await source.manager.find(LicenseOrder, { where: { from_account_id: tier5AccountsB.account.id }, }); expect(licenseOrderRecordB.length).not.toBe(0); // 第五階層のアカウントBのライセンス割り当て履歴が削除されていないこと const LicenseAllocationHistoryRecordB = await source.manager.find( LicenseAllocationHistory, { where: { account_id: tier5AccountsB.account.id }, }, ); expect(LicenseAllocationHistoryRecordB.length).not.toBe(0); const UserArchive = await getUserArchive(source); expect(UserArchive.length).toBe(2); const LicenseArchive = await getLicenseArchive(source); expect(LicenseArchive.length).toBe(40); const LicenseAllocationHistoryArchive = await getLicenseAllocationHistoryArchive(source); expect(LicenseAllocationHistoryArchive.length).toBe(1); }); it('アカウントの削除に失敗した場合はエラーを返す', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideSendgridService(service, {}); const loggerSpy = jest.spyOn(service['logger'], 'log').mockImplementation(); // 第五階層のアカウント作成 const tier4Accounts = await makeHierarchicalAccounts(source); const { account: account1, admin: admin1 } = await makeTestAccount(source, { parent_account_id: tier4Accounts.tier4Accounts[0].account.id, }); const account = account1; const admin = admin1; const context = makeContext(admin.external_id, 'requestId'); // 第五階層のアカウント作成 const tier5Accounts = await makeTestAccount(source, { parent_account_id: account.id, tier: 5, }); // ユーザの作成 const user = await makeTestUser(source, { account_id: tier5Accounts.account.id, }); // アカウント情報の削除失敗 overrideAccountsRepositoryService(service, { deleteAccountAndInsertArchives: jest.fn().mockRejectedValue(new Error()), }); // ADB2Cユーザーの削除成功 overrideAdB2cService(service, { deleteUsers: jest.fn(), }); // blobstorageコンテナの削除成功 overrideBlobstorageService(service, { deleteContainer: jest.fn(), }); // アカウント情報の削除に失敗することを確認 await expect( service.deleteAccountAndData( context, tier5Accounts.admin.external_id, tier5Accounts.account.id, ), ).rejects.toEqual( new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, ), ); // loggerSpyがスパイしているlogger.logメソッドが出力したログを確認(目視確認用) const logs = loggerSpy.mock.calls.map((call) => call[0]); console.log(logs); // DB内が削除されていないことを確認 const accountRecord = await getAccount(source, tier5Accounts.account.id); expect(accountRecord?.id).not.toBeNull(); const userRecord = await getUser(source, user?.id ?? 0); expect(userRecord?.id).not.toBeNull(); }); it('ADB2Cユーザーの削除失敗時は、MANUAL_RECOVERY_REQUIREDを出して処理続行', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideSendgridService(service, {}); const loggerSpy = jest.spyOn(service['logger'], 'log').mockImplementation(); // 第五階層のアカウント作成 const tier4Accounts = await makeHierarchicalAccounts(source); const { account: account1, admin: admin1 } = await makeTestAccount(source, { parent_account_id: tier4Accounts.tier4Accounts[0].account.id, }); const account = account1; const admin = admin1; const context = makeContext(admin.external_id, 'requestId'); // 第五階層のアカウント作成 const tier5Accounts = await makeTestAccount(source, { parent_account_id: account.id, tier: 5, }); // ユーザの作成 const user = await makeTestUser(source, { account_id: tier5Accounts.account.id, }); // ADB2Cユーザーの削除失敗 overrideAdB2cService(service, { deleteUsers: jest.fn().mockRejectedValue(new Error()), }); // blobstorageコンテナの削除成功 overrideBlobstorageService(service, { deleteContainer: jest.fn(), }); // 処理自体は成功することを確認 expect( await service.deleteAccountAndData( context, tier5Accounts.admin.external_id, tier5Accounts.account.id, ), ).toEqual(undefined); // loggerSpyがスパイしているlogger.logメソッドが出力したログを確認(目視確認用) const logs = loggerSpy.mock.calls.map((call) => call[0]); console.log(logs); // DB内が想定通りになっているか確認 const accountRecord = await getAccount(source, tier5Accounts.account.id); expect(accountRecord).toBe(null); const userRecord = await getUser(source, user?.id ?? 0); expect(userRecord).toBe(null); }); it('blobstorageコンテナを削除で失敗した場合は、MANUAL_RECOVERY_REQUIRED出して正常終了', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideSendgridService(service, {}); const loggerSpy = jest.spyOn(service['logger'], 'log').mockImplementation(); // 第五階層のアカウント作成 const tier4Accounts = await makeHierarchicalAccounts(source); const { account: account1, admin: admin1 } = await makeTestAccount(source, { parent_account_id: tier4Accounts.tier4Accounts[0].account.id, }); const account = account1; const admin = admin1; const context = makeContext(admin.external_id, 'requestId'); // 第五階層のアカウント作成 const tier5Accounts = await makeTestAccount(source, { parent_account_id: account.id, tier: 5, }); // ユーザの作成 const user = await makeTestUser(source, { account_id: tier5Accounts.account.id, }); // ADB2Cユーザーの削除成功 overrideAdB2cService(service, { deleteUsers: jest.fn(), }); // blobstorageコンテナの削除失敗 overrideBlobstorageService(service, { deleteContainer: jest.fn().mockRejectedValue(new Error()), }); // 処理自体は成功することを確認 expect( await service.deleteAccountAndData( context, tier5Accounts.admin.external_id, tier5Accounts.account.id, ), ).toEqual(undefined); // loggerSpyがスパイしているlogger.logメソッドが出力したログを確認(目視確認用) const logs = loggerSpy.mock.calls.map((call) => call[0]); console.log(logs); // DB内が想定通りになっているか確認 const accountRecord = await getAccount(source, tier5Accounts.account.id); expect(accountRecord).toBe(null); const userRecord = await getUser(source, user?.id ?? 0); expect(userRecord).toBe(null); }); }); describe('getAccountInfoMinimalAccess', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('IDトークンのsub情報からアカウントの階層情報を取得できること(第五階層)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5, }); const context = makeContext(admin.external_id, 'requestId'); // 作成したデータを確認 { const tier5Account = await getAccount(source, account.id); expect(tier5Account?.tier).toBe(5); } const tier = await service.getAccountInfoMinimalAccess( context, admin.external_id, ); //実行結果を確認 expect(tier).toBe(5); }); it('IDトークンのSub情報からアカウントの階層情報を取得できること(第四階層)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // 第四階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 4, }); const context = makeContext(admin.external_id, 'requestId'); // 作成したデータを確認 { const tier5Account = await getAccount(source, account.id); expect(tier5Account?.tier).toBe(4); } const tier = await service.getAccountInfoMinimalAccess( context, admin.external_id, ); //実行結果を確認 expect(tier).toBe(4); }); it('対象のユーザーが存在しない場合、400エラーとなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // 第四階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 4, }); const context = makeContext(admin.external_id, 'requestId'); // 作成したデータを確認 { const tier5Account = await getAccount(source, account.id); expect(tier5Account?.tier).toBe(4); } try { await service.getAccountInfoMinimalAccess(context, 'fail_external_id'); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E010204')); } else { fail(); } } }); it('DBアクセスに失敗した場合、500エラーとなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // 第四階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 4, }); const context = makeContext(admin.external_id, 'requestId'); // 作成したデータを確認 { const tier5Account = await getAccount(source, account.id); expect(tier5Account?.tier).toBe(4); } //DBアクセスに失敗するようにする const usersRepositoryService = module.get( UsersRepositoryService, ); usersRepositoryService.findUserByExternalId = jest .fn() .mockRejectedValue('DB failed'); try { await service.getAccountInfoMinimalAccess(context, admin.external_id); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('getCompanyName', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウントIDから会社名が取得できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5, company_name: 'testCompany', }); const context = makeContext(admin.external_id, 'requestId'); const response = await service.getCompanyName(context, account.id); expect({ companyName: 'testCompany' }).toEqual(response); }); it('アカウントが存在しない場合、400エラーとなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // 第五階層のアカウント作成 const { admin } = await makeTestAccount(source, { tier: 5, company_name: 'testCompany', }); const context = makeContext(admin.external_id, 'requestId'); try { await service.getCompanyName(context, 123); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E010501')); } else { fail(); } } }); });