Merged PR 461: APIテスト実施
## 概要 [Task2672: APIテスト実施](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2672) - 元PBI or タスクへのリンク(内容・目的などはそちらにあるはず) - 何をどう変更したか、追加したライブラリなど - このPull Requestでの対象/対象外 詳細なレコード(ライセンス、タスク、ユーザーグループなど)は別途dev動作確認にてデータを用意して行います。 現時点では、各レコードの削除はMySQL用にmigrationファイルにて記述したON DELETE CASCADEの機能にて削除を行う為、SQLiteを用いた本ユニットテストでは動作確認対象外としています。 - 影響範囲(他の機能にも影響があるか) entityの定義(accounts - users)のON DELETE CASCADEを明記 ## レビューポイント - 本ユニットテストは正常系の動作確認と、それぞれのservice内部で異常発生時もAPI自体は正常終了し、[MANUAL_RECOVERY_REQUIRED]ログが表示されることの確認を主な目的として実装しています。 ## UIの変更 なし ## 動作確認状況 - ローカルで確認(ユニットテスト) ## 補足 - 相談、参考資料などがあれば
This commit is contained in:
parent
964077a480
commit
a8bacefc5f
@ -29,6 +29,7 @@ export const overrideAdB2cService = <TService>(
|
||||
username: string,
|
||||
) => Promise<{ sub: string } | ConflictError>;
|
||||
deleteUser?: (externalId: string, context: Context) => Promise<void>;
|
||||
deleteUsers?: (externalIds: string[], context: Context) => Promise<void>;
|
||||
getUsers?: (
|
||||
context: Context,
|
||||
externalIds: string[],
|
||||
@ -49,6 +50,12 @@ export const overrideAdB2cService = <TService>(
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
if (overrides.deleteUsers) {
|
||||
Object.defineProperty(obj, obj.deleteUsers.name, {
|
||||
value: overrides.deleteUsers,
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
if (overrides.getUsers) {
|
||||
Object.defineProperty(obj, obj.getUsers.name, {
|
||||
value: overrides.getUsers,
|
||||
@ -232,6 +239,7 @@ export const overrideAccountsRepositoryService = <TService>(
|
||||
adminUserAcceptedTermsVersion: string,
|
||||
) => Promise<{ newAccount: Account; adminUser: User }>;
|
||||
deleteAccount?: (accountId: number, userId: number) => Promise<void>;
|
||||
deleteAccountAndInsertArchives?: (accountId: number) => Promise<User[]>;
|
||||
},
|
||||
): void => {
|
||||
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
|
||||
@ -248,4 +256,10 @@ export const overrideAccountsRepositoryService = <TService>(
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
if (overrides.deleteAccountAndInsertArchives) {
|
||||
Object.defineProperty(obj, obj.deleteAccountAndInsertArchives.name, {
|
||||
value: overrides.deleteAccountAndInsertArchives,
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -34,6 +34,7 @@ import {
|
||||
getUsers,
|
||||
makeTestUser,
|
||||
makeHierarchicalAccounts,
|
||||
getUser,
|
||||
} from '../../common/test/utility';
|
||||
import { AccountsService } from './accounts.service';
|
||||
import { Context, makeContext } from '../../common/log';
|
||||
@ -5205,7 +5206,6 @@ describe('getAccountInfo', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAuthors', () => {
|
||||
let source: DataSource = null;
|
||||
beforeEach(async () => {
|
||||
@ -5312,3 +5312,231 @@ describe('getAuthors', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('deleteAccountAndData', () => {
|
||||
let source: DataSource = null;
|
||||
beforeEach(async () => {
|
||||
source = new DataSource({
|
||||
type: 'sqlite',
|
||||
database: ':memory:',
|
||||
logging: false,
|
||||
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
|
||||
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
|
||||
});
|
||||
return source.initialize();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await source.destroy();
|
||||
source = null;
|
||||
});
|
||||
it('アカウント情報が削除されること', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
// 第五階層のアカウント作成
|
||||
const 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);
|
||||
// 第五階層のアカウント作成
|
||||
const tier5Accounts = await makeTestAccount(source, {
|
||||
parent_account_id: account.id,
|
||||
tier: 5,
|
||||
});
|
||||
|
||||
// ユーザの作成
|
||||
const user = await makeTestUser(source, {
|
||||
account_id: tier5Accounts.account.id,
|
||||
});
|
||||
// ADB2Cユーザーの削除成功
|
||||
overrideAdB2cService(service, {
|
||||
deleteUsers: jest.fn(),
|
||||
});
|
||||
// blobstorageコンテナの削除成功
|
||||
overrideBlobstorageService(service, {
|
||||
deleteContainer: jest.fn(),
|
||||
});
|
||||
// アカウント情報の削除
|
||||
await service.deleteAccountAndData(
|
||||
context,
|
||||
tier5Accounts.admin.external_id,
|
||||
tier5Accounts.account.id,
|
||||
);
|
||||
|
||||
// DB内が想定通りになっているか確認
|
||||
const accountRecord = await getAccount(source, tier5Accounts.account.id);
|
||||
expect(accountRecord).toBe(null);
|
||||
|
||||
const userRecord = await getUser(source, user.id);
|
||||
expect(userRecord).toBe(null);
|
||||
});
|
||||
it('アカウントの削除に失敗した場合はエラーを返す', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
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);
|
||||
// 第五階層のアカウント作成
|
||||
const tier5Accounts = await makeTestAccount(source, {
|
||||
parent_account_id: account.id,
|
||||
tier: 5,
|
||||
});
|
||||
|
||||
// ユーザの作成
|
||||
const user = await makeTestUser(source, {
|
||||
account_id: tier5Accounts.account.id,
|
||||
});
|
||||
|
||||
// アカウント情報の削除失敗
|
||||
overrideAccountsRepositoryService(service, {
|
||||
deleteAccountAndInsertArchives: jest.fn().mockRejectedValue(new Error()),
|
||||
});
|
||||
|
||||
// ADB2Cユーザーの削除成功
|
||||
overrideAdB2cService(service, {
|
||||
deleteUsers: jest.fn(),
|
||||
});
|
||||
|
||||
// blobstorageコンテナの削除成功
|
||||
overrideBlobstorageService(service, {
|
||||
deleteContainer: jest.fn(),
|
||||
});
|
||||
|
||||
// アカウント情報の削除に失敗することを確認
|
||||
await expect(
|
||||
service.deleteAccountAndData(
|
||||
context,
|
||||
tier5Accounts.admin.external_id,
|
||||
tier5Accounts.account.id,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
),
|
||||
);
|
||||
|
||||
// loggerSpyがスパイしているlogger.logメソッドが出力したログを確認(目視確認用)
|
||||
const logs = loggerSpy.mock.calls.map((call) => call[0]);
|
||||
console.log(logs);
|
||||
|
||||
// DB内が削除されていないことを確認
|
||||
const accountRecord = await getAccount(source, tier5Accounts.account.id);
|
||||
expect(accountRecord.id).not.toBeNull();
|
||||
const userRecord = await getUser(source, user.id);
|
||||
expect(userRecord.id).not.toBeNull();
|
||||
});
|
||||
it('ADB2Cユーザーの削除失敗時は、MANUAL_RECOVERY_REQUIREDを出して処理続行', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
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);
|
||||
// 第五階層のアカウント作成
|
||||
const tier5Accounts = await makeTestAccount(source, {
|
||||
parent_account_id: account.id,
|
||||
tier: 5,
|
||||
});
|
||||
|
||||
// ユーザの作成
|
||||
const user = await makeTestUser(source, {
|
||||
account_id: tier5Accounts.account.id,
|
||||
});
|
||||
|
||||
// ADB2Cユーザーの削除失敗
|
||||
overrideAdB2cService(service, {
|
||||
deleteUsers: jest.fn().mockRejectedValue(new Error()),
|
||||
});
|
||||
|
||||
// blobstorageコンテナの削除成功
|
||||
overrideBlobstorageService(service, {
|
||||
deleteContainer: jest.fn(),
|
||||
});
|
||||
|
||||
// 処理自体は成功することを確認
|
||||
expect(
|
||||
await service.deleteAccountAndData(
|
||||
context,
|
||||
tier5Accounts.admin.external_id,
|
||||
tier5Accounts.account.id,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
|
||||
// loggerSpyがスパイしているlogger.logメソッドが出力したログを確認(目視確認用)
|
||||
const logs = loggerSpy.mock.calls.map((call) => call[0]);
|
||||
console.log(logs);
|
||||
|
||||
// DB内が想定通りになっているか確認
|
||||
const accountRecord = await getAccount(source, tier5Accounts.account.id);
|
||||
expect(accountRecord).toBe(null);
|
||||
const userRecord = await getUser(source, user.id);
|
||||
expect(userRecord).toBe(null);
|
||||
});
|
||||
it('blobstorageコンテナを削除で失敗した場合は、MANUAL_RECOVERY_REQUIRED出して正常終了', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
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);
|
||||
// 第五階層のアカウント作成
|
||||
const tier5Accounts = await makeTestAccount(source, {
|
||||
parent_account_id: account.id,
|
||||
tier: 5,
|
||||
});
|
||||
|
||||
// ユーザの作成
|
||||
const user = await makeTestUser(source, {
|
||||
account_id: tier5Accounts.account.id,
|
||||
});
|
||||
|
||||
// ADB2Cユーザーの削除成功
|
||||
overrideAdB2cService(service, {
|
||||
deleteUsers: jest.fn(),
|
||||
});
|
||||
|
||||
// blobstorageコンテナの削除失敗
|
||||
overrideBlobstorageService(service, {
|
||||
deleteContainer: jest.fn().mockRejectedValue(new Error()),
|
||||
});
|
||||
|
||||
// 処理自体は成功することを確認
|
||||
expect(
|
||||
await service.deleteAccountAndData(
|
||||
context,
|
||||
tier5Accounts.admin.external_id,
|
||||
tier5Accounts.account.id,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
|
||||
// loggerSpyがスパイしているlogger.logメソッドが出力したログを確認(目視確認用)
|
||||
const logs = loggerSpy.mock.calls.map((call) => call[0]);
|
||||
console.log(logs);
|
||||
|
||||
// DB内が想定通りになっているか確認
|
||||
const accountRecord = await getAccount(source, tier5Accounts.account.id);
|
||||
expect(accountRecord).toBe(null);
|
||||
const userRecord = await getUser(source, user.id);
|
||||
expect(userRecord).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
@ -916,7 +916,6 @@ export class AccountsRepositoryService {
|
||||
account_id: accountId,
|
||||
},
|
||||
});
|
||||
|
||||
const userArchiveRepo = entityManager.getRepository(UserArchive);
|
||||
await userArchiveRepo
|
||||
.createQueryBuilder()
|
||||
@ -924,7 +923,6 @@ export class AccountsRepositoryService {
|
||||
.into(UserArchive)
|
||||
.values(users)
|
||||
.execute();
|
||||
|
||||
// アカウントを削除
|
||||
// アカウントを削除することで、外部キー制約がで紐づいている関連テーブルのデータも削除される
|
||||
const accountRepo = entityManager.getRepository(Account);
|
||||
|
||||
@ -70,7 +70,7 @@ export class User {
|
||||
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
updated_at: Date;
|
||||
|
||||
@ManyToOne(() => Account, (account) => account.user)
|
||||
@ManyToOne(() => Account, (account) => account.user, { onDelete: 'CASCADE' }) // onDeleteはSQLite用設定値.本番用は別途migrationで設定
|
||||
@JoinColumn({ name: 'account_id' })
|
||||
account?: Account;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user