From 704a5aafc293c446dc0268dc1953e4514f4eaf74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B9=AF=E6=9C=AC=20=E9=96=8B?= Date: Wed, 16 Aug 2023 02:54:32 +0000 Subject: [PATCH] =?UTF-8?q?Merged=20PR=20327:=20=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=92=E6=9C=80=E6=96=B0=E5=8C=96=EF=BC=88=E3=83=A6?= =?UTF-8?q?=E3=83=BC=E3=82=B6=E3=83=BC=E8=BF=BD=E5=8A=A0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task2400: テストを最新化(ユーザー追加)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2400) - 新規ユーザー追加のユニットテストをDBを利用するテストへ置き換え - API呼び出しやRepository呼び出しをMockする仕組みを改善 - オブジェクト全てを上書きするのではなく、特定のメソッドのみを上書きする形に修正 - これにより、他のDBアクセスは正常にSQLiteへのアクセスが成功しつつ、特定のDBアクセスのみMockにできる ## レビューポイント - テストケースは足りているか - Mockする仕組みを改善したが、修正内容は問題ないか ## 動作確認状況 - npm run test で成功 --- dictation_server/src/common/test/overrides.ts | 89 +- .../accounts/accounts.service.spec.ts | 4 +- .../src/features/users/test/utility.ts | 90 ++ .../src/features/users/users.service.spec.ts | 962 ++++++++++++------ .../repositories/users/entity/user.entity.ts | 4 +- 5 files changed, 814 insertions(+), 335 deletions(-) diff --git a/dictation_server/src/common/test/overrides.ts b/dictation_server/src/common/test/overrides.ts index 054573e..6be0936 100644 --- a/dictation_server/src/common/test/overrides.ts +++ b/dictation_server/src/common/test/overrides.ts @@ -1,4 +1,7 @@ import { ConflictError } from '../../gateways/adb2c/adb2c.service'; +import { User, newUser } from '../../repositories/users/entity/user.entity'; + +// ### ユニットテスト用コード以外では絶対に使用してはいけないダーティな手段を使用しているが、他の箇所では使用しないこと ### /** * adB2cServiceのモックを作成して、TServiceが依存するサービス(adB2cService)の参照を上書きする @@ -17,11 +20,14 @@ export const overrideAdB2cService = ( }, ): void => { const { createUser } = overrides; - Object.defineProperty(service, 'adB2cService', { - value: { - createUser: createUser ?? jest.fn().mockResolvedValue({ sub: 'dummy' }), - }, - }); + // テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得 + const obj = (service as any).adB2cService; + if (createUser) { + Object.defineProperty(obj, createUser.name, { + value: createUser, + writable: true, + }); + } }; /** @@ -38,6 +44,11 @@ export const overrideSendgridService = ( userId: number, email: string, ) => Promise<{ subject: string; text: string; html: string }>; + createMailContentFromEmailConfirmForNormalUser?: ( + accountId: number, + userId: number, + email: string, + ) => Promise<{ subject: string; text: string; html: string }>; sendMail?: ( to: string, from: string, @@ -47,17 +58,59 @@ export const overrideSendgridService = ( ) => Promise; }, ): void => { - const { createMailContentFromEmailConfirm, sendMail } = overrides; - Object.defineProperty(service, 'sendgridService', { - value: { - createMailContentFromEmailConfirm: - createMailContentFromEmailConfirm ?? - jest.fn().mockResolvedValue({ - subject: 'dummySubject', - text: 'dummyText', - html: 'dummyHtml', - }), - sendMail: sendMail ?? jest.fn().mockResolvedValue(undefined), - }, - }); + const { + createMailContentFromEmailConfirm, + createMailContentFromEmailConfirmForNormalUser, + sendMail, + } = overrides; + + // テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得 + const obj = (service as any).sendgridService; + if (sendMail) { + Object.defineProperty(obj, sendMail.name, { + value: sendMail, + writable: true, + }); + } + + if (createMailContentFromEmailConfirm) { + Object.defineProperty(obj, createMailContentFromEmailConfirm.name, { + value: createMailContentFromEmailConfirm, + writable: true, + }); + } + + if (createMailContentFromEmailConfirmForNormalUser) { + Object.defineProperty( + obj, + createMailContentFromEmailConfirmForNormalUser.name, + { + value: createMailContentFromEmailConfirmForNormalUser, + writable: true, + }, + ); + } +}; + +/** + * usersRepositoryのモックを作成して、TServiceが依存するサービス(usersRepositoryService)の参照を上書きする + * ※ serviceに指定するオブジェクトは`usersRepository: UsersRepositoryService`メンバ変数を持つ必要がある + * @param service 上書きしたいTService + * @param overrides usersRepositoryの各種メソッドのモックが返す値(省略した場合は既定のダミーの値) + */ +export const overrideUsersRepositoryService = ( + service: TService, + overrides: { + createNormalUser?: (user: newUser) => Promise; + }, +): void => { + const { createNormalUser } = overrides; + // テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得 + const obj = (service as any).usersRepository; + if (createNormalUser) { + Object.defineProperty(obj, createNormalUser.name, { + value: createNormalUser, + writable: true, + }); + } }; diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts index f068766..2ffaf96 100644 --- a/dictation_server/src/features/accounts/accounts.service.spec.ts +++ b/dictation_server/src/features/accounts/accounts.service.spec.ts @@ -137,7 +137,7 @@ describe('createAccount', () => { expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); } else { - expect(true).toBe(false); // ここには来てはいけない + fail(); } } @@ -186,7 +186,7 @@ describe('createAccount', () => { expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST); expect(e.getResponse()).toEqual(makeErrorResponse('E010301')); } else { - expect(true).toBe(false); // ここには来てはいけない + fail(); } } diff --git a/dictation_server/src/features/users/test/utility.ts b/dictation_server/src/features/users/test/utility.ts index cb3a442..4bdd840 100644 --- a/dictation_server/src/features/users/test/utility.ts +++ b/dictation_server/src/features/users/test/utility.ts @@ -38,6 +38,69 @@ import { AdB2cMockValue, makeAdB2cServiceMock } from './users.service.mock'; import { AdB2cService } from '../../../gateways/adb2c/adb2c.service'; import { LICENSE_ALLOCATED_STATUS, LICENSE_TYPE } from '../../../constants'; +export const createAccountAndAdminUser = async ( + datasource: DataSource, + adminExternalId: string, +): Promise<{ + accountId: number; + adminId: number; + role: string; + tier: number; +}> => { + const { identifiers: account_idf } = await datasource + .getRepository(Account) + .insert({ + tier: 1, + country: 'JP', + delegation_permission: false, + locked: false, + company_name: 'test inc.', + verified: true, + deleted_at: '', + created_by: 'test_runner', + created_at: new Date(), + updated_by: 'updater', + updated_at: new Date(), + }); + const account = account_idf.pop() as Account; + + const { identifiers: user_idf } = await datasource + .getRepository(User) + .insert({ + account_id: account.id, + external_id: adminExternalId, + role: 'admin none', + accepted_terms_version: '1.0', + email_verified: true, + auto_renew: true, + license_alert: true, + notification: true, + encryption: true, + encryption_password: 'password', + prompt: true, + created_by: 'test_runner', + created_at: new Date(), + updated_by: 'updater', + updated_at: new Date(), + }); + const user = user_idf.pop() as User; + + // Accountの管理者を設定する + await datasource.getRepository(Account).update( + { id: user.account_id }, + { + primary_admin_user_id: user.id, + }, + ); + + return { + accountId: account.id, + adminId: user.id, + role: user.role, + tier: account.tier, + }; +}; + export const createAccount = async ( datasource: DataSource, ): Promise<{ accountId: number }> => { @@ -116,6 +179,33 @@ export const getLicenses = async ( return licenses; }; +/** + * テスト ユーティリティ: 指定外部IDを持つユーザーを取得する + * @param dataSource データソース + * @param externalId 外部ID + * @returns 該当ユーザー + */ +export const getUserByExternalId = async ( + datasource: DataSource, + externalId: string, +): Promise => { + const user = await datasource.getRepository(User).findOne({ + where: { + external_id: externalId, + }, + }); + return user; +}; + +/** + * テスト ユーティリティ: すべてのユーザーを取得する + * @param dataSource データソース + * @returns 該当ユーザー一覧 + */ +export const getUsers = async (dataSource: DataSource): Promise => { + return await dataSource.getRepository(User).find(); +}; + /** * * @param datasource diff --git a/dictation_server/src/features/users/users.service.spec.ts b/dictation_server/src/features/users/users.service.spec.ts index cbce69f..848b41f 100644 --- a/dictation_server/src/features/users/users.service.spec.ts +++ b/dictation_server/src/features/users/users.service.spec.ts @@ -12,11 +12,14 @@ import { import { EmailAlreadyVerifiedError } from '../../repositories/users/errors/types'; import { createAccount, + createAccountAndAdminUser, createLicense, createUser, createUserGroup, getLicenses, getUser, + getUserByExternalId, + getUsers, makeTestingModuleWithAdb2c, } from './test/utility'; import { DataSource } from 'typeorm'; @@ -30,6 +33,11 @@ import { USER_ROLES, } from '../../constants'; import { makeTestingModule } from '../../common/test/modules'; +import { + overrideAdB2cService, + overrideSendgridService, + overrideUsersRepositoryService, +} from '../../common/test/overrides'; import { ExpirationThresholdDate } from '../licenses/types/types'; import { License } from '../../repositories/licenses/entity/license.entity'; @@ -340,22 +348,110 @@ describe('UsersService.confirmUserAndInitPassword', () => { }); describe('UsersService.createUser', () => { + let source: DataSource = null; + beforeEach(async () => { + source = new DataSource({ + type: 'sqlite', + database: ':memory:', + logging: false, + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: true, // trueにすると自動的にmigrationが行われるため注意 + }); + return source.initialize(); + }); + + afterEach(async () => { + await source.destroy(); + source = null; + }); + + it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:None)', async () => { + const module = await makeTestingModule(source); + const service = module.get(UsersService); + const adminExternalId = 'ADMIN0001'; + const { + accountId, + role: adminRole, + tier, + } = await createAccountAndAdminUser(source, adminExternalId); + + const token: AccessToken = { + userId: adminExternalId, + role: adminRole, + tier: tier, + }; + + const name = 'test_user1'; + const role = USER_ROLES.NONE; + const email = 'test1@example.co.jp'; + const autoRenew = true; + const licenseAlert = true; + const notification = true; + + const externalId = '0001'; + + overrideAdB2cService(service, { + createUser: async ( + _email: string, + _password: string, + _username: string, + ) => { + // ユーザー作成時に指定したパラメータが正しく渡されていることを確認 + expect(email).toEqual(_email); + expect(name).toEqual(_username); + + return { sub: externalId }; + }, + }); + overrideSendgridService(service, {}); + + expect( + await service.createUser( + token, + name, + role, + email, + autoRenew, + licenseAlert, + notification, + ), + ).toEqual(undefined); + + // 追加されたユーザーが正しくDBに登録されていることを確認 + const user = await getUserByExternalId(source, externalId); + expect(user).not.toBeNull(); + expect(user.account_id).toEqual(accountId); + expect(user.role).toEqual(role); + expect(user.author_id).toEqual(null); + expect(user.email_verified).toEqual(false); + expect(user.auto_renew).toEqual(autoRenew); + expect(user.license_alert).toEqual(licenseAlert); + expect(user.notification).toEqual(notification); + expect(user.encryption).toEqual(false); + expect(user.encryption_password).toEqual(null); + expect(user.prompt).toEqual(false); + + // 他にユーザーが登録されていないことを確認 + const users = await getUsers(source); + expect(users.length).toEqual(2); + }); + it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author; 暗号化あり)', async () => { - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const licensesRepositoryMockValue = null; - const adb2cParam = makeDefaultAdB2cMockValue(); - const sendgridMockValue = makeDefaultSendGridlValue(); - const configMockValue = makeDefaultConfigValue(); - const sortCriteriaRepositoryMockValue = - makeDefaultSortCriteriaRepositoryMockValue(); - const service = await makeUsersServiceMock( - usersRepositoryMockValue, - licensesRepositoryMockValue, - adb2cParam, - sendgridMockValue, - configMockValue, - sortCriteriaRepositoryMockValue, - ); + const module = await makeTestingModule(source); + const service = module.get(UsersService); + const adminExternalId = 'ADMIN0001'; + const { + accountId, + role: adminRole, + tier, + } = await createAccountAndAdminUser(source, adminExternalId); + + const token: AccessToken = { + userId: adminExternalId, + role: adminRole, + tier: tier, + }; + const name = 'test_user2'; const role = USER_ROLES.AUTHOR; const email = 'test2@example.co.jp'; @@ -366,7 +462,24 @@ describe('UsersService.createUser', () => { const encryption = true; const prompt = true; const encryptionPassword = 'testPassword'; - const token: AccessToken = { userId: '0001', role: '', tier: 5 }; + + const externalId = '0001'; + + overrideAdB2cService(service, { + createUser: async ( + _email: string, + _password: string, + _username: string, + ) => { + // ユーザー作成時に指定したパラメータが正しく渡されていることを確認 + expect(email).toEqual(_email); + expect(name).toEqual(_username); + + return { sub: externalId }; + }, + }); + overrideSendgridService(service, {}); + expect( await service.createUser( token, @@ -382,24 +495,42 @@ describe('UsersService.createUser', () => { prompt, ), ).toEqual(undefined); + + // 追加されたユーザーが正しくDBに登録されていることを確認 + const user = await getUserByExternalId(source, externalId); + expect(user).not.toBeNull(); + expect(user.account_id).toEqual(accountId); + expect(user.role).toEqual(role); + expect(user.author_id).toEqual(authorId); + expect(user.email_verified).toEqual(false); + expect(user.auto_renew).toEqual(autoRenew); + expect(user.license_alert).toEqual(licenseAlert); + expect(user.notification).toEqual(notification); + expect(user.encryption).toEqual(encryption); + expect(user.encryption_password).toEqual(encryptionPassword); + expect(user.prompt).toEqual(prompt); + + // 他にユーザーが登録されていないことを確認 + const users = await getUsers(source); + expect(users.length).toEqual(2); }); it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author; 暗号化無し)', async () => { - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const licensesRepositoryMockValue = null; - const adb2cParam = makeDefaultAdB2cMockValue(); - const sendgridMockValue = makeDefaultSendGridlValue(); - const configMockValue = makeDefaultConfigValue(); - const sortCriteriaRepositoryMockValue = - makeDefaultSortCriteriaRepositoryMockValue(); - const service = await makeUsersServiceMock( - usersRepositoryMockValue, - licensesRepositoryMockValue, - adb2cParam, - sendgridMockValue, - configMockValue, - sortCriteriaRepositoryMockValue, - ); + const module = await makeTestingModule(source); + const service = module.get(UsersService); + const adminExternalId = 'ADMIN0001'; + const { + accountId, + role: adminRole, + tier, + } = await createAccountAndAdminUser(source, adminExternalId); + + const token: AccessToken = { + userId: adminExternalId, + role: adminRole, + tier: tier, + }; + const name = 'test_user2'; const role = USER_ROLES.AUTHOR; const email = 'test2@example.co.jp'; @@ -409,9 +540,485 @@ describe('UsersService.createUser', () => { const authorId = 'testID'; const encryption = false; const prompt = true; - const encryptionPassword = undefined; - const token: AccessToken = { userId: '0001', role: '', tier: 5 }; + + const externalId = '0001'; + + overrideAdB2cService(service, { + createUser: async ( + _email: string, + _password: string, + _username: string, + ) => { + // ユーザー作成時に指定したパラメータが正しく渡されていることを確認 + expect(email).toEqual(_email); + expect(name).toEqual(_username); + + return { sub: externalId }; + }, + }); + overrideSendgridService(service, {}); + expect( + await service.createUser( + token, + name, + role, + email, + autoRenew, + licenseAlert, + notification, + authorId, + encryption, + undefined, + prompt, + ), + ).toEqual(undefined); + + // 追加されたユーザーが正しくDBに登録されていることを確認 + const user = await getUserByExternalId(source, externalId); + expect(user).not.toBeNull(); + expect(user.account_id).toEqual(accountId); + expect(user.role).toEqual(role); + expect(user.author_id).toEqual(authorId); + expect(user.email_verified).toEqual(false); + expect(user.auto_renew).toEqual(autoRenew); + expect(user.license_alert).toEqual(licenseAlert); + expect(user.notification).toEqual(notification); + expect(user.encryption).toEqual(encryption); + expect(user.encryption_password).toBeNull(); + expect(user.prompt).toEqual(prompt); + + // 他にユーザーが登録されていないことを確認 + const users = await getUsers(source); + expect(users.length).toEqual(2); + }); + + it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Transcriptioninst)', async () => { + const module = await makeTestingModule(source); + const service = module.get(UsersService); + const adminExternalId = 'ADMIN0001'; + const { + accountId, + role: adminRole, + tier, + } = await createAccountAndAdminUser(source, adminExternalId); + + const token: AccessToken = { + userId: adminExternalId, + role: adminRole, + tier: tier, + }; + + const name = 'test_user3'; + const role = USER_ROLES.TYPIST; + const email = 'test3@example.co.jp'; + const autoRenew = true; + const licenseAlert = true; + const notification = true; + + const externalId = '0001'; + + overrideAdB2cService(service, { + createUser: async ( + _email: string, + _password: string, + _username: string, + ) => { + // ユーザー作成時に指定したパラメータが正しく渡されていることを確認 + expect(email).toEqual(_email); + expect(name).toEqual(_username); + + return { sub: externalId }; + }, + }); + overrideSendgridService(service, {}); + + expect( + await service.createUser( + token, + name, + role, + email, + autoRenew, + licenseAlert, + notification, + ), + ).toEqual(undefined); + + // 追加されたユーザーが正しくDBに登録されていることを確認 + const user = await getUserByExternalId(source, externalId); + expect(user).not.toBeNull(); + expect(user.account_id).toEqual(accountId); + expect(user.role).toEqual(role); + expect(user.author_id).toBeNull(); + expect(user.email_verified).toEqual(false); + expect(user.auto_renew).toEqual(autoRenew); + expect(user.license_alert).toEqual(licenseAlert); + expect(user.notification).toEqual(notification); + expect(user.encryption).toEqual(false); + expect(user.encryption_password).toBeNull(); + expect(user.prompt).toEqual(false); + + // 他にユーザーが登録されていないことを確認 + const users = await getUsers(source); + expect(users.length).toEqual(2); + }); + + it('DBネットワークエラーとなる場合、エラーとなる。', async () => { + const module = await makeTestingModule(source); + const service = module.get(UsersService); + const adminExternalId = 'ADMIN0001'; + const { role: adminRole, tier } = await createAccountAndAdminUser( + source, + adminExternalId, + ); + + const token: AccessToken = { + userId: adminExternalId, + role: adminRole, + tier: tier, + }; + + const name = 'test_user1'; + const role = USER_ROLES.NONE; + const email = 'test1@example.co.jp'; + const autoRenew = true; + const licenseAlert = true; + const notification = true; + + const externalId = '0001'; + + overrideAdB2cService(service, { + createUser: async ( + _email: string, + _password: string, + _username: string, + ) => { + // ユーザー作成時に指定したパラメータが正しく渡されていることを確認 + expect(email).toEqual(_email); + expect(name).toEqual(_username); + + return { sub: externalId }; + }, + }); + overrideSendgridService(service, {}); + + // DBエラーを発生させる + overrideUsersRepositoryService(service, { + createNormalUser: async () => { + throw new Error('DB error'); + }, + }); + + try { + await service.createUser( + token, + name, + role, + email, + autoRenew, + licenseAlert, + notification, + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); + expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); + } else { + fail(); + } + } + }); + + it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。', async () => { + const module = await makeTestingModule(source); + const service = module.get(UsersService); + const adminExternalId = 'ADMIN0001'; + const { role: adminRole, tier } = await createAccountAndAdminUser( + source, + adminExternalId, + ); + + const token: AccessToken = { + userId: adminExternalId, + role: adminRole, + tier: tier, + }; + + const name = 'test_user1'; + const role = USER_ROLES.NONE; + const email = 'test1@example.co.jp'; + const autoRenew = true; + const licenseAlert = true; + const notification = true; + + overrideAdB2cService(service, { + createUser: async ( + _email: string, + _password: string, + _username: string, + ) => { + // ユーザー作成時に指定したパラメータが正しく渡されていることを確認 + expect(email).toEqual(_email); + expect(name).toEqual(_username); + + throw new Error('ADB2C error'); + }, + }); + overrideSendgridService(service, {}); + + try { + await service.createUser( + token, + name, + role, + email, + autoRenew, + licenseAlert, + notification, + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); + expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); + } else { + fail(); + } + } + + // ユーザーが登録されていないことを確認 + const users = await getUsers(source); + expect(users.length).toEqual(1); + }); + + it('Azure AD B2C内でメールアドレスが重複している場合、エラーとなる。', async () => { + const module = await makeTestingModule(source); + const service = module.get(UsersService); + const adminExternalId = 'ADMIN0001'; + const { role: adminRole, tier } = await createAccountAndAdminUser( + source, + adminExternalId, + ); + + const token: AccessToken = { + userId: adminExternalId, + role: adminRole, + tier: tier, + }; + + const name = 'test_user1'; + const role = USER_ROLES.NONE; + const email = 'test1@example.co.jp'; + const autoRenew = true; + const licenseAlert = true; + const notification = true; + + overrideAdB2cService(service, { + createUser: async ( + _email: string, + _password: string, + _username: string, + ) => { + // ユーザー作成時に指定したパラメータが正しく渡されていることを確認 + expect(email).toEqual(_email); + expect(name).toEqual(_username); + + // Conflictエラーを返す + return { + reason: 'email', + message: 'The email address is already in use by another account.', + }; + }, + }); + overrideSendgridService(service, {}); + + try { + await service.createUser( + token, + name, + role, + email, + autoRenew, + licenseAlert, + notification, + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); + expect(e.getResponse()).toEqual(makeErrorResponse('E010301')); + } else { + fail(); + } + } + + // ユーザーが登録されていないことを確認 + const users = await getUsers(source); + expect(users.length).toEqual(1); + }); + + it('AuthorIDが重複している場合、エラーとなる。(AuthorID重複チェックでエラー)', async () => { + const module = await makeTestingModule(source); + const service = module.get(UsersService); + const adminExternalId = 'ADMIN0001'; + const { role: adminRole, tier } = await createAccountAndAdminUser( + source, + adminExternalId, + ); + + const token: AccessToken = { + userId: adminExternalId, + role: adminRole, + tier: tier, + }; + + const name = 'test_user2'; + const role = USER_ROLES.AUTHOR; + const autoRenew = true; + const licenseAlert = true; + const notification = true; + const authorId = 'testID'; + const encryption = true; + const prompt = true; + const encryptionPassword = 'testPassword'; + + const email_1 = 'test_1@example.co.jp'; + const externalId_1 = '0001'; + + overrideAdB2cService(service, { + createUser: async ( + _email: string, + _password: string, + _username: string, + ) => { + // ユーザー作成時に指定したパラメータが正しく渡されていることを確認 + expect(email_1).toEqual(_email); + expect(name).toEqual(_username); + + return { sub: externalId_1 }; + }, + }); + overrideSendgridService(service, {}); + + expect( + await service.createUser( + token, + name, + role, + email_1, + autoRenew, + licenseAlert, + notification, + authorId, + encryption, + encryptionPassword, + prompt, + ), + ).toEqual(undefined); + + { + // 他にユーザーが登録されていないことを確認 + const users = await getUsers(source); + expect(users.length).toEqual(2); + } + + // Azure Ad B2CのMockをユーザー2用に切り替える + const email_2 = 'test_1@example.co.jp'; + const externalId_2 = '0001'; + overrideAdB2cService(service, { + createUser: async ( + _email: string, + _password: string, + _username: string, + ) => { + // ユーザー作成時に指定したパラメータが正しく渡されていることを確認 + expect(email_2).toEqual(_email); + expect(name).toEqual(_username); + + return { sub: externalId_2 }; + }, + }); + + try { + await service.createUser( + token, + name, + role, + email_2, + autoRenew, + licenseAlert, + notification, + authorId, + encryption, + encryptionPassword, + prompt, + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); + expect(e.getResponse()).toEqual(makeErrorResponse('E010302')); + } else { + fail(); + } + } + + // 新規にユーザーが登録されていないことを確認 + { + const users = await getUsers(source); + expect(users.length).toEqual(2); + } + }); + + it('AuthorIDが重複している場合、エラーとなる。(insert失敗)', async () => { + const module = await makeTestingModule(source); + const service = module.get(UsersService); + const adminExternalId = 'ADMIN0001'; + const { role: adminRole, tier } = await createAccountAndAdminUser( + source, + adminExternalId, + ); + + const token: AccessToken = { + userId: adminExternalId, + role: adminRole, + tier: tier, + }; + + const name = 'test_user2'; + const role = USER_ROLES.AUTHOR; + const email = 'test2@example.co.jp'; + const autoRenew = true; + const licenseAlert = true; + const notification = true; + const authorId = 'testID'; + const encryption = true; + const prompt = true; + const encryptionPassword = 'testPassword'; + + const externalId = '0001'; + + overrideAdB2cService(service, { + createUser: async ( + _email: string, + _password: string, + _username: string, + ) => { + // ユーザー作成時に指定したパラメータが正しく渡されていることを確認 + expect(email).toEqual(_email); + expect(name).toEqual(_username); + + return { sub: externalId }; + }, + }); + overrideSendgridService(service, {}); + + // AuthorIDのUNIQUE制約エラーを発生させる + overrideUsersRepositoryService(service, { + createNormalUser: async () => { + throw { code: 'ER_DUP_ENTRY' }; + }, + }); + + try { await service.createUser( token, name, @@ -424,286 +1031,15 @@ describe('UsersService.createUser', () => { encryption, encryptionPassword, prompt, - ), - ).toEqual(undefined); - }); - - it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Transcriptioninst)', async () => { - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const licensesRepositoryMockValue = null; - const adb2cParam = makeDefaultAdB2cMockValue(); - const sendgridMockValue = makeDefaultSendGridlValue(); - const configMockValue = makeDefaultConfigValue(); - const sortCriteriaRepositoryMockValue = - makeDefaultSortCriteriaRepositoryMockValue(); - const service = await makeUsersServiceMock( - usersRepositoryMockValue, - licensesRepositoryMockValue, - adb2cParam, - sendgridMockValue, - configMockValue, - sortCriteriaRepositoryMockValue, - ); - const name = 'test_user3'; - const role = USER_ROLES.TYPIST; - const email = 'test3@example.co.jp'; - const autoRenew = true; - const licenseAlert = true; - const notification = true; - const token: AccessToken = { userId: '0001', role: '', tier: 5 }; - expect( - await service.createUser( - token, - name, - role, - email, - autoRenew, - licenseAlert, - notification, - undefined, - ), - ).toEqual(undefined); - }); - - it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:None)', async () => { - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const licensesRepositoryMockValue = null; - const adb2cParam = makeDefaultAdB2cMockValue(); - const sendgridMockValue = makeDefaultSendGridlValue(); - const configMockValue = makeDefaultConfigValue(); - const sortCriteriaRepositoryMockValue = - makeDefaultSortCriteriaRepositoryMockValue(); - const service = await makeUsersServiceMock( - usersRepositoryMockValue, - licensesRepositoryMockValue, - adb2cParam, - sendgridMockValue, - configMockValue, - sortCriteriaRepositoryMockValue, - ); - const name = 'test_user1'; - const role = USER_ROLES.NONE; - const email = 'test1@example.co.jp'; - const autoRenew = true; - const licenseAlert = true; - const notification = true; - const token: AccessToken = { userId: '0001', role: '', tier: 5 }; - expect( - await service.createUser( - token, - name, - role, - email, - autoRenew, - licenseAlert, - notification, - ), - ).toEqual(undefined); - }); - - it('DBネットワークエラーとなる場合、エラーとなる。', async () => { - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const licensesRepositoryMockValue = null; - const adb2cParam = makeDefaultAdB2cMockValue(); - const sendgridMockValue = makeDefaultSendGridlValue(); - const configMockValue = makeDefaultConfigValue(); - const sortCriteriaRepositoryMockValue = - makeDefaultSortCriteriaRepositoryMockValue(); - usersRepositoryMockValue.createNormalUser = new Error('DB error'); - const service = await makeUsersServiceMock( - usersRepositoryMockValue, - licensesRepositoryMockValue, - adb2cParam, - sendgridMockValue, - configMockValue, - sortCriteriaRepositoryMockValue, - ); - const name = 'test_user5'; - const role = USER_ROLES.TYPIST; - const email = 'test5@example.co.jp'; - const autoRenew = true; - const licenseAlert = true; - const notification = true; - const token: AccessToken = { userId: '0001', role: '', tier: 5 }; - await expect( - service.createUser( - token, - name, - role, - email, - autoRenew, - licenseAlert, - notification, - ), - ).rejects.toEqual( - new HttpException( - makeErrorResponse('E009999'), - HttpStatus.INTERNAL_SERVER_ERROR, - ), - ); - }); - it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。', async () => { - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const licensesRepositoryMockValue = null; - const adb2cParam = makeDefaultAdB2cMockValue(); - adb2cParam.createUser = new Error(); - const sendgridMockValue = makeDefaultSendGridlValue(); - const configMockValue = makeDefaultConfigValue(); - const sortCriteriaRepositoryMockValue = - makeDefaultSortCriteriaRepositoryMockValue(); - const service = await makeUsersServiceMock( - usersRepositoryMockValue, - licensesRepositoryMockValue, - adb2cParam, - sendgridMockValue, - configMockValue, - sortCriteriaRepositoryMockValue, - ); - const name = 'test_user6'; - const role = USER_ROLES.TYPIST; - const email = 'test6@example.co.jp'; - const autoRenew = true; - const licenseAlert = true; - const notification = true; - const token: AccessToken = { userId: '0001', role: '', tier: 5 }; - await expect( - service.createUser( - token, - name, - role, - email, - autoRenew, - licenseAlert, - notification, - ), - ).rejects.toEqual( - new HttpException( - makeErrorResponse('E009999'), - HttpStatus.INTERNAL_SERVER_ERROR, - ), - ); - }); - it('メールアドレスが重複している場合、エラーとなる。', async () => { - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const licensesRepositoryMockValue = null; - const adb2cParam = makeDefaultAdB2cMockValue(); - adb2cParam.createUser = { reason: 'email', message: 'ObjectConflict' }; - const sendgridMockValue = makeDefaultSendGridlValue(); - const configMockValue = makeDefaultConfigValue(); - const sortCriteriaRepositoryMockValue = - makeDefaultSortCriteriaRepositoryMockValue(); - const service = await makeUsersServiceMock( - usersRepositoryMockValue, - licensesRepositoryMockValue, - adb2cParam, - sendgridMockValue, - configMockValue, - sortCriteriaRepositoryMockValue, - ); - const name = 'test_user7'; - const role = USER_ROLES.TYPIST; - const email = 'test7@example.co.jp'; - const autoRenew = true; - const licenseAlert = true; - const notification = true; - const token: AccessToken = { userId: '0001', role: '', tier: 5 }; - await expect( - service.createUser( - token, - name, - role, - email, - autoRenew, - licenseAlert, - notification, - ), - ).rejects.toEqual( - new HttpException(makeErrorResponse('E010301'), HttpStatus.BAD_REQUEST), - ); - }); - it('AuthorIDが重複している場合、エラーとなる。(AuthorID重複チェックでエラー)', async () => { - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const licensesRepositoryMockValue = null; - const adb2cParam = makeDefaultAdB2cMockValue(); - const sendgridMockValue = makeDefaultSendGridlValue(); - const configMockValue = makeDefaultConfigValue(); - const sortCriteriaRepositoryMockValue = - makeDefaultSortCriteriaRepositoryMockValue(); - usersRepositoryMockValue.createNormalUser = new Error(); - usersRepositoryMockValue.existsAuthorId = true; - - const service = await makeUsersServiceMock( - usersRepositoryMockValue, - licensesRepositoryMockValue, - adb2cParam, - sendgridMockValue, - configMockValue, - sortCriteriaRepositoryMockValue, - ); - const name = 'test_user8'; - const role = USER_ROLES.AUTHOR; - const email = 'test8@example.co.jp'; - const autoRenew = true; - const licenseAlert = true; - const notification = true; - const authorId = 'testID'; - const token: AccessToken = { userId: '0001', role: '', tier: 5 }; - await expect( - service.createUser( - token, - name, - role, - email, - autoRenew, - licenseAlert, - notification, - authorId, - ), - ).rejects.toEqual( - new HttpException(makeErrorResponse('E010302'), HttpStatus.BAD_REQUEST), - ); - }); - it('AuthorIDが重複している場合、エラーとなる。(insert失敗)', async () => { - const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue(); - const licensesRepositoryMockValue = null; - const adb2cParam = makeDefaultAdB2cMockValue(); - const sendgridMockValue = makeDefaultSendGridlValue(); - const configMockValue = makeDefaultConfigValue(); - const sortCriteriaRepositoryMockValue = - makeDefaultSortCriteriaRepositoryMockValue(); - usersRepositoryMockValue.createNormalUser = new Error(); - usersRepositoryMockValue.createNormalUser.name = 'ER_DUP_ENTRY'; - - const service = await makeUsersServiceMock( - usersRepositoryMockValue, - licensesRepositoryMockValue, - adb2cParam, - sendgridMockValue, - configMockValue, - sortCriteriaRepositoryMockValue, - ); - const name = 'test_user9'; - const role = USER_ROLES.AUTHOR; - const email = 'test9@example.co.jp'; - const autoRenew = true; - const licenseAlert = true; - const notification = true; - const authorId = 'testID'; - const token: AccessToken = { userId: '0001', role: '', tier: 5 }; - await expect( - service.createUser( - token, - name, - role, - email, - autoRenew, - licenseAlert, - notification, - authorId, - ), - ).rejects.toEqual( - new HttpException(makeErrorResponse('E010302'), HttpStatus.BAD_REQUEST), - ); + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); + expect(e.getResponse()).toEqual(makeErrorResponse('E010302')); + } else { + fail(); + } + } }); }); diff --git a/dictation_server/src/repositories/users/entity/user.entity.ts b/dictation_server/src/repositories/users/entity/user.entity.ts index 7ed3f88..51cc19c 100644 --- a/dictation_server/src/repositories/users/entity/user.entity.ts +++ b/dictation_server/src/repositories/users/entity/user.entity.ts @@ -45,13 +45,13 @@ export class User { @Column({ default: true }) notification: boolean; - @Column({ nullable: true }) + @Column({ default: false }) encryption?: boolean; @Column({ nullable: true }) encryption_password?: string; - @Column({ nullable: true }) + @Column({ default: false }) prompt?: boolean; @Column({ nullable: true })