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 { createAudioFile, createLicense, createLicenseOrder, createLicenseSetExpiryDateAndStatus, createOptionItems, createWorktype, getLicenseOrders, getOptionItems, getSortCriteriaList, getTaskFilterList, getTypistGroup, getTypistGroupMember, getTypistGroupMembers, 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, getAccountArchive, getTasks, getSortCriteria, getTaskFilter, getJobNumber, } from '../../common/test/utility'; import { AccountsService } from './accounts.service'; import { Context, makeContext } from '../../common/log'; import { ADB2C_SIGN_IN_TYPE, INITIAL_JOB_NUMBER, LICENSE_ALLOCATED_STATUS, LICENSE_ISSUE_STATUS, LICENSE_TYPE, MANUAL_RECOVERY_REQUIRED, OPTION_ITEM_VALUE_TYPE, STORAGE_SIZE_PER_LICENSE, TASK_STATUS, 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, createWorkflowTypist, getWorkflowTypists, getWorkflows, } from '../workflows/test/utility'; import { UsersService } from '../users/users.service'; import { truncateAllTable } from '../../common/test/init'; import { createTask, getCheckoutPermissions } from '../tasks/test/utility'; import { createCheckoutPermissions } from '../tasks/test/utility'; import { TestLogger } from '../../common/test/logger'; import { Account } from '../../repositories/accounts/entity/account.entity'; import { createTemplateFile, getTemplateFiles, } from '../templates/test/utility'; import { createUserGroup } from '../users/test/utility'; import { User } from '../../repositories/users/entity/user.entity'; 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウントを作成できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const 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 }; }, }); let _subject = ''; let _url: string | undefined = ''; overrideSendgridService(service, { sendMail: async ( context: Context, to: string[], cc: string[], from: string, subject: string, text: string, html: string, ) => { const urlPattern = /https?:\/\/[^\s]+/g; const urls = text.match(urlPattern); const url = urls?.pop(); _subject = subject; _url = url; }, }); 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?.auto_file_delete).toBe(false); expect(account?.file_retention_days).toBe(30); 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); // jobNumberの初期値が正しく設定されているか確認 const jobNumber = await getJobNumber(source, accountId); expect(jobNumber?.job_number).toBe(INITIAL_JOB_NUMBER); // sortCriteriaが正しく設定されているか確認 const sortCriteria = await getSortCriteria(source, user?.id ?? 0); expect(sortCriteria?.user_id).toBe(user?.id); expect(sortCriteria?.direction).toBe('ASC'); expect(sortCriteria?.parameter).toBe('JOB_NUMBER'); // taskFilterが正しく設定されているか確認 const taskFilter = await getTaskFilter(source, user?.id ?? 0); expect(taskFilter?.user_id).toBe(user?.id); expect(taskFilter?.author_id).toBe(null); expect(taskFilter?.file_name).toBe(null); // 想定通りのメールが送られているか確認 expect(_subject).toBe('User Registration Notification [U-102]'); expect( _url?.startsWith('http://localhost:8081/mail-confirm?verify='), ).toBeTruthy(); }); 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 getSortCriteriaList(source); expect(sortCriteria.length).toBe(0); const taskFilter = await getTaskFilterList(source); expect(taskFilter.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 getSortCriteriaList(source); expect(sortCriteria.length).toBe(0); const taskFilter = await getTaskFilterList(source); expect(taskFilter.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 getSortCriteriaList(source); expect(sortCriteria.length).toBe(0); const taskFilter = await getTaskFilterList(source); expect(taskFilter.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 getSortCriteriaList(source); expect(sortCriteria.length).toBe(1); const taskFilter = await getTaskFilterList(source); expect(taskFilter.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 getSortCriteriaList(source); expect(sortCriteria.length).toBe(0); const taskFilter = await getTaskFilterList(source); expect(taskFilter.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 getSortCriteriaList(source); expect(sortCriteria.length).toBe(1); const taskFilter = await getTaskFilterList(source); expect(taskFilter.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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('パートナーを追加できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const 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 }; }, }); let _subject = ''; let _url: string | undefined = ''; overrideSendgridService(service, { sendMail: async ( context: Context, to: string[], cc: string[], from: string, subject: string, text: string, html: string, ) => { const urlPattern = /https?:\/\/[^\s]+/g; const urls = text.match(urlPattern); const url = urls?.pop(); _subject = subject; _url = url; }, }); 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); const sortCriteria = await getSortCriteria(source, createdUser?.id ?? 0); expect(sortCriteria).not.toBeNull(); expect(sortCriteria?.user_id).toBe(createdUser?.id); expect(sortCriteria?.direction).toBe('ASC'); expect(sortCriteria?.parameter).toBe('JOB_NUMBER'); // taskFilterが正しく設定されているか確認 const taskFilter = await getTaskFilter( source, createdUser?.id ?? 0 ); expect(taskFilter?.user_id).toBe(createdUser?.id); expect(taskFilter?.author_id).toBe(null); expect(taskFilter?.file_name).toBe(null); const jobNumber = await getJobNumber(source, accountId); expect(jobNumber?.job_number).toBe(INITIAL_JOB_NUMBER); // 想定通りのメールが送られているか確認 expect(_subject).toBe('User Registration Notification [U-114]'); expect( _url?.startsWith('http://localhost:8081/mail-confirm/user?verify='), ).toBeTruthy(); } }); 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); const sortCriteria = await getSortCriteriaList(source); expect(sortCriteria.length).toBe(2); const taskFilter = await getTaskFilterList(source); expect(taskFilter.length).toBe(2); const jobNumber = await getJobNumber(source, accounts[1].id); expect(jobNumber?.job_number).toBe(INITIAL_JOB_NUMBER); // 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); const sortCriteria = await getSortCriteriaList(source); expect(sortCriteria.length).toBe(2); const taskFilter = await getTaskFilterList(source); expect(taskFilter.length).toBe(2); const jobNumber = await getJobNumber(source, accounts[1].id); expect(jobNumber?.job_number).toBe(INITIAL_JOB_NUMBER); // 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('第五階層のライセンス情報を取得する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 親アカウントと子アカウント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, new Date(2037, 1, 1, 23, 59, 59), '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: 40000000000, usedSize: 0, shortage: 2, isStorageAvailable: false, }); expect(resultChild2).toEqual({ totalLicense: 10, allocatedLicense: 5, reusableLicense: 0, freeLicense: 10, expiringWithin14daysLicense: 5, issueRequesting: 0, numberOfRequesting: 0, storageSize: 25000000000, usedSize: 0, shortage: 0, isStorageAvailable: false, }); }); it('第五階層のストレージ使用量が取得できる(ライセンスなし、音声ファイルなし)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // アカウントを作成する const { id: accountId } = ( await makeTestAccount(source, { tier: 5, company_name: 'company1', }) ).account; const service = module.get(AccountsService); const context = makeContext(`uuidv4`, 'xxx-xxx-xxx-xxx', 'requestId'); const result = await service.getLicenseSummary(context, accountId); expect(result).toEqual({ totalLicense: 0, allocatedLicense: 0, reusableLicense: 0, freeLicense: 0, expiringWithin14daysLicense: 0, issueRequesting: 0, numberOfRequesting: 0, storageSize: 0, usedSize: 0, shortage: 0, isStorageAvailable: false, }); }); it('第五階層のストレージ使用量が取得できる(ライセンスあり、音声ファイルあり)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // アカウントを作成する const { id: accountId } = ( await makeTestAccount(source, { tier: 5, company_name: 'company1', }) ).account; // audioFileを作成する const fileSize1 = 15000; await createAudioFile(source, accountId, 1, fileSize1); const fileSize2 = 17000; await createAudioFile(source, accountId, 1, fileSize2); // ライセンスを作成する const reusableLicense = 3; for (let i = 0; i < reusableLicense; i++) { await createLicenseSetExpiryDateAndStatus( source, accountId, new Date(2037, 1, 1, 23, 59, 59), LICENSE_ALLOCATED_STATUS.REUSABLE, ); } const allocatedLicense = 2; for (let i = 0; i < allocatedLicense; i++) { await createLicenseSetExpiryDateAndStatus( source, accountId, new Date(2037, 1, 1, 23, 59, 59), LICENSE_ALLOCATED_STATUS.ALLOCATED, i + 1, // なんでもよい。重複しないようにインクリメントする。 ); } const unallocatedLicense = 5; for (let i = 0; i < unallocatedLicense; i++) { await createLicenseSetExpiryDateAndStatus( source, accountId, null, LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); } // 自アカウントだけに絞って計算出来ていることを確認するため、別のアカウントとaudioFileとライセンス作成する。 const { id: otherAccountId } = ( await makeTestAccount(source, { tier: 5, company_name: 'company2', }) ).account; await createAudioFile(source, otherAccountId, 1, 5000); await createLicenseSetExpiryDateAndStatus( source, otherAccountId, new Date(2037, 1, 1, 23, 59, 59), LICENSE_ALLOCATED_STATUS.ALLOCATED, ); // テスト実行 const service = module.get(AccountsService); const context = makeContext(`uuidv4`, 'xxx-xxx-xxx-xxx', 'requestId'); const result = await service.getLicenseSummary(context, accountId); const expectedStorageSize = (reusableLicense + allocatedLicense) * STORAGE_SIZE_PER_LICENSE * 1000 * 1000 * 1000; // 5GB expect(result).toEqual({ totalLicense: reusableLicense + unallocatedLicense, allocatedLicense: allocatedLicense, reusableLicense: reusableLicense, freeLicense: unallocatedLicense, expiringWithin14daysLicense: 0, issueRequesting: 0, numberOfRequesting: 0, storageSize: expectedStorageSize, usedSize: fileSize1 + fileSize2, 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('パラメータのアカウント自身と子アカウントに紐つくライセンス情報を取得する(第五の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件)'Allocated'+ 有効期限間近でない(1件)'Allocated' → 6件 expect(response.childrenPartnerLicenses[0].allocatedLicense).toBe(6); // 有効期限間近(5件)+ 有効期限間近でない(10件) → 10件 expect(response.childrenPartnerLicenses[1].stockLicense).toBe(10); // 有効期限間近(5件)- (有効期限間近でない未割当(10件)'Unallocated' + 有効期限未設定(1件)) → -5件 → 0件 expect(response.childrenPartnerLicenses[1].shortage).toBe(0); // 有効期限間近(5件)'Allocated' → 5件 expect(response.childrenPartnerLicenses[1].allocatedLicense).toBe(5); // 有効期限が15日後のライセンス(1件)'Allocated' expect(response.childrenPartnerLicenses[2].stockLicense).toBe(0); // 有効期限が15日後のものはshortageにカウントされない expect(response.childrenPartnerLicenses[2].shortage).toBe(0); // 有効期限が15日後のライセンス(1件)'Allocated' expect(response.childrenPartnerLicenses[2].allocatedLicense).toBe(1); }); }); 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('指定したアカウント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'); // 注文履歴のライセンス種別が取得できていること expect(response.orderHistories[0].type).toBe(LICENSE_TYPE.NORMAL); }); 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('指定した注文を発行済みにする', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideAdB2cService(service, { getUsers: async () => [], }); 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); overrideAdB2cService(service, { getUsers: async () => [], }); 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); overrideAdB2cService(service, { getUsers: async () => [], }); 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('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('非表示指定されたDealer以外のDealerを取得できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 100件のDealerを作成し、country,id,company_nameを取得する const dealers: { country: string; id: number; name: string }[] = []; for (let i = 0; i < 100; i++) { const { id, company_name, country } = ( await makeTestAccount(source, { parent_account_id: i, tier: TIERS.TIER4, country: 'JP', company_name: `DEALER_${i}`, }) ).account; dealers.push({ id, name: company_name, country }); } const service = module.get(AccountsService); const context = makeContext(`uuidv4`, 'requestId'); const result = await service.getDealers(context); // idが50と99のDealerを非表示にする expect(result.dealers.length).toBe(98); expect(result).toEqual({ dealers: dealers.filter((dealer) => dealer.id !== 50 && dealer.id !== 99), }); }); 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('指定した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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('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('deleteTypistGroup', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('TypistGroupを削除できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを追加する const { id: typistUserId } = await makeTestUser(source, { account_id: account.id, external_id: 'typist-user-external-id', role: USER_ROLES.TYPIST, }); }); it('TypistGroupを削除できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを追加する const { id: typistUserId } = await makeTestUser(source, { account_id: account.id, external_id: 'typist-user-external-id', role: USER_ROLES.TYPIST, }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const typistGroupName = 'typist-group-name'; await service.createTypistGroup( context, admin.external_id, typistGroupName, [typistUserId], ); //作成したデータを確認 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).toBe(group[0].id); expect(groupUsers[0].user_id).toBe(typistUserId); } await service.deleteTypistGroup(context, admin.external_id, group[0].id); //実行結果を確認 { const typistGroups = await getTypistGroup(source, account.id); expect(typistGroups.length).toBe(0); const typistGroupUsers = await getTypistGroupMembers(source); expect(typistGroupUsers.length).toBe(0); } }); it('タイピストグループが存在しない場合、400エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを追加する const user = await makeTestUser(source, { account_id: account.id, external_id: 'typist-user-external-id', role: USER_ROLES.TYPIST, }); 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, [user.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).toBe(group[0].id); } try { await service.deleteTypistGroup(context, admin.external_id, 999); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E015001')); } else { fail(); } } }); it('タイピストグループがルーティングルールに紐づいていた場合、400エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを追加する const { id: typistUserId } = await makeTestUser(source, { account_id: account.id, external_id: 'typist-user-external-id', role: USER_ROLES.TYPIST, }); const { id: authorUserId } = await makeTestUser(source, { account_id: account.id, external_id: 'author-user-external-id', role: USER_ROLES.AUTHOR, }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const typistGroupName = 'typist-group-name'; await service.createTypistGroup( context, admin.external_id, typistGroupName, [typistUserId], ); const group = await getTypistGroup(source, account.id); const workflow = await createWorkflow(source, account.id, authorUserId); await createWorkflowTypist(source, workflow.id, undefined, group[0].id); //作成したデータを確認 { const workflowTypists = await getWorkflowTypists(source, workflow.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).toBe(group[0].id); expect(groupUsers[0].user_id).toBe(typistUserId); expect(workflowTypists.length).toBe(1); expect(workflowTypists[0].typist_group_id).toBe(group[0].id); } try { await service.deleteTypistGroup(context, admin.external_id, group[0].id); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E015002')); } else { fail(); } } }); it('タイピストグループがタスクのチェックアウト候補だった場合、400エラーを返却する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを追加する const { id: typistUserId } = await makeTestUser(source, { account_id: account.id, external_id: 'typist-user-external-id', role: USER_ROLES.TYPIST, }); const authorId = 'AUTHOR_ID'; const { id: authorUserId } = await makeTestUser(source, { account_id: account.id, external_id: 'author-user-external-id', role: USER_ROLES.AUTHOR, author_id: authorId, }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const typistGroupName = 'typist-group-name'; await service.createTypistGroup( context, admin.external_id, typistGroupName, [typistUserId], ); const group = await getTypistGroup(source, account.id); const { taskId } = await createTask( source, account.id, authorUserId, authorId, 'worktypeId', '01', '00000001', TASK_STATUS.UPLOADED, ); await createCheckoutPermissions(source, taskId, undefined, group[0].id); //作成したデータを確認 { const checkoutPermission = await getCheckoutPermissions(source, taskId); 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).toBe(group[0].id); expect(groupUsers[0].user_id).toBe(typistUserId); expect(checkoutPermission.length).toBe(1); expect(checkoutPermission[0].user_group_id).toBe(group[0].id); } try { await service.deleteTypistGroup(context, admin.external_id, group[0].id); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E015003')); } 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 typiptUserExternalId = 'typist-user-external-id'; const { id: typistUserId } = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, }); const service = module.get(AccountsService); const context = makeContext(admin.external_id, 'requestId'); const typistGroupName = 'typist-group-name'; await service.createTypistGroup( context, admin.external_id, typistGroupName, [typistUserId], ); //作成したデータを確認 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(typistUserId); } //DBアクセスに失敗するようにする const typistGroupService = module.get( UserGroupsRepositoryService, ); typistGroupService.deleteTypistGroup = jest .fn() .mockRejectedValue('DB failed'); try { await service.deleteTypistGroup(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('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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウント内の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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('指定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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('指定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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウントの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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('ライセンス発行のキャンセルが完了する(第一階層で実行)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { tier1Accounts: 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); overrideAdB2cService(service, { getUsers: async () => [], }); 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); overrideAdB2cService(service, { getUsers: async () => [], }); 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); overrideAdB2cService(service, { getUsers: async () => [], }); 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); overrideAdB2cService(service, { getUsers: async () => [], }); 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); overrideAdB2cService(service, { getUsers: async () => [], }); 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); overrideAdB2cService(service, { getUsers: async () => [], }); 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('パートナー一覧を取得する', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウント情報を更新する(第五階層が実行/セカンダリ管理者ユーザがnull)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); let _subject = ''; let _url: string | undefined = ''; overrideSendgridService(service, { sendMail: async ( context: Context, to: string[], cc: string[], from: string, subject: string, text: string, html: string, ) => { const urlPattern = /https?:\/\/[^\s]+/g; const urls = text.match(urlPattern); const url = urls?.pop(); _subject = subject; _url = url; }, }); overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'TEMP' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, }); 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); // 想定通りのメールが送られているか確認 expect(_subject).toBe('Account Edit Notification [U-112]'); expect(_url).toBe('http://localhost:8081/'); }); it('アカウント情報を更新する(第五階層以外が実行)', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideAdB2cService(service, { getUser: async () => { return { id: 'admin.external_id', displayName: 'admin' }; }, }); 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); overrideAdB2cService(service, { getUser: async () => { return { id: 'admin.external_id', displayName: 'admin' }; }, }); 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); overrideAdB2cService(service, { getUser: async () => { return { id: 'admin.external_id', displayName: 'admin' }; }, }); 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); overrideAdB2cService(service, { getUser: async () => { return { id: 'admin.external_id', displayName: 'admin' }; }, }); 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); overrideAdB2cService(service, { getUser: async () => { return { id: 'admin.external_id', displayName: 'admin' }; }, }); 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('パラメータのユーザに対応するアカウント情報を取得できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts( source, ); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { parent_account_id: tier4Accounts[0].account.id, }); const service = module.get(AccountsService); overrideAdB2cService(service, { getUsers: async () => [], }); 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウント内の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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウント内の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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウント情報が削除されること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); let _subject = ''; let _url: string | undefined = ''; overrideSendgridService(service, { sendMail: async ( context: Context, to: string[], cc: string[], from: string, subject: string, text: string, html: string, ) => { const urlPattern = /https?:\/\/[^\s]+/g; const urls = text.match(urlPattern); const url = urls?.pop(); _subject = subject; _url = url; }, }); // 第一~第四階層のアカウント作成 const { 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, ); overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'TEMP' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, getUsers: async (context, externalIds) => { return externalIds.map((x) => ({ displayName: 'admin', id: x, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: `mail+${x}@example.com`, }, ], })); }, 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); // 第五階層のアカウントAの管理者ユーザーが持つsortCriteriaが削除されていること const sortCriteriaAccuntAAdmin = await getSortCriteria( source, tier5AccountsA.admin.id, ); expect(sortCriteriaAccuntAAdmin).toBe(null); // 第五階層のアカウントAの一般ユーザーが持つsortCriteriaが削除されていること const sortCriteriaAccuntAUser = await getSortCriteria( source, userA?.id ?? 0, ); expect(sortCriteriaAccuntAUser).toBe(null); // 第五階層のアカウントAの管理者ユーザーが持つtaskFilterが削除されていること const taskFilterAccuntAAdmin = await getTaskFilter(source, tier5AccountsA.admin.id); expect(taskFilterAccuntAAdmin).toBe(null); // 第五階層のアカウントAの一般ユーザーが持つtaskFilterが削除されていること const taskFilterAccuntAUser = await getTaskFilter(source, userA?.id ?? 0); expect(taskFilterAccuntAUser).toBe(null); // 第五階層のアカウントAのJobNumberが削除されていること const jobNumberAccuntA = await getJobNumber( source, tier5AccountsA.account.id, ); expect(jobNumberAccuntA).toBe(null); // 第五階層のアカウント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 accountArchive = await getAccountArchive(source); expect(accountArchive.length).toBe(1); const archive = accountArchive.at(0); expect(archive?.id).toBe(tier5AccountsA.account.id); 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); // 第五階層のアカウントBの管理者ユーザーが持つsortCriteriaが削除されていないこと const sortCriteriaAccuntBAdmin = await getSortCriteria( source, tier5AccountsB.admin.id, ); expect(sortCriteriaAccuntBAdmin?.user_id).toBe(tier5AccountsB.admin.id); expect(sortCriteriaAccuntBAdmin?.direction).toBe('ASC'); expect(sortCriteriaAccuntBAdmin?.parameter).toBe('JOB_NUMBER'); // 第五階層のアカウントBの一般ユーザーが持つsortCriteriaが削除されていないこと const sortCriteriaAccuntBUser = await getSortCriteria( source, userB?.id ?? 0, ); expect(sortCriteriaAccuntBUser?.user_id).toBe(userB?.id ?? 0); expect(sortCriteriaAccuntBUser?.direction).toBe('ASC'); expect(sortCriteriaAccuntBUser?.parameter).toBe('FILE_LENGTH'); // 第五階層のアカウントBの管理者ユーザーが持つtaskFilterが削除されていないこと const taskFilterAccuntBAdmin = await getTaskFilter(source, tier5AccountsB.admin.id); expect(taskFilterAccuntBAdmin?.user_id).toBe(tier5AccountsB.admin.id); expect(taskFilterAccuntBAdmin?.author_id).toBe(null); expect(taskFilterAccuntBAdmin?.file_name).toBe(null); // 第五階層のアカウントBの一般ユーザーが持つtaskFilterが削除されていないこと const taskFilterAccuntBUser = await getTaskFilter(source, userB?.id ?? 0); expect(taskFilterAccuntBUser?.user_id).toBe(userB?.id); expect(taskFilterAccuntBUser?.author_id).toBe(null); expect(taskFilterAccuntBUser?.file_name).toBe(null); // 第五階層のアカウントBのJobNumberが削除されていないこと const jobNumberAccuntB = await getJobNumber( source, tier5AccountsB.account.id, ); expect(jobNumberAccuntB?.account_id).toBe(tier5AccountsB.account.id); expect(jobNumberAccuntB?.job_number).toBe('00000000'); expect(_subject).toBe('Account Deleted Notification [U-111]'); expect(_url).toBe('http://localhost:8081/'); }); it('アカウントの削除に失敗した場合はエラーを返す', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // ADB2Cユーザーの削除成功 overrideAdB2cService(service, { deleteUsers: jest.fn(), getUsers: jest.fn(), getUser: async () => { return { id: 'admin.external_id', displayName: 'admin' }; }, }); 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()), }); // 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(); // アーカイブが作成されていないことを確認 const accountArchive = await getAccountArchive(source); expect(accountArchive.length).toBe(0); const userArchive = await getUserArchive(source); expect(userArchive.length).toBe(0); }); it('ADB2Cユーザーの削除失敗時は、MANUAL_RECOVERY_REQUIREDを出して処理続行', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // ADB2Cユーザーの削除失敗 overrideAdB2cService(service, { deleteUsers: jest.fn().mockRejectedValue(new Error()), getUsers: jest.fn(), getUser: async () => { return { id: 'admin.external_id', displayName: 'admin' }; }, }); 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, }); // 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); const accountArchive = await getAccountArchive(source); expect(accountArchive.length).toBe(1); const archive = accountArchive.at(0); expect(archive?.id).toBe(tier5Accounts.account.id); const userArchive = await getUserArchive(source); expect(userArchive.length).toBe(2); const expectUserIds = [tier5Accounts.admin.id, user.id].sort(); const userArchiveIds = userArchive.map((x) => x.id).sort(); expect(expectUserIds).toStrictEqual(userArchiveIds); }); it('blobstorageコンテナを削除で失敗した場合は、MANUAL_RECOVERY_REQUIRED出して正常終了', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // ADB2Cユーザーの削除成功 overrideAdB2cService(service, { deleteUsers: jest.fn(), getUsers: jest.fn(), getUser: async () => { return { id: 'admin.external_id', displayName: 'admin' }; }, }); 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, }); // 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); const accountArchive = await getAccountArchive(source); expect(accountArchive.length).toBe(1); const archive = accountArchive.at(0); expect(archive?.id).toBe(tier5Accounts.account.id); const userArchive = await getUserArchive(source); expect(userArchive.length).toBe(2); const expectUserIds = [tier5Accounts.admin.id, user.id].sort(); const userArchiveIds = userArchive.map((x) => x.id).sort(); expect(expectUserIds).toStrictEqual(userArchiveIds); }); }); 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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('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が行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウント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(); } } }); }); describe('updateFileDeleteSetting', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('自動削除の設定を更新できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const 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 autoFileDelete = true; const retentionDays = 100; await service.updateFileDeleteSetting( context, admin.external_id, autoFileDelete, retentionDays, ); // 更新後データの確認 { const updatedAccount = await getAccount(source, account.id); expect(updatedAccount?.auto_file_delete).toBe(autoFileDelete); expect(updatedAccount?.file_retention_days).toBe(retentionDays); } }); it('対象アカウント非存在時に500エラーを返す', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // 更新するデータを設定 const nonExistentId = `nonExistentId`; // 存在しないIDを指定 const autoFileDelete = true; const retentionDays = 100; const context = makeContext(nonExistentId, 'requestId'); try { await service.updateFileDeleteSetting( context, nonExistentId, autoFileDelete, retentionDays, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } 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: 5, }); const context = makeContext(admin.external_id, 'requestId'); // 作成したデータを確認 { const tier5Account = await getAccount(source, account.id); expect(tier5Account?.tier).toBe(5); } //DBアクセスに失敗するようにする const usersRepositoryService = module.get( UsersRepositoryService, ); usersRepositoryService.findUserByExternalId = jest .fn() .mockRejectedValue('DB failed'); try { await service.updateFileDeleteSetting( context, admin.external_id, true, 100, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('updateRestrictionStatus', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('アカウント利用制限のON/OFFが出来る', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第一階層のアカウントを作成する const { admin } = await makeTestAccount(source, { tier: 1, }); const context = makeContext(admin.external_id, 'requestId'); // 操作対象の第五階層のアカウントを作成する const { account } = await makeTestAccount(source, { tier: 5, locked: false, }); const service = module.get(AccountsService); // 利用制限をかけられるか確認。 { const restricted = true; await service.updateRestrictionStatus(context, account.id, restricted); const result = await getAccount(source, account.id); expect(result?.locked).toBe(restricted); } // 利用制限を解除できるか確認。 { const restricted = false; await service.updateRestrictionStatus(context, account.id, restricted); const result = await getAccount(source, account.id); expect(result?.locked).toBe(restricted); } }); it('対象アカウントが存在しない場合は500エラーを返す', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントを作成せずに実行する const context = makeContext('dummy', 'requestId'); try { await service.updateRestrictionStatus(context, 0, false); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); it('DBアクセスに失敗した場合、500エラーを返す', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 第一階層のアカウントを作成する const { admin } = await makeTestAccount(source, { tier: 1, }); const context = makeContext(admin.external_id, 'requestId'); // 操作対象の第五階層のアカウントを作成する const { account } = await makeTestAccount(source, { tier: 5, locked: false, }); //DBアクセスに失敗するようにする const accountsRepositoryService = module.get( AccountsRepositoryService, ); accountsRepositoryService.updateRestrictionStatus = jest .fn() .mockRejectedValue('DB failed'); // テスト実行する const service = module.get(AccountsService); try { await service.updateRestrictionStatus(context, account.id, true); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('switchParent', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('第三階層<->第四階層間の階層構造変更処理ができる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 新規親アカウントのアカウントを作成する const { account: newParent } = await makeTestAccount(source, { tier: 3, country: `AU`, }); // 子アカウントを作成する const { account: child1 } = await makeTestAccount(source, { tier: 4, country: `AU`, parent_account_id: undefined, delegation_permission: true, }); const { account: child2 } = await makeTestAccount(source, { tier: 4, country: `NZ`, // 同じリージョンで切り替えできることの確認 parent_account_id: undefined, delegation_permission: true, }); // ライセンス注文作成 await createLicenseOrder(source, child1.id, 10, 1); // 注文先アカウントは何でもいいため適当 await createLicenseOrder(source, child1.id, 10, 1); // 親アカウントは何でもいいため適当 await createLicenseOrder(source, child2.id, 10, 1); // 親アカウントは何でもいいため適当 // テスト実行 const context = makeContext(`external_id`, 'requestId'); const service = module.get(AccountsService); await service.switchParent(context, newParent.id, [child1.id, child2.id]); const child1Result = await getAccount(source, child1.id); const child2Result = await getAccount(source, child2.id); const child1LicenseOrderResult = await getLicenseOrders(source, child1.id); const child2LicenseOrderResult = await getLicenseOrders(source, child2.id); // アカウントテーブルの更新確認 expect(child1Result?.parent_account_id).toBe(newParent.id); expect(child1Result?.delegation_permission).toBe(false); expect(child2Result?.parent_account_id).toBe(newParent.id); expect(child2Result?.delegation_permission).toBe(false); // ライセンス注文が全てcancelされていることの確認 expect(child1LicenseOrderResult.length).toBe(2); const child1LicenseOrderStatuses = child1LicenseOrderResult.every( (x) => x.status === LICENSE_ISSUE_STATUS.CANCELED, ); expect(child1LicenseOrderStatuses).toBeTruthy(); expect(child2LicenseOrderResult.length).toBe(1); const child2LicenseOrderStatuses = child2LicenseOrderResult.every( (x) => x.status === LICENSE_ISSUE_STATUS.CANCELED, ); expect(child2LicenseOrderStatuses).toBeTruthy(); }); it('第四階層<->第五階層間の階層構造変更処理ができる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 新規親アカウントのアカウントを作成する const { account: newParent } = await makeTestAccount(source, { tier: 4, country: `AU`, }); // 子アカウントを作成する const { account: child1 } = await makeTestAccount(source, { tier: 5, country: `AU`, parent_account_id: undefined, delegation_permission: true, }); const { account: child2 } = await makeTestAccount(source, { tier: 5, country: `AU`, parent_account_id: undefined, delegation_permission: true, }); // ライセンス注文作成 await createLicenseOrder(source, child1.id, 10, 1); // 注文先アカウントは何でもいいため適当 await createLicenseOrder(source, child1.id, 10, 1); // 親アカウントは何でもいいため適当 await createLicenseOrder(source, child2.id, 10, 1); // 親アカウントは何でもいいため適当 // テスト実行 const context = makeContext(`external_id`, 'requestId'); const service = module.get(AccountsService); await service.switchParent(context, newParent.id, [child1.id, child2.id]); const child1Result = await getAccount(source, child1.id); const child2Result = await getAccount(source, child2.id); const child1LicenseOrderResult = await getLicenseOrders(source, child1.id); const child2LicenseOrderResult = await getLicenseOrders(source, child2.id); // アカウントテーブルの更新確認 expect(child1Result?.parent_account_id).toBe(newParent.id); expect(child1Result?.delegation_permission).toBe(false); expect(child2Result?.parent_account_id).toBe(newParent.id); expect(child2Result?.delegation_permission).toBe(false); // ライセンス注文が全てcancelされていることの確認 expect(child1LicenseOrderResult.length).toBe(2); const child1LicenseOrderStatuses = child1LicenseOrderResult.every( (x) => x.status === LICENSE_ISSUE_STATUS.CANCELED, ); expect(child1LicenseOrderStatuses).toBeTruthy(); expect(child2LicenseOrderResult.length).toBe(1); const child2LicenseOrderStatuses = child2LicenseOrderResult.every( (x) => x.status === LICENSE_ISSUE_STATUS.CANCELED, ); expect(child2LicenseOrderStatuses).toBeTruthy(); }); it('第四<->第五の切り替えで、親子で国が異なる場合でも階層構造変更処理ができる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 新規親アカウントのアカウントを作成する const { account: newParent } = await makeTestAccount(source, { tier: 4, country: `AU`, }); // 子アカウントを作成する const { account: child1 } = await makeTestAccount(source, { tier: 5, country: `NZ`, parent_account_id: undefined, delegation_permission: true, }); const { account: child2 } = await makeTestAccount(source, { tier: 5, country: `GB`, parent_account_id: undefined, delegation_permission: true, }); // ライセンス注文作成 await createLicenseOrder(source, child1.id, 10, 1); // 注文先アカウントは何でもいいため適当 await createLicenseOrder(source, child1.id, 10, 1); // 親アカウントは何でもいいため適当 await createLicenseOrder(source, child2.id, 10, 1); // 親アカウントは何でもいいため適当 // テスト実行 const context = makeContext(`external_id`, 'requestId'); const service = module.get(AccountsService); await service.switchParent(context, newParent.id, [child1.id, child2.id]); const child1Result = await getAccount(source, child1.id); const child2Result = await getAccount(source, child2.id); const child1LicenseOrderResult = await getLicenseOrders(source, child1.id); const child2LicenseOrderResult = await getLicenseOrders(source, child2.id); // アカウントテーブルの更新確認 expect(child1Result?.parent_account_id).toBe(newParent.id); expect(child1Result?.delegation_permission).toBe(false); expect(child2Result?.parent_account_id).toBe(newParent.id); expect(child2Result?.delegation_permission).toBe(false); // ライセンス注文が全てcancelされていることの確認 expect(child1LicenseOrderResult.length).toBe(2); const child1LicenseOrderStatuses = child1LicenseOrderResult.every( (x) => x.status === LICENSE_ISSUE_STATUS.CANCELED, ); expect(child1LicenseOrderStatuses).toBeTruthy(); expect(child2LicenseOrderResult.length).toBe(1); const child2LicenseOrderStatuses = child2LicenseOrderResult.every( (x) => x.status === LICENSE_ISSUE_STATUS.CANCELED, ); expect(child2LicenseOrderStatuses).toBeTruthy(); }); it('切り替え先親アカウントが存在しない場合は400エラー(親アカウント不在エラー)を返す', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 子アカウントの取得では1件だけ返すようにする const accountsRepositoryService = module.get( AccountsRepositoryService, ); const child = new Account(); child.id = 1; accountsRepositoryService.findAccountsById = jest .fn() .mockResolvedValue([child]); const context = makeContext('external_id', 'requestId'); const service = module.get(AccountsService); try { // 切り替え先アカウントを作成せずに実行する await service.switchParent(context, 10, [child.id]); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E017001')); } else { fail(); } } }); it('切り替え先親アカウントが第三・第四以外の階層の場合は400エラー(階層関係不適切エラー)を返す', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 子アカウントの取得では1件だけ返すようにする const accountsRepositoryService = module.get( AccountsRepositoryService, ); const child = new Account(); child.id = 1; child.tier = 4; accountsRepositoryService.findAccountsById = jest .fn() .mockResolvedValue([child]); const context = makeContext('external_id', 'requestId'); const service = module.get(AccountsService); // 親アカウントの階層が第五階層の場合に失敗する const parent = new Account(); parent.id = 10; try { parent.tier = 5; accountsRepositoryService.findAccountById = jest .fn() .mockResolvedValue(parent); await service.switchParent(context, parent.id, [child.id]); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E017002')); } else { fail(); } } try { // 親アカウントの階層が第二階層の場合に失敗する parent.tier = 2; accountsRepositoryService.findAccountById = jest .fn() .mockResolvedValue(parent); await service.switchParent(context, parent.id, [child.id]); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E017002')); } else { fail(); } } try { // 親アカウントの階層が第一階層の場合に失敗する parent.tier = 1; accountsRepositoryService.findAccountById = jest .fn() .mockResolvedValue(parent); await service.switchParent(context, parent.id, [child.id]); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E017002')); } else { fail(); } } }); it('第五階層の子アカウントに対して、第三階層の切り替え先親アカウントを指定した場合は400エラー(階層関係不適切エラー)を返す', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 子アカウントの取得では1件だけ返すようにする const accountsRepositoryService = module.get( AccountsRepositoryService, ); const child1 = new Account(); child1.id = 1; child1.tier = 5; const child2 = new Account(); child1.id = 1; child1.tier = 4; accountsRepositoryService.findAccountsById = jest .fn() .mockResolvedValue([child1, child2]); const context = makeContext('external_id', 'requestId'); const service = module.get(AccountsService); const parent = new Account(); parent.id = 10; parent.tier = 3; try { accountsRepositoryService.findAccountById = jest .fn() .mockResolvedValue(parent); await service.switchParent(context, parent.id, [child1.id, child2.id]); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E017002')); } else { fail(); } } }); it('第三<->第四の切り替えで、親子でリージョンが異なる場合は400エラー(リージョン関係不一致エラー)を返す', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const accountsRepositoryService = module.get( AccountsRepositoryService, ); const child1 = new Account(); child1.id = 1; child1.tier = 4; child1.country = 'AU'; const child2 = new Account(); child2.id = 2; child2.tier = 4; child2.country = 'US'; // このアカウントだけリージョンが異なるようにしておく accountsRepositoryService.findAccountsById = jest .fn() .mockResolvedValue([child1, child2]); const context = makeContext('external_id', 'requestId'); const service = module.get(AccountsService); const parent = new Account(); parent.id = 10; parent.tier = 3; parent.country = 'AU'; try { accountsRepositoryService.findAccountById = jest .fn() .mockResolvedValue(parent); await service.switchParent(context, parent.id, [child1.id, child2.id]); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E017003')); } else { fail(); } } }); }); describe('deletePartnerAccount', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('パートナーアカウント情報が削除されること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); let _subject = ''; overrideSendgridService(service, { sendMail: async ( context: Context, to: string[], cc: string[], from: string, subject: string, text: string, html: string, ) => { _subject = subject; }, }); // 第3階層のアカウント作成 const { account: tier3Account, admin: tier3Admin } = await makeTestAccount( source, { tier: 3 }, ); // 第4階層のアカウント作成 const { account: tier4Account, admin: tier4Admin } = await makeTestAccount( source, { parent_account_id: tier3Account.id, tier: 4, }, { role: USER_ROLES.AUTHOR, author_id: 'AUTHOR_ID', }, ); const typist = await makeTestUser(source, { account_id: tier4Account.id, role: USER_ROLES.TYPIST, }); const context = makeContext(tier3Admin.external_id, 'requestId'); // ライセンス await createLicenseOrder( source, tier4Account.id, tier3Account.id, 100, 'PO001', ); await createLicenseSetExpiryDateAndStatus( source, tier4Account.id, null, LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); // ワークタイプ await createWorktype(source, tier4Account.id, 'worktype1'); // タスク await createTask( source, tier4Account.id, tier4Admin.id, tier4Admin.author_id ?? '', '', '00', '00000001', TASK_STATUS.UPLOADED, ); // ユーザーグループ await createUserGroup(source, tier4Account.id, 'usergroup1', [typist.id]); // テンプレートファイル await createTemplateFile(source, tier4Account.id, 'template1', 'url'); overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, getUsers: async (context, externalIds) => externalIds.map((externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }), deleteUsers: jest.fn(), }); // blobstorageコンテナの削除成功 overrideBlobstorageService(service, { deleteContainer: jest.fn(), }); // 作成したデータを確認 { const tier4AccountRecord = await getAccount(source, tier4Account.id); expect(tier4AccountRecord?.id).toBe(tier4Account.id); expect(tier4AccountRecord?.tier).toBe(4); const userRecord = await getUsers(source); expect(userRecord.length).toBe(3); const licenseRecord = await getLicenses(source, tier4Account.id); expect(licenseRecord.length).toBe(1); const licenseOrderRecord = await source.manager.find(LicenseOrder, { where: { from_account_id: tier4Account.id }, }); expect(licenseOrderRecord.length).toBe(1); const worktypeRecord = await getWorktypes(source, tier4Account.id); expect(worktypeRecord.length).toBe(1); const taskRecord = await getTasks(source, tier4Account.id); expect(taskRecord.length).toBe(1); const userGroupRecord = await getTypistGroup(source, tier4Account.id); expect(userGroupRecord.length).toBe(1); const templateFileRecord = await getTemplateFiles( source, tier4Account.id, ); expect(templateFileRecord.length).toBe(1); const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); expect(tier4AccountJobNumber?.job_number).toBe('00000000'); expect(tier4AccountJobNumber?.account_id).toBe(tier4Account.id); const tier4AccountAdminSortCriteria = await getSortCriteria( source, tier4Admin?.id ?? 0, ); expect(tier4AccountAdminSortCriteria?.direction).toBe('ASC'); expect(tier4AccountAdminSortCriteria?.parameter).toBe('JOB_NUMBER'); const tier4AccountTypistSortCriteria = await getSortCriteria( source, typist.id, ); expect(tier4AccountTypistSortCriteria?.direction).toBe('ASC'); expect(tier4AccountTypistSortCriteria?.parameter).toBe('FILE_LENGTH'); const tier4AccountAdminTaskFilter = await getTaskFilter(source, tier4Admin?.id ?? 0); expect(tier4AccountAdminTaskFilter?.user_id).toBe(tier4Admin?.id); expect(tier4AccountAdminTaskFilter?.author_id).toBe(null); expect(tier4AccountAdminTaskFilter?.file_name).toBe(null); const tier4AccountTypisttaskFilter = await getTaskFilter(source, typist.id); expect(tier4AccountTypisttaskFilter?.user_id).toBe(typist.id); expect(tier4AccountTypisttaskFilter?.author_id).toBe(null); expect(tier4AccountTypisttaskFilter?.file_name).toBe(null); } // パートナーアカウント情報の削除 await service.deletePartnerAccount( context, tier3Admin.external_id, tier4Account.id, ); // DB内が想定通りになっているか確認 { // パートナーアカウントが削除されていること const account4Record = await getAccount(source, tier4Account.id); expect(account4Record).toBe(null); const userRecordA = await getUser(source, tier4Admin?.id ?? 0); expect(userRecordA).toBe(null); // パートナーアカウントのユーザーが削除されていること const userRecord = await getUsers(source); expect( userRecord.filter((x) => x.account_id === tier4Account.id).length, ).toBe(0); // パートナーアカウントのJobNumberが削除されていること const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); expect(tier4AccountJobNumber).toBe(null); // パートナーアカウントのソート条件が削除されていること const tier4AccountAdminSortCriteria = await getSortCriteria( source, tier4Admin?.id ?? 0, ); expect(tier4AccountAdminSortCriteria).toBe(null); const tier4AccountTypistSortCriteria = await getSortCriteria( source, typist.id, ); expect(tier4AccountTypistSortCriteria).toBe(null); // パートナーアカウントのTask Filter条件が削除されていること const tier4AccountAdminTaskFilter = await getTaskFilter(source, tier4Admin?.id ?? 0); expect(tier4AccountAdminTaskFilter).toBe(null); const tier4AccountTypisttaskFilter = await getTaskFilter(source, typist.id); expect(tier4AccountTypisttaskFilter).toBe(null); // パートナーアカウントのライセンスが削除されていること const licenseRecord = await source.manager.find(License, { where: { account_id: tier4Account.id }, }); expect(licenseRecord.length).toBe(0); // パートナーアカウントのライセンス注文履歴が削除されていること const licenseOrderRecord = await source.manager.find(LicenseOrder, { where: { from_account_id: tier4Account.id }, }); expect(licenseOrderRecord.length).toBe(0); // パートナーアカウントのライセンス割り当て履歴が削除されていること const LicenseAllocationHistoryRecord = await source.manager.find( LicenseAllocationHistory, { where: { account_id: tier4Account.id } }, ); expect(LicenseAllocationHistoryRecord.length).toBe(0); // パートナーアカウントのワークタイプが削除されていること const worktypeRecord = await getWorktypes(source, tier4Account.id); expect(worktypeRecord.length).toBe(0); // パートナーアカウントのタスクが削除されていること const taskRecord = await getTasks(source, tier4Account.id); expect(taskRecord.length).toBe(0); // パートナーアカウントのユーザーグループが削除されていること const userGroupRecord = await getTypistGroup(source, tier4Account.id); expect(userGroupRecord.length).toBe(0); // パートナーアカウントのテンプレートファイルが削除されていること const templateFileRecord = await getTemplateFiles( source, tier4Account.id, ); expect(templateFileRecord.length).toBe(0); // パートナーアカウント削除完了通知が送信されていること expect(_subject).toBe('Partner Account Deleted Notification [U-123]'); } }); it('パートナーアカウントの親が実行者でない場合、エラーとなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); let _subject = ''; overrideSendgridService(service, { sendMail: async ( context: Context, to: string[], cc: string[], from: string, subject: string, text: string, html: string, ) => { _subject = subject; }, }); // 第3階層のアカウント作成 const { account: tier3Account, admin: tier3Admin } = await makeTestAccount( source, { tier: 3 }, ); const { account: tier3Parent } = await makeTestAccount(source, { tier: 3 }); // 第4階層のアカウント作成 const { account: tier4Account, admin: tier4Admin } = await makeTestAccount( source, { parent_account_id: tier3Parent.id, tier: 4, }, { role: USER_ROLES.AUTHOR, author_id: 'AUTHOR_ID', }, ); const typist = await makeTestUser(source, { account_id: tier4Account.id, role: USER_ROLES.TYPIST, }); const context = makeContext(tier3Admin.external_id, 'requestId'); // ライセンス await createLicenseOrder( source, tier4Account.id, tier3Account.id, 100, 'PO001', ); await createLicenseSetExpiryDateAndStatus( source, tier4Account.id, null, LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); // ワークタイプ await createWorktype(source, tier4Account.id, 'worktype1'); // タスク await createTask( source, tier4Account.id, tier4Admin.id, tier4Admin.author_id ?? '', '', '00', '00000001', TASK_STATUS.UPLOADED, ); // ユーザーグループ await createUserGroup(source, tier4Account.id, 'usergroup1', [typist.id]); // テンプレートファイル await createTemplateFile(source, tier4Account.id, 'template1', 'url'); overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, getUsers: async (context, externalIds) => externalIds.map((externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }), deleteUsers: jest.fn(), }); // blobstorageコンテナの削除成功 overrideBlobstorageService(service, { deleteContainer: jest.fn(), }); // 作成したデータを確認 { const tier4AccountRecord = await getAccount(source, tier4Account.id); expect(tier4AccountRecord?.id).toBe(tier4Account.id); expect(tier4AccountRecord?.tier).toBe(4); const userRecord = await getUsers(source); expect(userRecord.length).toBe(4); const licenseRecord = await getLicenses(source, tier4Account.id); expect(licenseRecord.length).toBe(1); const licenseOrderRecord = await source.manager.find(LicenseOrder, { where: { from_account_id: tier4Account.id }, }); expect(licenseOrderRecord.length).toBe(1); const worktypeRecord = await getWorktypes(source, tier4Account.id); expect(worktypeRecord.length).toBe(1); const taskRecord = await getTasks(source, tier4Account.id); expect(taskRecord.length).toBe(1); const userGroupRecord = await getTypistGroup(source, tier4Account.id); expect(userGroupRecord.length).toBe(1); const templateFileRecord = await getTemplateFiles( source, tier4Account.id, ); expect(templateFileRecord.length).toBe(1); const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); expect(tier4AccountJobNumber?.job_number).toBe('00000000'); expect(tier4AccountJobNumber?.account_id).toBe(tier4Account.id); const tier4AccountAdminSortCriteria = await getSortCriteria( source, tier4Admin?.id ?? 0, ); expect(tier4AccountAdminSortCriteria?.direction).toBe('ASC'); expect(tier4AccountAdminSortCriteria?.parameter).toBe('JOB_NUMBER'); const tier4AccountTypistSortCriteria = await getSortCriteria( source, typist.id, ); expect(tier4AccountTypistSortCriteria?.direction).toBe('ASC'); expect(tier4AccountTypistSortCriteria?.parameter).toBe('FILE_LENGTH'); const tier4AccountAdminTaskFilter = await getTaskFilter(source, tier4Admin?.id ?? 0); expect(tier4AccountAdminTaskFilter?.user_id).toBe(tier4Admin?.id); expect(tier4AccountAdminTaskFilter?.author_id).toBe(null); expect(tier4AccountAdminTaskFilter?.file_name).toBe(null); const tier4AccountTypisttaskFilter = await getTaskFilter(source, typist.id,); expect(tier4AccountTypisttaskFilter?.user_id).toBe(typist.id); expect(tier4AccountTypisttaskFilter?.author_id).toBe(null); expect(tier4AccountTypisttaskFilter?.file_name).toBe(null); } try { // パートナーアカウント情報の削除 await service.deletePartnerAccount( context, tier3Admin.external_id, tier4Account.id, ); fail(); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E018001')); } else { fail(); } } }); it('パートナーアカウントが親が子アカウントを持つ場合、エラーとなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); let _subject = ''; overrideSendgridService(service, { sendMail: async ( context: Context, to: string[], cc: string[], from: string, subject: string, text: string, html: string, ) => { _subject = subject; }, }); // 第3階層のアカウント作成 const { account: tier3Account, admin: tier3Admin } = await makeTestAccount( source, { tier: 3 }, ); // 第4階層のアカウント作成 const { account: tier4Account, admin: tier4Admin } = await makeTestAccount( source, { parent_account_id: tier3Account.id, tier: 4, }, { role: USER_ROLES.AUTHOR, author_id: 'AUTHOR_ID', }, ); const typist = await makeTestUser(source, { account_id: tier4Account.id, role: USER_ROLES.TYPIST, }); // 第5階層のアカウント作成 await makeTestAccount(source, { parent_account_id: tier4Account.id, tier: 5, }); const context = makeContext(tier3Admin.external_id, 'requestId'); // ライセンス await createLicenseOrder( source, tier4Account.id, tier3Account.id, 100, 'PO001', ); await createLicenseSetExpiryDateAndStatus( source, tier4Account.id, null, LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); // ワークタイプ await createWorktype(source, tier4Account.id, 'worktype1'); // タスク await createTask( source, tier4Account.id, tier4Admin.id, tier4Admin.author_id ?? '', '', '00', '00000001', TASK_STATUS.UPLOADED, ); // ユーザーグループ await createUserGroup(source, tier4Account.id, 'usergroup1', [typist.id]); // テンプレートファイル await createTemplateFile(source, tier4Account.id, 'template1', 'url'); overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, deleteUsers: jest.fn(), }); // blobstorageコンテナの削除成功 overrideBlobstorageService(service, { deleteContainer: jest.fn(), }); // 作成したデータを確認 { const tier4AccountRecord = await getAccount(source, tier4Account.id); expect(tier4AccountRecord?.id).toBe(tier4Account.id); expect(tier4AccountRecord?.tier).toBe(4); const userRecord = await getUsers(source); expect(userRecord.length).toBe(4); const licenseRecord = await getLicenses(source, tier4Account.id); expect(licenseRecord.length).toBe(1); const licenseOrderRecord = await source.manager.find(LicenseOrder, { where: { from_account_id: tier4Account.id }, }); expect(licenseOrderRecord.length).toBe(1); const worktypeRecord = await getWorktypes(source, tier4Account.id); expect(worktypeRecord.length).toBe(1); const taskRecord = await getTasks(source, tier4Account.id); expect(taskRecord.length).toBe(1); const userGroupRecord = await getTypistGroup(source, tier4Account.id); expect(userGroupRecord.length).toBe(1); const templateFileRecord = await getTemplateFiles( source, tier4Account.id, ); expect(templateFileRecord.length).toBe(1); const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); expect(tier4AccountJobNumber?.job_number).toBe('00000000'); expect(tier4AccountJobNumber?.account_id).toBe(tier4Account.id); const tier4AccountAdminSortCriteria = await getSortCriteria( source, tier4Admin?.id ?? 0, ); expect(tier4AccountAdminSortCriteria?.direction).toBe('ASC'); expect(tier4AccountAdminSortCriteria?.parameter).toBe('JOB_NUMBER'); const tier4AccountTypistSortCriteria = await getSortCriteria( source, typist.id, ); expect(tier4AccountTypistSortCriteria?.direction).toBe('ASC'); expect(tier4AccountTypistSortCriteria?.parameter).toBe('FILE_LENGTH'); const tier4AccountAdminTaskFilter = await getTaskFilter(source, tier4Admin?.id ?? 0); expect(tier4AccountAdminTaskFilter?.user_id).toBe(tier4Admin?.id); expect(tier4AccountAdminTaskFilter?.author_id).toBe(null); expect(tier4AccountAdminTaskFilter?.file_name).toBe(null); const tier4AccountTypisttaskFilter = await getTaskFilter(source, typist.id); expect(tier4AccountTypisttaskFilter?.user_id).toBe(typist.id); expect(tier4AccountTypisttaskFilter?.author_id).toBe(null); expect(tier4AccountTypisttaskFilter?.file_name).toBe(null); } try { // パートナーアカウント情報の削除 await service.deletePartnerAccount( context, tier3Admin.external_id, tier4Account.id, ); fail(); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E018001')); } else { fail(); } } }); it('ADB2Cユーザーの削除失敗時は、MANUAL_RECOVERY_REQUIREDを出して処理続行すること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); let _subject = ''; overrideSendgridService(service, { sendMail: async ( context: Context, to: string[], cc: string[], from: string, subject: string, text: string, html: string, ) => { _subject = subject; }, }); const loggerSpy = jest .spyOn(service['logger'], 'error') .mockImplementation(); // 第3階層のアカウント作成 const { account: tier3Account, admin: tier3Admin } = await makeTestAccount( source, { tier: 3 }, ); // 第4階層のアカウント作成 const { account: tier4Account, admin: tier4Admin } = await makeTestAccount( source, { parent_account_id: tier3Account.id, tier: 4, }, { role: USER_ROLES.AUTHOR, author_id: 'AUTHOR_ID', }, ); const typist = await makeTestUser(source, { account_id: tier4Account.id, role: USER_ROLES.TYPIST, }); const context = makeContext(tier3Admin.external_id, 'requestId'); // ライセンス await createLicenseOrder( source, tier4Account.id, tier3Account.id, 100, 'PO001', ); await createLicenseSetExpiryDateAndStatus( source, tier4Account.id, null, LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); // ワークタイプ await createWorktype(source, tier4Account.id, 'worktype1'); // タスク await createTask( source, tier4Account.id, tier4Admin.id, tier4Admin.author_id ?? '', '', '00', '00000001', TASK_STATUS.UPLOADED, ); // ユーザーグループ await createUserGroup(source, tier4Account.id, 'usergroup1', [typist.id]); // テンプレートファイル await createTemplateFile(source, tier4Account.id, 'template1', 'url'); overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, getUsers: async (context, externalIds) => externalIds.map((externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }), deleteUsers: () => { throw new Error('deleteUsers failed'); }, }); // blobstorageコンテナの削除成功 overrideBlobstorageService(service, { deleteContainer: jest.fn(), }); // 作成したデータを確認 { const tier4AccountRecord = await getAccount(source, tier4Account.id); expect(tier4AccountRecord?.id).toBe(tier4Account.id); expect(tier4AccountRecord?.tier).toBe(4); const userRecord = await getUsers(source); expect(userRecord.length).toBe(3); const licenseRecord = await getLicenses(source, tier4Account.id); expect(licenseRecord.length).toBe(1); const licenseOrderRecord = await source.manager.find(LicenseOrder, { where: { from_account_id: tier4Account.id }, }); expect(licenseOrderRecord.length).toBe(1); const worktypeRecord = await getWorktypes(source, tier4Account.id); expect(worktypeRecord.length).toBe(1); const taskRecord = await getTasks(source, tier4Account.id); expect(taskRecord.length).toBe(1); const userGroupRecord = await getTypistGroup(source, tier4Account.id); expect(userGroupRecord.length).toBe(1); const templateFileRecord = await getTemplateFiles( source, tier4Account.id, ); expect(templateFileRecord.length).toBe(1); const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); expect(tier4AccountJobNumber?.job_number).toBe('00000000'); expect(tier4AccountJobNumber?.account_id).toBe(tier4Account.id); const tier4AccountAdminSortCriteria = await getSortCriteria( source, tier4Admin?.id ?? 0, ); expect(tier4AccountAdminSortCriteria?.direction).toBe('ASC'); expect(tier4AccountAdminSortCriteria?.parameter).toBe('JOB_NUMBER'); const tier4AccountTypistSortCriteria = await getSortCriteria( source, typist.id, ); expect(tier4AccountTypistSortCriteria?.direction).toBe('ASC'); expect(tier4AccountTypistSortCriteria?.parameter).toBe('FILE_LENGTH'); const tier4AccountAdminTaskFilter = await getTaskFilter(source,tier4Admin?.id ?? 0); expect(tier4AccountAdminTaskFilter?.user_id).toBe(tier4Admin?.id); expect(tier4AccountAdminTaskFilter?.author_id).toBe(null); expect(tier4AccountAdminTaskFilter?.file_name).toBe(null); const tier4AccountTypisttaskFilter = await getTaskFilter(source, typist.id); expect(tier4AccountTypisttaskFilter?.user_id).toBe(typist.id); expect(tier4AccountTypisttaskFilter?.author_id).toBe(null); expect(tier4AccountTypisttaskFilter?.file_name).toBe(null); } // パートナーアカウント情報の削除 await service.deletePartnerAccount( context, tier3Admin.external_id, tier4Account.id, ); // loggerSpyがスパイしているlogger.logメソッドが出力したログを確認(目視確認用) const logs = loggerSpy.mock.calls.map((call) => call[0]); console.log(logs); // DB内が想定通りになっているか確認 { // パートナーアカウントが削除されていること const account4Record = await getAccount(source, tier4Account.id); expect(account4Record).toBe(null); const userRecordA = await getUser(source, tier4Admin?.id ?? 0); expect(userRecordA).toBe(null); // パートナーアカウントのユーザーが削除されていること const userRecord = await getUsers(source); expect( userRecord.filter((x) => x.account_id === tier4Account.id).length, ).toBe(0); // パートナーアカウントのJobNumberが削除されていること const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); expect(tier4AccountJobNumber).toBe(null); // パートナーアカウントのソート条件が削除されていること const tier4AccountAdminSortCriteria = await getSortCriteria( source, tier4Admin?.id ?? 0, ); expect(tier4AccountAdminSortCriteria).toBe(null); const tier4AccountTypistSortCriteria = await getSortCriteria( source, typist.id, ); expect(tier4AccountTypistSortCriteria).toBe(null); // パートナーアカウントのTask Filter条件が削除されていること const tier4AccountAdminTaskFilter = await getTaskFilter(source, tier4Admin?.id ?? 0); expect(tier4AccountAdminTaskFilter).toBe(null); const tier4AccountTypisttaskFilter = await getTaskFilter( source, typist.id); expect(tier4AccountTypisttaskFilter).toBe(null); // パートナーアカウントのライセンスが削除されていること const licenseRecord = await source.manager.find(License, { where: { account_id: tier4Account.id }, }); expect(licenseRecord.length).toBe(0); // パートナーアカウントのライセンス注文履歴が削除されていること const licenseOrderRecord = await source.manager.find(LicenseOrder, { where: { from_account_id: tier4Account.id }, }); expect(licenseOrderRecord.length).toBe(0); // パートナーアカウントのライセンス割り当て履歴が削除されていること const LicenseAllocationHistoryRecord = await source.manager.find( LicenseAllocationHistory, { where: { account_id: tier4Account.id } }, ); expect(LicenseAllocationHistoryRecord.length).toBe(0); // パートナーアカウントのワークタイプが削除されていること const worktypeRecord = await getWorktypes(source, tier4Account.id); expect(worktypeRecord.length).toBe(0); // パートナーアカウントのタスクが削除されていること const taskRecord = await getTasks(source, tier4Account.id); expect(taskRecord.length).toBe(0); // パートナーアカウントのユーザーグループが削除されていること const userGroupRecord = await getTypistGroup(source, tier4Account.id); expect(userGroupRecord.length).toBe(0); // パートナーアカウントのテンプレートファイルが削除されていること const templateFileRecord = await getTemplateFiles( source, tier4Account.id, ); expect(templateFileRecord.length).toBe(0); // パートナーアカウント削除完了通知が送信されていること expect(_subject).toBe('Partner Account Deleted Notification [U-123]'); // 手動復旧が必要なエラーログが出力されていること expect(logs.some((x) => x.startsWith(MANUAL_RECOVERY_REQUIRED))).toBe( true, ); } }); it('Blobコンテナの削除失敗時は、MANUAL_RECOVERY_REQUIREDを出して処理続行すること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); let _subject = ''; overrideSendgridService(service, { sendMail: async ( context: Context, to: string[], cc: string[], from: string, subject: string, text: string, html: string, ) => { _subject = subject; }, }); const loggerSpy = jest .spyOn(service['logger'], 'error') .mockImplementation(); // 第3階層のアカウント作成 const { account: tier3Account, admin: tier3Admin } = await makeTestAccount( source, { tier: 3 }, ); // 第4階層のアカウント作成 const { account: tier4Account, admin: tier4Admin } = await makeTestAccount( source, { parent_account_id: tier3Account.id, tier: 4, }, { role: USER_ROLES.AUTHOR, author_id: 'AUTHOR_ID', }, ); const typist = await makeTestUser(source, { account_id: tier4Account.id, role: USER_ROLES.TYPIST, }); const context = makeContext(tier3Admin.external_id, 'requestId'); // ライセンス await createLicenseOrder( source, tier4Account.id, tier3Account.id, 100, 'PO001', ); await createLicenseSetExpiryDateAndStatus( source, tier4Account.id, null, LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); // ワークタイプ await createWorktype(source, tier4Account.id, 'worktype1'); // タスク await createTask( source, tier4Account.id, tier4Admin.id, tier4Admin.author_id ?? '', '', '00', '00000001', TASK_STATUS.UPLOADED, ); // ユーザーグループ await createUserGroup(source, tier4Account.id, 'usergroup1', [typist.id]); // テンプレートファイル await createTemplateFile(source, tier4Account.id, 'template1', 'url'); overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, getUsers: async (context, externalIds) => externalIds.map((externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }), deleteUsers: jest.fn(), }); // blobstorageコンテナの削除成功 overrideBlobstorageService(service, { deleteContainer: () => { throw new Error('deleteContainer failed'); }, }); // 作成したデータを確認 { const tier4AccountRecord = await getAccount(source, tier4Account.id); expect(tier4AccountRecord?.id).toBe(tier4Account.id); expect(tier4AccountRecord?.tier).toBe(4); const userRecord = await getUsers(source); expect(userRecord.length).toBe(3); const licenseRecord = await getLicenses(source, tier4Account.id); expect(licenseRecord.length).toBe(1); const licenseOrderRecord = await source.manager.find(LicenseOrder, { where: { from_account_id: tier4Account.id }, }); expect(licenseOrderRecord.length).toBe(1); const worktypeRecord = await getWorktypes(source, tier4Account.id); expect(worktypeRecord.length).toBe(1); const taskRecord = await getTasks(source, tier4Account.id); expect(taskRecord.length).toBe(1); const userGroupRecord = await getTypistGroup(source, tier4Account.id); expect(userGroupRecord.length).toBe(1); const templateFileRecord = await getTemplateFiles( source, tier4Account.id, ); expect(templateFileRecord.length).toBe(1); const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); expect(tier4AccountJobNumber?.job_number).toBe('00000000'); expect(tier4AccountJobNumber?.account_id).toBe(tier4Account.id); const tier4AccountAdminSortCriteria = await getSortCriteria( source, tier4Admin?.id ?? 0, ); expect(tier4AccountAdminSortCriteria?.direction).toBe('ASC'); expect(tier4AccountAdminSortCriteria?.parameter).toBe('JOB_NUMBER'); const tier4AccountTypistSortCriteria = await getSortCriteria( source, typist.id, ); expect(tier4AccountTypistSortCriteria?.direction).toBe('ASC'); expect(tier4AccountTypistSortCriteria?.parameter).toBe('FILE_LENGTH'); const tier4AccountAdminTaskFilter = await getTaskFilter(source, tier4Admin?.id ?? 0); expect(tier4AccountAdminTaskFilter?.user_id).toBe(tier4Admin?.id); expect(tier4AccountAdminTaskFilter?.author_id).toBe(null); expect(tier4AccountAdminTaskFilter?.file_name).toBe(null); const tier4AccountTypisttaskFilter = await getTaskFilter(source, typist.id); expect(tier4AccountTypisttaskFilter?.user_id).toBe(typist.id); expect(tier4AccountTypisttaskFilter?.author_id).toBe(null); expect(tier4AccountTypisttaskFilter?.file_name).toBe(null); } // パートナーアカウント情報の削除 await service.deletePartnerAccount( context, tier3Admin.external_id, tier4Account.id, ); // loggerSpyがスパイしているlogger.logメソッドが出力したログを確認(目視確認用) const logs = loggerSpy.mock.calls.map((call) => call[0]); console.log(logs); // DB内が想定通りになっているか確認 { // パートナーアカウントが削除されていること const account4Record = await getAccount(source, tier4Account.id); expect(account4Record).toBe(null); const userRecordA = await getUser(source, tier4Admin?.id ?? 0); expect(userRecordA).toBe(null); // パートナーアカウントのユーザーが削除されていること const userRecord = await getUsers(source); expect( userRecord.filter((x) => x.account_id === tier4Account.id).length, ).toBe(0); // パートナーアカウントのJobNumberが削除されていること const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); expect(tier4AccountJobNumber).toBe(null); // パートナーアカウントのソート条件が削除されていること const tier4AccountAdminSortCriteria = await getSortCriteria( source, tier4Admin?.id ?? 0, ); expect(tier4AccountAdminSortCriteria).toBe(null); const tier4AccountTypistSortCriteria = await getSortCriteria( source, typist.id, ); expect(tier4AccountTypistSortCriteria).toBe(null); // パートナーアカウントのTask Filter条件が削除されていること const tier4AccountAdminTaskFilter = await getTaskFilter(source, tier4Admin?.id ?? 0); expect(tier4AccountAdminTaskFilter).toBe(null); const tier4AccountTypisttaskFilter = await getTaskFilter(source, typist.id); expect(tier4AccountTypisttaskFilter).toBe(null); // パートナーアカウントのライセンスが削除されていること const licenseRecord = await source.manager.find(License, { where: { account_id: tier4Account.id }, }); expect(licenseRecord.length).toBe(0); // パートナーアカウントのライセンス注文履歴が削除されていること const licenseOrderRecord = await source.manager.find(LicenseOrder, { where: { from_account_id: tier4Account.id }, }); expect(licenseOrderRecord.length).toBe(0); // パートナーアカウントのライセンス割り当て履歴が削除されていること const LicenseAllocationHistoryRecord = await source.manager.find( LicenseAllocationHistory, { where: { account_id: tier4Account.id } }, ); expect(LicenseAllocationHistoryRecord.length).toBe(0); // パートナーアカウントのワークタイプが削除されていること const worktypeRecord = await getWorktypes(source, tier4Account.id); expect(worktypeRecord.length).toBe(0); // パートナーアカウントのタスクが削除されていること const taskRecord = await getTasks(source, tier4Account.id); expect(taskRecord.length).toBe(0); // パートナーアカウントのユーザーグループが削除されていること const userGroupRecord = await getTypistGroup(source, tier4Account.id); expect(userGroupRecord.length).toBe(0); // パートナーアカウントのテンプレートファイルが削除されていること const templateFileRecord = await getTemplateFiles( source, tier4Account.id, ); expect(templateFileRecord.length).toBe(0); // パートナーアカウント削除完了通知が送信されていること expect(_subject).toBe('Partner Account Deleted Notification [U-123]'); // 手動復旧が必要なエラーログが出力されていること expect(logs.some((x) => x.startsWith(MANUAL_RECOVERY_REQUIRED))).toBe( true, ); } }); it('メール送信失敗時でも処理続行すること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); overrideSendgridService(service, { sendMail: async () => { throw new Error('sendMail failed'); }, }); // 第3階層のアカウント作成 const { account: tier3Account, admin: tier3Admin } = await makeTestAccount( source, { tier: 3 }, ); // 第4階層のアカウント作成 const { account: tier4Account, admin: tier4Admin } = await makeTestAccount( source, { parent_account_id: tier3Account.id, tier: 4, }, { role: USER_ROLES.AUTHOR, author_id: 'AUTHOR_ID', }, ); const typist = await makeTestUser(source, { account_id: tier4Account.id, role: USER_ROLES.TYPIST, }); const context = makeContext(tier3Admin.external_id, 'requestId'); // ライセンス await createLicenseOrder( source, tier4Account.id, tier3Account.id, 100, 'PO001', ); await createLicenseSetExpiryDateAndStatus( source, tier4Account.id, null, LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); // ワークタイプ await createWorktype(source, tier4Account.id, 'worktype1'); // タスク await createTask( source, tier4Account.id, tier4Admin.id, tier4Admin.author_id ?? '', '', '00', '00000001', TASK_STATUS.UPLOADED, ); // ユーザーグループ await createUserGroup(source, tier4Account.id, 'usergroup1', [typist.id]); // テンプレートファイル await createTemplateFile(source, tier4Account.id, 'template1', 'url'); overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, getUsers: async (context, externalIds) => externalIds.map((externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }), deleteUsers: jest.fn(), }); // blobstorageコンテナの削除成功 overrideBlobstorageService(service, { deleteContainer: jest.fn(), }); // 作成したデータを確認 { const tier4AccountRecord = await getAccount(source, tier4Account.id); expect(tier4AccountRecord?.id).toBe(tier4Account.id); expect(tier4AccountRecord?.tier).toBe(4); const userRecord = await getUsers(source); expect(userRecord.length).toBe(3); const licenseRecord = await getLicenses(source, tier4Account.id); expect(licenseRecord.length).toBe(1); const licenseOrderRecord = await source.manager.find(LicenseOrder, { where: { from_account_id: tier4Account.id }, }); expect(licenseOrderRecord.length).toBe(1); const worktypeRecord = await getWorktypes(source, tier4Account.id); expect(worktypeRecord.length).toBe(1); const taskRecord = await getTasks(source, tier4Account.id); expect(taskRecord.length).toBe(1); const userGroupRecord = await getTypistGroup(source, tier4Account.id); expect(userGroupRecord.length).toBe(1); const templateFileRecord = await getTemplateFiles( source, tier4Account.id, ); expect(templateFileRecord.length).toBe(1); const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); expect(tier4AccountJobNumber?.job_number).toBe('00000000'); expect(tier4AccountJobNumber?.account_id).toBe(tier4Account.id); const tier4AccountAdminSortCriteria = await getSortCriteria( source, tier4Admin?.id ?? 0, ); expect(tier4AccountAdminSortCriteria?.direction).toBe('ASC'); expect(tier4AccountAdminSortCriteria?.parameter).toBe('JOB_NUMBER'); const tier4AccountTypistSortCriteria = await getSortCriteria( source, typist.id, ); expect(tier4AccountTypistSortCriteria?.direction).toBe('ASC'); expect(tier4AccountTypistSortCriteria?.parameter).toBe('FILE_LENGTH'); const tier4AccountAdminTaskFilter = await getTaskFilter( source, tier4Admin?.id ?? 0, ); expect(tier4AccountAdminTaskFilter?.user_id).toBe(tier4Admin?.id); expect(tier4AccountAdminTaskFilter?.author_id).toBe(null); expect(tier4AccountAdminTaskFilter?.file_name).toBe(null); const tier4AccountTypisttaskFilter = await getTaskFilter( source, typist.id, ); expect(tier4AccountTypisttaskFilter?.user_id).toBe(typist.id); expect(tier4AccountTypisttaskFilter?.author_id).toBe(null); expect(tier4AccountTypisttaskFilter?.file_name).toBe(null); } // パートナーアカウント情報の削除 await service.deletePartnerAccount( context, tier3Admin.external_id, tier4Account.id, ); // DB内が想定通りになっているか確認 { // パートナーアカウントが削除されていること const account4Record = await getAccount(source, tier4Account.id); expect(account4Record).toBe(null); const userRecordA = await getUser(source, tier4Admin?.id ?? 0); expect(userRecordA).toBe(null); // パートナーアカウントのユーザーが削除されていること const userRecord = await getUsers(source); expect( userRecord.filter((x) => x.account_id === tier4Account.id).length, ).toBe(0); // パートナーアカウントのJobNumberが削除されていること const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); expect(tier4AccountJobNumber).toBe(null); // パートナーアカウントのソート条件が削除されていること const tier4AccountAdminSortCriteria = await getSortCriteria( source, tier4Admin?.id ?? 0, ); expect(tier4AccountAdminSortCriteria).toBe(null); const tier4AccountTypistSortCriteria = await getSortCriteria( source, typist.id, ); expect(tier4AccountTypistSortCriteria).toBe(null); // パートナーアカウントのTask Filter条件が削除されていること const tier4AccountAdminTaskFilter = await getTaskFilter(source, tier4Admin?.id ?? 0); expect(tier4AccountAdminTaskFilter).toBe(null); const tier4AccountTypisttaskFilter = await getTaskFilter(source, typist.id); expect(tier4AccountTypisttaskFilter).toBe(null); // パートナーアカウントのライセンスが削除されていること const licenseRecord = await source.manager.find(License, { where: { account_id: tier4Account.id }, }); expect(licenseRecord.length).toBe(0); // パートナーアカウントのライセンス注文履歴が削除されていること const licenseOrderRecord = await source.manager.find(LicenseOrder, { where: { from_account_id: tier4Account.id }, }); expect(licenseOrderRecord.length).toBe(0); // パートナーアカウントのライセンス割り当て履歴が削除されていること const LicenseAllocationHistoryRecord = await source.manager.find( LicenseAllocationHistory, { where: { account_id: tier4Account.id } }, ); expect(LicenseAllocationHistoryRecord.length).toBe(0); // パートナーアカウントのワークタイプが削除されていること const worktypeRecord = await getWorktypes(source, tier4Account.id); expect(worktypeRecord.length).toBe(0); // パートナーアカウントのタスクが削除されていること const taskRecord = await getTasks(source, tier4Account.id); expect(taskRecord.length).toBe(0); // パートナーアカウントのユーザーグループが削除されていること const userGroupRecord = await getTypistGroup(source, tier4Account.id); expect(userGroupRecord.length).toBe(0); // パートナーアカウントのテンプレートファイルが削除されていること const templateFileRecord = await getTemplateFiles( source, tier4Account.id, ); expect(templateFileRecord.length).toBe(0); } }); }); describe('getPartnerUsers', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('パートナーアカウント情報が取得できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // 第3階層のアカウント作成 const { account: tier3Account, admin: tier3Admin } = await makeTestAccount( source, { tier: 3 }, ); // 第4階層のアカウント作成 const { account: tier4Account, admin: tier4Admin } = await makeTestAccount( source, { parent_account_id: tier3Account.id, tier: 4, }, { role: USER_ROLES.AUTHOR, author_id: 'AUTHOR_ID', }, ); const typist = await makeTestUser(source, { account_id: tier4Account.id, role: USER_ROLES.TYPIST, }); const context = makeContext(tier3Admin.external_id, 'requestId'); overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, getUsers: async (context, externalIds) => externalIds.map((externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }), deleteUsers: jest.fn(), }); // パートナーアカウント情報の取得 const partnerUsers = await service.getPartnerUsers( context, tier3Admin.external_id, tier4Account.id, ); expect(partnerUsers).toEqual([ { id: tier4Admin.id, name: 'adb2c' + tier4Admin.external_id, email: 'mail@example.com', isPrimaryAdmin: true, }, { id: typist.id, name: 'adb2c' + typist.external_id, email: 'mail@example.com', isPrimaryAdmin: false, }, ]); }); it('パートナーアカウントの親が実行者でない場合、エラーとなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // 第3階層のアカウント作成 const { admin: tier3Admin } = await makeTestAccount(source, { tier: 3 }); const { account: tier3Parent } = await makeTestAccount(source, { tier: 3 }); // 第4階層のアカウント作成 const { account: tier4Account } = await makeTestAccount( source, { parent_account_id: tier3Parent.id, tier: 4, }, { role: USER_ROLES.AUTHOR, author_id: 'AUTHOR_ID', }, ); const context = makeContext(tier3Admin.external_id, 'requestId'); overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, getUsers: async (context, externalIds) => externalIds.map((externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }), deleteUsers: jest.fn(), }); try { // パートナーアカウント情報の取得 await service.getPartnerUsers( context, tier3Admin.external_id, tier4Account.id, ); fail(); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E019001')); } else { fail(); } } }); }); describe('updatePartnerInfo', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('パートナーアカウントの会社名を変更できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 新規親アカウントのアカウントを作成する const { account: parent, admin: parentAdmin } = await makeTestAccount( source, { tier: 3 }, { external_id: 'parent_external_id' }, ); // 子アカウントを作成する const { account: partner, admin: partnerAdmin } = await makeTestAccount( source, { tier: 4, parent_account_id: parent.id, company_name: 'oldCompanyName', }, ); // 作成したデータを確認 { const partnerRecord = await getAccount(source, partner.id); expect(partnerRecord?.company_name).toBe('oldCompanyName'); } const service = module.get(AccountsService); let _subject = ''; overrideSendgridService(service, { sendMail: async ( context: Context, to: string[], cc: string[], from: string, subject: string, text: string, html: string, ) => { _subject = subject; }, }); overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, getUsers: async (context, externalIds) => externalIds.map((externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }), }); // テスト実行 const context = makeContext(parentAdmin.external_id, 'requestId'); await service.updatePartnerInfo( context, parentAdmin.external_id, partner.id, partnerAdmin.id, 'newCompanyName', ); { // DB内が想定通りになっているか確認 const partnerRecord = await getAccount(source, partner.id); expect(partnerRecord?.company_name).toBe('newCompanyName'); // パートナーアカウント情報変更完了通知が送信されていること expect(_subject).toBe('Partner Account Edit Notification [U-124]'); } }); it('パートナーアカウントのプライマリ管理者を変更できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 新規親アカウントのアカウントを作成する const { account: parent, admin: parentAdmin } = await makeTestAccount( source, { tier: 3 }, { external_id: 'parent_external_id' }, ); // 子アカウントを作成する const { account: partner, admin: partnerAdmin } = await makeTestAccount( source, { tier: 4, parent_account_id: parent.id, }, ); const newPartnerAdmin = await makeTestUser(source, { account_id: partner.id, }); // 作成したデータを確認 { const partnerRecord = await getAccount(source, partner.id); expect(partnerRecord?.primary_admin_user_id).toBe(partnerAdmin.id); } const service = module.get(AccountsService); let _subject = ''; overrideSendgridService(service, { sendMail: async ( context: Context, to: string[], cc: string[], from: string, subject: string, text: string, html: string, ) => { _subject = subject; }, }); overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, getUsers: async (context, externalIds) => externalIds.map((externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }), }); // テスト実行 const context = makeContext(parentAdmin.external_id, 'requestId'); await service.updatePartnerInfo( context, parentAdmin.external_id, partner.id, newPartnerAdmin.id, partner.company_name, ); { // DB内が想定通りになっているか確認 const partnerRecord = await getAccount(source, partner.id); expect(partnerRecord?.primary_admin_user_id).toBe(newPartnerAdmin.id); // パートナーアカウント情報変更完了通知が送信されていること expect(_subject).toBe('Partner Account Edit Notification [U-124]'); } }); it('変更対象アカウントが実行者のパートナーアカウントでない場合、エラーなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 新規親アカウントのアカウントを作成する const { admin: parentAdmin } = await makeTestAccount( source, { tier: 3 }, { external_id: 'parent_external_id' }, ); // 子アカウントを作成する const { account: partner, admin: partnerAdmin } = await makeTestAccount( source, { tier: 4 }, ); // 作成したデータを確認 { const partnerRecord = await getAccount(source, partner.id); expect(partnerRecord?.primary_admin_user_id).toBe(partnerAdmin.id); } const service = module.get(AccountsService); overrideSendgridService(service, { sendMail: async ( context: Context, to: string[], cc: string[], from: string, subject: string, text: string, html: string, ) => { // empty }, }); overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, getUsers: async (context, externalIds) => externalIds.map((externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }), }); // テスト実行 const context = makeContext(parentAdmin.external_id, 'requestId'); try { await service.updatePartnerInfo( context, parentAdmin.external_id, partner.id, partnerAdmin.id, partner.company_name, ); fail(); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E020001')); } else { fail(); } } }); it('DBアクセスがエラーの場合、エラーなること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 新規親アカウントのアカウントを作成する const { account: parent, admin: parentAdmin } = await makeTestAccount( source, { tier: 3 }, { external_id: 'parent_external_id' }, ); // 子アカウントを作成する const { account: partner, admin: partnerAdmin } = await makeTestAccount( source, { tier: 4, parent_account_id: parent.id }, ); // 作成したデータを確認 { const partnerRecord = await getAccount(source, partner.id); expect(partnerRecord?.primary_admin_user_id).toBe(partnerAdmin.id); } const service = module.get(AccountsService); overrideSendgridService(service, { sendMail: async ( context: Context, to: string[], cc: string[], from: string, subject: string, text: string, html: string, ) => { // empty }, }); overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, getUsers: async (context, externalIds) => externalIds.map((externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }), }); //DBアクセスに失敗するようにする const accountsRepositoryService = module.get( AccountsRepositoryService, ); accountsRepositoryService.updatePartnerInfo = jest .fn() .mockRejectedValue('DB failed'); // テスト実行 const context = makeContext(parentAdmin.external_id, 'requestId'); try { await service.updatePartnerInfo( context, parentAdmin.external_id, partner.id, partnerAdmin.id, partner.company_name, ); fail(); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); it('メール送信に失敗した場合でも、エラーとならず成功となること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); // 新規親アカウントのアカウントを作成する const { account: parent, admin: parentAdmin } = await makeTestAccount( source, { tier: 3 }, { external_id: 'parent_external_id' }, ); // 子アカウントを作成する const { account: partner, admin: partnerAdmin } = await makeTestAccount( source, { tier: 4, parent_account_id: parent.id, company_name: 'oldCompanyName' }, ); // 作成したデータを確認 { const partnerRecord = await getAccount(source, partner.id); expect(partnerRecord?.company_name).toBe('oldCompanyName'); } const service = module.get(AccountsService); overrideSendgridService(service, { sendMail: async ( context: Context, to: string[], cc: string[], from: string, subject: string, text: string, html: string, ) => { throw new Error('sendMail failed'); }, }); overrideAdB2cService(service, { getUser: async (context, externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }, getUsers: async (context, externalIds) => externalIds.map((externalId) => { return { displayName: 'adb2c' + externalId, id: externalId, identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'xxxxxx', issuerAssignedId: 'mail@example.com', }, ], }; }), }); // テスト実行 const context = makeContext(parentAdmin.external_id, 'requestId'); await service.updatePartnerInfo( context, parentAdmin.external_id, partner.id, partnerAdmin.id, 'newCompanyName', ); { // DB内が想定通りになっているか確認 const partnerRecord = await getAccount(source, partner.id); expect(partnerRecord?.company_name).toBe('newCompanyName'); } }); }); describe('searchPartners', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('各階層がCompanyNameで検索時、自身の配下のアカウントが部分一致検索できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const tier4Account = await makeTestAccount(source, { parent_account_id: tier3Account.account.id, tier: 4, company_name: 'UnitTestCompany4', }); const tier5Account = await makeTestAccount(source, { parent_account_id: tier4Account.account.id, tier: 5, company_name: 'UnitTestCompany5', }); const adb2cReturn = [ { id: tier2Account.admin.external_id, displayName: 'partner2', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner2@example.com', }, ], }, { id: tier3Account.admin.external_id, displayName: 'partner3', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner3@example.com', }, ], }, { id: tier4Account.admin.external_id, displayName: 'partner4', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner4@example.com', }, ], }, { id: tier5Account.admin.external_id, displayName: 'partner5', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner5@example.com', }, ], }, ] as AdB2cUser[]; overrideAdB2cService(service, { getUsers: async (_context: Context, _externalIds: string[]) => { return adb2cReturn; }, }); // 第一が名称で検索 const t1Partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier1Account.admin.external_id, tier1Account.account.tier, 'Test', ); // 取得件数が正しいこと expect(t1Partners.searchResult.length).toBe(4); // 名称がヒットしても自分自身は検索できないこと expect( t1Partners.searchResult.find( (partner) => partner.accountId === tier1Account.account.id, ), ).toBeUndefined(); // 階層の昇順で取得できていること expect(t1Partners.searchResult[0].name).toBe( tier2Account.account.company_name, ); expect(t1Partners.searchResult[1].name).toBe( tier3Account.account.company_name, ); expect(t1Partners.searchResult[2].name).toBe( tier4Account.account.company_name, ); expect(t1Partners.searchResult[3].name).toBe( tier5Account.account.company_name, ); expect(t1Partners.searchResult[0].email).toBe('partner2@example.com'); expect(t1Partners.searchResult[1].email).toBe('partner3@example.com'); expect(t1Partners.searchResult[2].email).toBe('partner4@example.com'); expect(t1Partners.searchResult[3].email).toBe('partner5@example.com'); // 1件目のみすべてのプロパティを検査 expect(t1Partners.searchResult[0].tier).toBe(tier2Account.account.tier); expect(t1Partners.searchResult[0].country).toBe( tier2Account.account.country, ); expect(t1Partners.searchResult[0].accountId).toBe(tier2Account.account.id); expect(t1Partners.searchResult[0].primaryAdmin).toBe('partner2'); // 第二が名称で検索 const t2Partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier2Account.admin.external_id, tier2Account.account.tier, 'Test', ); // 取得件数が正しいこと expect(t2Partners.searchResult.length).toBe(3); // 名称がヒットしても自分自身は検索できないこと expect( t2Partners.searchResult.find( (partner) => partner.accountId === tier2Account.account.id, ), ).toBeUndefined(); // 階層の昇順で取得できていること expect(t2Partners.searchResult[0].name).toBe( tier3Account.account.company_name, ); expect(t2Partners.searchResult[1].name).toBe( tier4Account.account.company_name, ); expect(t2Partners.searchResult[2].name).toBe( tier5Account.account.company_name, ); // 第三が名称で検索 const t3Partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier3Account.admin.external_id, tier3Account.account.tier, 'Test', ); // 取得件数が正しいこと expect(t3Partners.searchResult.length).toBe(2); // 名称がヒットしても自分自身は検索できないこと expect( t3Partners.searchResult.find( (partner) => partner.accountId === tier3Account.account.id, ), ).toBeUndefined(); // 階層の昇順で取得できていること expect(t3Partners.searchResult[0].name).toBe( tier4Account.account.company_name, ); expect(t3Partners.searchResult[1].name).toBe( tier5Account.account.company_name, ); // 第四が名称で検索 const t4Partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier4Account.admin.external_id, tier4Account.account.tier, 'Test', ); // 取得件数が正しいこと expect(t4Partners.searchResult.length).toBe(1); // 名称がヒットしても自分自身は検索できないこと expect( t4Partners.searchResult.find( (partner) => partner.accountId === tier3Account.account.id, ), ).toBeUndefined(); // 階層の昇順で取得できていること expect(t4Partners.searchResult[0].name).toBe( tier5Account.account.company_name, ); }); it('第一階層が第二〜第五のAccountIdで検索時、それぞれ1アカウントのみが検索できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const tier4Account = await makeTestAccount(source, { parent_account_id: tier3Account.account.id, tier: 4, company_name: 'UnitTestCompany4', }); const tier5Account = await makeTestAccount(source, { parent_account_id: tier4Account.account.id, tier: 5, company_name: 'UnitTestCompany5', }); const adb2cReturn = [ { id: tier2Account.admin.external_id, displayName: 'partner2', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner2@example.com', }, ], }, { id: tier3Account.admin.external_id, displayName: 'partner3', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner3@example.com', }, ], }, { id: tier4Account.admin.external_id, displayName: 'partner4', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner4@example.com', }, ], }, { id: tier5Account.admin.external_id, displayName: 'partner5', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner5@example.com', }, ], }, ] as AdB2cUser[]; overrideAdB2cService(service, { getUsers: async (_context: Context, _externalIds: string[]) => { return adb2cReturn; }, }); // 第一階層のIdで検索 const tier1Partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier1Account.admin.external_id, tier1Account.account.tier, undefined, tier1Account.account.id, ); // 検索者と同じアカウント階層は検索されないこと expect(tier1Partners.searchResult.length).toBe(0); // Id指定で1件のみ取得できていることを第二〜第五で確認 // 第二階層のIdで検索 const tier2Partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier1Account.admin.external_id, tier1Account.account.tier, undefined, tier2Account.account.id, ); expect(tier2Partners.searchResult.length).toBe(1); expect(tier2Partners.searchResult[0].name).toBe( tier2Account.account.company_name, ); // 第二階層のみ詳しく値を検査 expect(tier2Partners.searchResult[0].email).toBe('partner2@example.com'); expect(tier2Partners.searchResult[0].tier).toBe(tier2Account.account.tier); expect(tier2Partners.searchResult[0].country).toBe( tier2Account.account.country, ); expect(tier2Partners.searchResult[0].accountId).toBe( tier2Account.account.id, ); expect(tier2Partners.searchResult[0].primaryAdmin).toBe('partner2'); // 第三階層のIdで検索 const tier3partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier1Account.admin.external_id, tier1Account.account.tier, undefined, tier3Account.account.id, ); expect(tier3partners.searchResult.length).toBe(1); expect(tier3partners.searchResult[0].name).toBe( tier3Account.account.company_name, ); // 第四階層のIdで検索 const tier4partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier1Account.admin.external_id, tier1Account.account.tier, undefined, tier4Account.account.id, ); expect(tier4partners.searchResult.length).toBe(1); expect(tier4partners.searchResult[0].name).toBe( tier4Account.account.company_name, ); // 第五階層のIdで検索 const tier5partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier1Account.admin.external_id, tier1Account.account.tier, undefined, tier5Account.account.id, ); expect(tier5partners.searchResult.length).toBe(1); expect(tier5partners.searchResult[0].name).toBe( tier5Account.account.company_name, ); }); it('第二階層が第三〜第五のAccountIdで検索時、それぞれ1アカウントのみが検索できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const tier4Account = await makeTestAccount(source, { parent_account_id: tier3Account.account.id, tier: 4, company_name: 'UnitTestCompany4', }); const tier5Account = await makeTestAccount(source, { parent_account_id: tier4Account.account.id, tier: 5, company_name: 'UnitTestCompany5', }); const adb2cReturn = [ { id: tier2Account.admin.external_id, displayName: 'partner2', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner2@example.com', }, ], }, { id: tier3Account.admin.external_id, displayName: 'partner3', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner3@example.com', }, ], }, { id: tier4Account.admin.external_id, displayName: 'partner4', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner4@example.com', }, ], }, { id: tier5Account.admin.external_id, displayName: 'partner5', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner5@example.com', }, ], }, ] as AdB2cUser[]; overrideAdB2cService(service, { getUsers: async (_context: Context, _externalIds: string[]) => { return adb2cReturn; }, }); // 第二階層のIdで検索 const tier2Partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier2Account.admin.external_id, tier2Account.account.tier, undefined, tier2Account.account.id, ); // 検索者と同じアカウント階層は検索されないこと expect(tier2Partners.searchResult.length).toBe(0); // Id指定で1件のみ取得できていることを第三〜第五で確認 // 第三階層のIdで検索 const tier3partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier2Account.admin.external_id, tier2Account.account.tier, undefined, tier3Account.account.id, ); expect(tier3partners.searchResult.length).toBe(1); expect(tier3partners.searchResult[0].name).toBe( tier3Account.account.company_name, ); // 第四階層のIdで検索 const tier4partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier2Account.admin.external_id, tier2Account.account.tier, undefined, tier4Account.account.id, ); expect(tier4partners.searchResult.length).toBe(1); expect(tier4partners.searchResult[0].name).toBe( tier4Account.account.company_name, ); // 第五階層のIdで検索 const tier5partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier2Account.admin.external_id, tier2Account.account.tier, undefined, tier5Account.account.id, ); expect(tier5partners.searchResult.length).toBe(1); expect(tier5partners.searchResult[0].name).toBe( tier5Account.account.company_name, ); }); it('第三階層が第四〜第五のAccountIdで検索時、それぞれ1アカウントのみが検索できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const tier4Account = await makeTestAccount(source, { parent_account_id: tier3Account.account.id, tier: 4, company_name: 'UnitTestCompany4', }); const tier5Account = await makeTestAccount(source, { parent_account_id: tier4Account.account.id, tier: 5, company_name: 'UnitTestCompany5', }); const adb2cReturn = [ { id: tier2Account.admin.external_id, displayName: 'partner2', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner2@example.com', }, ], }, { id: tier3Account.admin.external_id, displayName: 'partner3', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner3@example.com', }, ], }, { id: tier4Account.admin.external_id, displayName: 'partner4', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner4@example.com', }, ], }, { id: tier5Account.admin.external_id, displayName: 'partner5', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner5@example.com', }, ], }, ] as AdB2cUser[]; overrideAdB2cService(service, { getUsers: async (_context: Context, _externalIds: string[]) => { return adb2cReturn; }, }); // 第三階層のIdで検索 const tier3partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier3Account.admin.external_id, tier3Account.account.tier, undefined, tier3Account.account.id, ); // 検索者と同じアカウント階層は検索されないこと expect(tier3partners.searchResult.length).toBe(0); // Id指定で1件のみ取得できていることを第四〜第五で確認 // 第四階層のIdで検索 const tier4partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier3Account.admin.external_id, tier3Account.account.tier, undefined, tier4Account.account.id, ); expect(tier4partners.searchResult.length).toBe(1); expect(tier4partners.searchResult[0].name).toBe( tier4Account.account.company_name, ); // 第五階層のIdで検索 const tier5partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier3Account.admin.external_id, tier3Account.account.tier, undefined, tier5Account.account.id, ); expect(tier5partners.searchResult.length).toBe(1); expect(tier5partners.searchResult[0].name).toBe( tier5Account.account.company_name, ); }); it('第四階層が第五のAccountIdで検索時、1アカウントのみが検索できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const tier4Account = await makeTestAccount(source, { parent_account_id: tier3Account.account.id, tier: 4, company_name: 'UnitTestCompany4', }); const tier5Account = await makeTestAccount(source, { parent_account_id: tier4Account.account.id, tier: 5, company_name: 'UnitTestCompany5', }); const adb2cReturn = [ { id: tier2Account.admin.external_id, displayName: 'partner2', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner2@example.com', }, ], }, { id: tier3Account.admin.external_id, displayName: 'partner3', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner3@example.com', }, ], }, { id: tier4Account.admin.external_id, displayName: 'partner4', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner4@example.com', }, ], }, { id: tier5Account.admin.external_id, displayName: 'partner5', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner5@example.com', }, ], }, ] as AdB2cUser[]; overrideAdB2cService(service, { getUsers: async (_context: Context, _externalIds: string[]) => { return adb2cReturn; }, }); // 第四階層のIdで検索 const tier4partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier4Account.admin.external_id, tier4Account.account.tier, undefined, tier4Account.account.id, ); // 検索者と同じアカウント階層は検索されないこと expect(tier4partners.searchResult.length).toBe(0); // Id指定で1件のみ取得できていることを第四〜第五で確認 // 第五階層のIdで検索 const tier5partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier4Account.admin.external_id, tier4Account.account.tier, undefined, tier5Account.account.id, ); expect(tier5partners.searchResult.length).toBe(1); expect(tier5partners.searchResult[0].name).toBe( tier5Account.account.company_name, ); }); it('CompanyName, AccountIdで検索時、検索できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const tier4Account = await makeTestAccount(source, { parent_account_id: tier3Account.account.id, tier: 4, company_name: 'UnitTestCompany4', }); const tier5Account = await makeTestAccount(source, { parent_account_id: tier4Account.account.id, tier: 5, company_name: 'UnitTestCompany5', }); const adb2cReturn = [ { id: tier2Account.admin.external_id, displayName: 'partner2', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner2@example.com', }, ], }, { id: tier3Account.admin.external_id, displayName: 'partner3', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner3@example.com', }, ], }, { id: tier4Account.admin.external_id, displayName: 'partner4', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner4@example.com', }, ], }, { id: tier5Account.admin.external_id, displayName: 'partner5', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner5@example.com', }, ], }, ] as AdB2cUser[]; overrideAdB2cService(service, { getUsers: async (_context: Context, _externalIds: string[]) => { return adb2cReturn; }, }); // 第一が名称とAccountIdで検索 const t1Partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier1Account.admin.external_id, tier1Account.account.tier, 'Test', tier2Account.account.id ); // 取得件数が正しいこと expect(t1Partners.searchResult.length).toBe(1); expect(t1Partners.searchResult[0].name).toBe( tier2Account.account.company_name, ); }); it('第四階層が複数の第五階層アカウントを検索できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const tier4Account = await makeTestAccount(source, { parent_account_id: tier3Account.account.id, tier: 4, company_name: 'UnitTestCompany4', }); // 第五階層アカウントを複数作成する const tier5AccountNames = ['Alpha', 'Bravo', 'Charlie', 'Delta'] const makeTestAccountPromises = tier5AccountNames.map((name) => { return makeTestAccount(source!, { parent_account_id: tier4Account.account.id, tier: 5, company_name: `UnitTestCompany5_${name}`, }); }) const tier5Accounts = await Promise.all(makeTestAccountPromises); const adb2cReturn = [ { id: tier2Account.admin.external_id, displayName: 'partner2', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner2@example.com', }, ], }, { id: tier3Account.admin.external_id, displayName: 'partner3', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner3@example.com', }, ], }, { id: tier4Account.admin.external_id, displayName: 'partner4', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner4@example.com', }, ], }, { id: tier5Accounts[0].admin.external_id, displayName: 'partner5_1', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner5_1@example.com', }, ], }, { id: tier5Accounts[1].admin.external_id, displayName: 'partner5_2', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner5_2@example.com', }, ], }, { id: tier5Accounts[2].admin.external_id, displayName: 'partner5_3', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner5_3@example.com', }, ], }, { id: tier5Accounts[3].admin.external_id, displayName: 'partner5_4', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner5_4@example.com', }, ], }, ] as AdB2cUser[]; overrideAdB2cService(service, { getUsers: async (_context: Context, _externalIds: string[]) => { return adb2cReturn; }, }); // 第四階層が名称で検索 const tier4partners = await service.searchPartners( makeContext('trackingId', 'requestId'), tier4Account.admin.external_id, tier4Account.account.tier, 'Test' ); expect(tier4partners.searchResult.length).toBe(4); // 会社名の昇順で取得できていること expect(tier4partners.searchResult[0].name.includes(tier5AccountNames[0])).toBe(true); expect(tier4partners.searchResult[1].name.includes(tier5AccountNames[1])).toBe(true); expect(tier4partners.searchResult[2].name.includes(tier5AccountNames[2])).toBe(true); expect(tier4partners.searchResult[3].name.includes(tier5AccountNames[3])).toBe(true); }); it('存在しないCompanyNameで検索時、検索結果が0件になること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // 第三階層まで作成。 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const adb2cReturn = [ { id: tier3Account.admin.external_id, displayName: 'partner1', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner1@example.com', }, ], }, ] as AdB2cUser[]; overrideAdB2cService(service, { getUsers: async (_context: Context, _externalIds: string[]) => { return adb2cReturn; }, }); // ヒットしない条件で検索 const result = await service.searchPartners( makeContext('trackingId', 'requestId'), tier2Account.admin.external_id, tier2Account.account.tier, 'UNKOWN', ); // 検索結果が0件であること expect(result.searchResult.length).toBe(0); }); it('存在しないAccountIdで検索時、検索結果が0件になること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // 第三階層まで作成。 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const adb2cReturn = [ { id: tier3Account.admin.external_id, displayName: 'partner1', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner1@example.com', }, ], }, ] as AdB2cUser[]; overrideAdB2cService(service, { getUsers: async (_context: Context, _externalIds: string[]) => { return adb2cReturn; }, }); // ヒットしない条件で検索 const result = await service.searchPartners( makeContext('trackingId', 'requestId'), tier2Account.admin.external_id, tier2Account.account.tier, undefined, 999999, ); // 検索結果が0件であること expect(result.searchResult.length).toBe(0); }); it('存在するAccountIdと存在しないCompanyNameで検索時、検索結果が0件になること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // 第三階層まで作成。 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const adb2cReturn = [ { id: tier3Account.admin.external_id, displayName: 'partner1', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner1@example.com', }, ], }, ] as AdB2cUser[]; overrideAdB2cService(service, { getUsers: async (_context: Context, _externalIds: string[]) => { return adb2cReturn; }, }); // ヒットしない条件で検索 const result = await service.searchPartners( makeContext('trackingId', 'requestId'), tier2Account.admin.external_id, tier2Account.account.tier, 'UNKOWN', tier3Account.account.id, ); // 検索結果が0件であること expect(result.searchResult.length).toBe(0); }); it('検索を行うアカウントに関連しないアカウントのAccountIdで検索した場合、検索結果が0件になること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // 第三階層まで作成。 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); // 自身のアカウントに関連しないアカウント階層を作成 const anotherTier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'AnotherUnitTestCompany2', }); const anotherTier3Account = await makeTestAccount(source, { parent_account_id: anotherTier2Account.account.id, tier: 3, company_name: 'AnotherUnitTestCompany3', }); const adb2cReturn = [ { id: anotherTier3Account.admin.external_id, displayName: 'partner1', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner1@example.com', }, ], }, ] as AdB2cUser[]; overrideAdB2cService(service, { getUsers: async (_context: Context, _externalIds: string[]) => { return adb2cReturn; }, }); // 検索実行者と階層構造にないアカウントで検索 const result = await service.searchPartners( makeContext('trackingId', 'requestId'), tier2Account.admin.external_id, tier2Account.account.tier, undefined, anotherTier3Account.account.id, ); // 検索結果が0件であること expect(result.searchResult.length).toBe(0); }); it('第五階層のアカウントで検索した場合、検索結果が0件になること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const tier4Account = await makeTestAccount(source, { parent_account_id: tier3Account.account.id, tier: 4, company_name: 'UnitTestCompany4', }); const tier5Account = await makeTestAccount(source, { parent_account_id: tier4Account.account.id, tier: 5, company_name: 'UnitTestCompany5', }); const adb2cReturn = [ { id: tier2Account.admin.external_id, displayName: 'partner2', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner2@example.com', }, ], }, { id: tier3Account.admin.external_id, displayName: 'partner3', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner3@example.com', }, ], }, { id: tier4Account.admin.external_id, displayName: 'partner4', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner4@example.com', }, ], }, { id: tier5Account.admin.external_id, displayName: 'partner5', identities: [ { signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner5@example.com', }, ], }, ] as AdB2cUser[]; overrideAdB2cService(service, { getUsers: async (_context: Context, _externalIds: string[]) => { return adb2cReturn; }, }); // 第五階層のユーザーが検索条件なしで検索 const result = await service.searchPartners( makeContext('trackingId', 'requestId'), tier5Account.admin.external_id, tier5Account.account.tier, ); // 検索結果が0件であること expect(result.searchResult.length).toBe(0); }); it('DBエラー発生時、500エラーが返却されること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); // DBエラーを起こさせる overrideAccountsRepositoryService(service, { getAccountsRelatedOwnAccount: async () => { throw new Error('DB Error'); }, }); try { await service.searchPartners( makeContext('trackingId', 'requestId'), tier1Account.admin.external_id, tier1Account.account.tier, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); }); describe('getPartnerHierarchy', () => { let source: DataSource | null = null; beforeAll(async () => { if (source == null) { source = await (async () => { const s = new DataSource({ type: 'mysql', host: 'test_mysql_db', port: 3306, username: 'user', password: 'password', database: 'odms', entities: [__dirname + '/../../**/*.entity{.ts,.js}'], synchronize: false, // trueにすると自動的にmigrationが行われるため注意 logger: new TestLogger('none'), logging: true, }); return await s.initialize(); })(); } }); beforeEach(async () => { if (source) { await truncateAllTable(source); } }); afterAll(async () => { await source?.destroy(); source = null; }); it('⁠第一階層が、指定したアカウントの階層から自分までの階層を取得できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const tier4Account = await makeTestAccount(source, { parent_account_id: tier3Account.account.id, tier: 4, company_name: 'UnitTestCompany4', }); const tier5Account = await makeTestAccount(source, { parent_account_id: tier4Account.account.id, tier: 5, company_name: 'UnitTestCompany5', }); // 第五を指定して取得 const t5Hierarchy = await service.getPartnerHierarchy( makeContext('trackingId', 'requestId'), tier1Account.admin.external_id, tier1Account.account.tier, tier5Account.account.id, ); // 取得件数が正しいこと expect(t5Hierarchy.accountHierarchy.length).toBe(5); // 階層の昇順で取得できていること expect(t5Hierarchy.accountHierarchy[0].tier).toBe( tier1Account.account.tier, ); expect(t5Hierarchy.accountHierarchy[1].tier).toBe( tier2Account.account.tier, ); expect(t5Hierarchy.accountHierarchy[2].tier).toBe( tier3Account.account.tier, ); expect(t5Hierarchy.accountHierarchy[3].tier).toBe( tier4Account.account.tier, ); expect(t5Hierarchy.accountHierarchy[4].tier).toBe( tier5Account.account.tier, ); // 1件目のみすべてのプロパティを検査 expect(t5Hierarchy.accountHierarchy[0].name).toBe( tier1Account.account.company_name, ); expect(t5Hierarchy.accountHierarchy[0].accountId).toBe( tier1Account.account.id, ); // 第四を指定して取得 const t4Hierarchy = await service.getPartnerHierarchy( makeContext('trackingId', 'requestId'), tier1Account.admin.external_id, tier1Account.account.tier, tier4Account.account.id, ); // 取得件数が正しいこと expect(t4Hierarchy.accountHierarchy.length).toBe(4); // 階層の昇順で取得できていること expect(t4Hierarchy.accountHierarchy[0].tier).toBe( tier1Account.account.tier, ); expect(t4Hierarchy.accountHierarchy[1].tier).toBe( tier2Account.account.tier, ); expect(t4Hierarchy.accountHierarchy[2].tier).toBe( tier3Account.account.tier, ); expect(t4Hierarchy.accountHierarchy[3].tier).toBe( tier4Account.account.tier, ); // 第三を指定して取得 const t3Hierarchy = await service.getPartnerHierarchy( makeContext('trackingId', 'requestId'), tier1Account.admin.external_id, tier1Account.account.tier, tier3Account.account.id, ); // 取得件数が正しいこと expect(t3Hierarchy.accountHierarchy.length).toBe(3); // 階層の昇順で取得できていること expect(t3Hierarchy.accountHierarchy[0].tier).toBe( tier1Account.account.tier, ); expect(t3Hierarchy.accountHierarchy[1].tier).toBe( tier2Account.account.tier, ); expect(t3Hierarchy.accountHierarchy[2].tier).toBe( tier3Account.account.tier, ); // 第二を指定して取得 const t2Hierarchy = await service.getPartnerHierarchy( makeContext('trackingId', 'requestId'), tier1Account.admin.external_id, tier1Account.account.tier, tier2Account.account.id, ); // 取得件数が正しいこと expect(t2Hierarchy.accountHierarchy.length).toBe(2); // 階層の昇順で取得できていること expect(t2Hierarchy.accountHierarchy[0].tier).toBe( tier1Account.account.tier, ); expect(t2Hierarchy.accountHierarchy[1].tier).toBe( tier2Account.account.tier, ); }); it('⁠第二階層が、指定したアカウントの階層から自分までの階層を取得できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const tier4Account = await makeTestAccount(source, { parent_account_id: tier3Account.account.id, tier: 4, company_name: 'UnitTestCompany4', }); const tier5Account = await makeTestAccount(source, { parent_account_id: tier4Account.account.id, tier: 5, company_name: 'UnitTestCompany5', }); // 第五を指定して取得 const t5Hierarchy = await service.getPartnerHierarchy( makeContext('trackingId', 'requestId'), tier2Account.admin.external_id, tier2Account.account.tier, tier5Account.account.id, ); // 取得件数が正しいこと expect(t5Hierarchy.accountHierarchy.length).toBe(4); // 階層の昇順で取得できていること expect(t5Hierarchy.accountHierarchy[0].tier).toBe( tier2Account.account.tier, ); expect(t5Hierarchy.accountHierarchy[1].tier).toBe( tier3Account.account.tier, ); expect(t5Hierarchy.accountHierarchy[2].tier).toBe( tier4Account.account.tier, ); expect(t5Hierarchy.accountHierarchy[3].tier).toBe( tier5Account.account.tier, ); // 1件目のみすべてのプロパティを検査 expect(t5Hierarchy.accountHierarchy[0].name).toBe( tier2Account.account.company_name, ); expect(t5Hierarchy.accountHierarchy[0].accountId).toBe( tier2Account.account.id, ); // 第四を指定して取得 const t4Hierarchy = await service.getPartnerHierarchy( makeContext('trackingId', 'requestId'), tier2Account.admin.external_id, tier2Account.account.tier, tier4Account.account.id, ); // 取得件数が正しいこと expect(t4Hierarchy.accountHierarchy.length).toBe(3); // 階層の昇順で取得できていること expect(t4Hierarchy.accountHierarchy[0].tier).toBe( tier2Account.account.tier, ); expect(t4Hierarchy.accountHierarchy[1].tier).toBe( tier3Account.account.tier, ); expect(t4Hierarchy.accountHierarchy[2].tier).toBe( tier4Account.account.tier, ); // 第三を指定して取得 const t3Hierarchy = await service.getPartnerHierarchy( makeContext('trackingId', 'requestId'), tier2Account.admin.external_id, tier2Account.account.tier, tier3Account.account.id, ); // 取得件数が正しいこと expect(t3Hierarchy.accountHierarchy.length).toBe(2); // 階層の昇順で取得できていること expect(t3Hierarchy.accountHierarchy[0].tier).toBe( tier2Account.account.tier, ); expect(t3Hierarchy.accountHierarchy[1].tier).toBe( tier3Account.account.tier, ); }); it('⁠第三階層が、指定したアカウントの階層から自分までの階層を取得できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const tier4Account = await makeTestAccount(source, { parent_account_id: tier3Account.account.id, tier: 4, company_name: 'UnitTestCompany4', }); const tier5Account = await makeTestAccount(source, { parent_account_id: tier4Account.account.id, tier: 5, company_name: 'UnitTestCompany5', }); // 第五を指定して取得 const t5Hierarchy = await service.getPartnerHierarchy( makeContext('trackingId', 'requestId'), tier3Account.admin.external_id, tier3Account.account.tier, tier5Account.account.id, ); // 取得件数が正しいこと expect(t5Hierarchy.accountHierarchy.length).toBe(3); // 階層の昇順で取得できていること expect(t5Hierarchy.accountHierarchy[0].tier).toBe( tier3Account.account.tier, ); expect(t5Hierarchy.accountHierarchy[1].tier).toBe( tier4Account.account.tier, ); expect(t5Hierarchy.accountHierarchy[2].tier).toBe( tier5Account.account.tier, ); // 1件目のみすべてのプロパティを検査 expect(t5Hierarchy.accountHierarchy[0].name).toBe( tier3Account.account.company_name, ); expect(t5Hierarchy.accountHierarchy[0].accountId).toBe( tier3Account.account.id, ); // 第四を指定して取得 const t4Hierarchy = await service.getPartnerHierarchy( makeContext('trackingId', 'requestId'), tier3Account.admin.external_id, tier3Account.account.tier, tier4Account.account.id, ); // 取得件数が正しいこと expect(t4Hierarchy.accountHierarchy.length).toBe(2); // 階層の昇順で取得できていること expect(t4Hierarchy.accountHierarchy[0].tier).toBe( tier3Account.account.tier, ); expect(t4Hierarchy.accountHierarchy[1].tier).toBe( tier4Account.account.tier, ); }); it('⁠第四階層が、指定したアカウントの階層から自分までの階層を取得できること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const tier4Account = await makeTestAccount(source, { parent_account_id: tier3Account.account.id, tier: 4, company_name: 'UnitTestCompany4', }); const tier5Account = await makeTestAccount(source, { parent_account_id: tier4Account.account.id, tier: 5, company_name: 'UnitTestCompany5', }); // 第五を指定して取得 const t5Hierarchy = await service.getPartnerHierarchy( makeContext('trackingId', 'requestId'), tier4Account.admin.external_id, tier4Account.account.tier, tier5Account.account.id, ); // 取得件数が正しいこと expect(t5Hierarchy.accountHierarchy.length).toBe(2); // 階層の昇順で取得できていること expect(t5Hierarchy.accountHierarchy[0].tier).toBe( tier4Account.account.tier, ); expect(t5Hierarchy.accountHierarchy[1].tier).toBe( tier5Account.account.tier, ); // 1件目のみすべてのプロパティを検査 expect(t5Hierarchy.accountHierarchy[0].name).toBe( tier4Account.account.company_name, ); expect(t5Hierarchy.accountHierarchy[0].accountId).toBe( tier4Account.account.id, ); }); it('自分自身のアカウントを対象に階層を表示した時、エラーになること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); const tier4Account = await makeTestAccount(source, { parent_account_id: tier3Account.account.id, tier: 4, company_name: 'UnitTestCompany4', }); const tier5Account = await makeTestAccount(source, { parent_account_id: tier4Account.account.id, tier: 5, company_name: 'UnitTestCompany5', }); // 自分自身を指定して取得 try { await service.getPartnerHierarchy( makeContext('trackingId', 'requestId'), tier4Account.admin.external_id, tier4Account.account.tier, tier4Account.account.id, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); it('指定したアカウントが自身と親子関係になっていない場合、エラーになること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); const tier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'UnitTestCompany2', }); const tier3Account = await makeTestAccount(source, { parent_account_id: tier2Account.account.id, tier: 3, company_name: 'UnitTestCompany3', }); // 自身のアカウントに関連しないアカウント階層を作成 const anotherTier2Account = await makeTestAccount(source, { parent_account_id: tier1Account.account.id, tier: 2, company_name: 'AnotherUnitTestCompany2', }); const anotherTier3Account = await makeTestAccount(source, { parent_account_id: anotherTier2Account.account.id, tier: 3, company_name: 'AnotherUnitTestCompany3', }); // 自身と親子関係にないアカウントを指定して取得 try { await service.getPartnerHierarchy( makeContext('trackingId', 'requestId'), tier2Account.admin.external_id, tier2Account.account.tier, anotherTier3Account.account.id, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); it('DBエラー発生時、500エラーが返却されること', async () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); // アカウントの階層を作成 const tier1Account = await makeTestAccount(source, { parent_account_id: null, tier: 1, company_name: 'UnitTestCompany1', }); // DBエラーを起こさせる overrideAccountsRepositoryService(service, { findUserByExternalId: async () => { throw new Error('DB Error'); }, }); try { await service.getPartnerHierarchy( makeContext('trackingId', 'requestId'), tier1Account.admin.external_id, tier1Account.account.tier, tier1Account.account.id, ); } catch (e) { if (e instanceof HttpException) { expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { fail(); } } }); });