import { v4 as uuidv4 } from "uuid"; import { DataSource, In } from "typeorm"; import { User, UserArchive } from "../../entity/user.entity"; import { Account, AccountArchive } from "../../entity/account.entity"; import { ADMIN_ROLES, TASK_STATUS, USER_ROLES } from "../../constants"; import { License, LicenseAllocationHistory, LicenseArchive, LicenseAllocationHistoryArchive, } from "../../entity/license.entity"; import { bigintTransformer } from "../../common/entity"; import { Task } from "../../entity/task.entity"; import { AudioFile } from "../../entity/audio_file.entity"; import { AudioOptionItem } from "../../entity/audio_option_item.entity"; import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"; type InitialTestDBState = { tier1Accounts: { account: Account; users: User[] }[]; tier2Accounts: { account: Account; users: User[] }[]; tier3Accounts: { account: Account; users: User[] }[]; tier4Accounts: { account: Account; users: User[] }[]; tier5Accounts: { account: Account; users: User[] }[]; }; // 上書きされたら困る項目を除外したAccount型 type OverrideAccount = Omit< Account, "id" | "primary_admin_user_id" | "secondary_admin_user_id" | "user" >; // 上書きされたら困る項目を除外したUser型 type OverrideUser = Omit< User, "id" | "account" | "license" | "userGroupMembers" >; type OverrideAccountArchive = Omit< AccountArchive, "id" | "primary_admin_user_id" | "secondary_admin_user_id" | "user" >; // 上書きされたら困る項目を除外したUser型 type OverrideUserArchive = Omit< UserArchive, "id" | "account" | "license" | "userGroupMembers" >; type OverrideTask = Omit; type AccountDefault = { [K in keyof OverrideAccount]?: OverrideAccount[K] }; type UserDefault = { [K in keyof OverrideUser]?: OverrideUser[K] }; type AccountArchiveDefault = { [K in keyof OverrideAccountArchive]?: OverrideAccountArchive[K]; }; type UserArchiveDefault = { [K in keyof OverrideUserArchive]?: OverrideUserArchive[K]; }; type TaskDefault = { [K in keyof OverrideTask]?: OverrideTask[K] }; /** * テスト ユーティリティ: 指定したプロパティを上書きしたユーザーを作成する * @param dataSource データソース * @param defaultUserValue User型と同等かつoptionalなプロパティを持つ上書き箇所指定用オブジェクト * @returns 作成したユーザー */ export const makeTestUser = async ( datasource: DataSource, defaultUserValue?: UserDefault ): Promise => { const d = defaultUserValue; const { identifiers } = await datasource.getRepository(User).insert({ account_id: d?.account_id ?? -1, external_id: d?.external_id ?? uuidv4(), role: d?.role ?? `${ADMIN_ROLES.STANDARD} ${USER_ROLES.NONE}`, author_id: d?.author_id, accepted_eula_version: d?.accepted_eula_version ?? "1.0", accepted_dpa_version: d?.accepted_dpa_version ?? "1.0", email_verified: d?.email_verified ?? true, auto_renew: d?.auto_renew ?? true, notification: d?.notification ?? true, encryption: d?.encryption ?? true, prompt: d?.prompt ?? true, created_by: d?.created_by ?? "test_runner", created_at: d?.created_at ?? new Date(), updated_by: d?.updated_by ?? "updater", updated_at: d?.updated_at ?? new Date(), }); const result = identifiers.pop() as User; const user = await datasource.getRepository(User).findOne({ where: { id: result.id, }, }); if (!user) { throw new Error("Unexpected null"); } return user; }; /** * テスト ユーティリティ: 指定したプロパティを上書きしたアカウントとその管理者ユーザーを作成する * @param dataSource データソース * @param defaultUserValue Account型と同等かつoptionalなプロパティを持つ上書き箇所指定用オブジェクト * @param defaultAdminUserValue User型と同等かつoptionalなプロパティを持つ上書き箇所指定用オブジェクト(account_id等の所属関係が破壊される上書きは無視する) * @returns 作成したアカウント */ export const makeTestAccount = async ( datasource: DataSource, defaultAccountValue?: AccountDefault, defaultAdminUserValue?: UserDefault, isPrimaryAdminNotExist?: boolean, isSecondaryAdminNotExist?: boolean ): Promise<{ account: Account; admin: User }> => { let accountId: number; let userId: number; { const d = defaultAccountValue; const { identifiers } = await datasource.getRepository(Account).insert({ tier: d?.tier ?? 1, parent_account_id: d?.parent_account_id ?? undefined, country: d?.country ?? "US", delegation_permission: d?.delegation_permission ?? false, locked: d?.locked ?? false, company_name: d?.company_name ?? "test inc.", verified: d?.verified ?? true, auto_file_delete: d?.auto_file_delete ?? false, file_retention_days: d?.file_retention_days ?? 30, deleted_at: d?.deleted_at ?? null, created_by: d?.created_by ?? "test_runner", created_at: d?.created_at ?? new Date(), updated_by: d?.updated_by ?? "updater", updated_at: d?.updated_at ?? new Date(), }); const result = identifiers.pop() as Account; accountId = bigintTransformer.from(result.id); } { const d = defaultAdminUserValue; const { identifiers } = await datasource.getRepository(User).insert({ external_id: d?.external_id ?? uuidv4(), account_id: accountId, role: d?.role ?? "none", author_id: d?.author_id ?? undefined, accepted_eula_version: d?.accepted_eula_version !== undefined ? d.accepted_eula_version : "1.0", accepted_privacy_notice_version: d?.accepted_privacy_notice_version !== undefined ? d.accepted_privacy_notice_version : "1.0", accepted_dpa_version: d?.accepted_dpa_version !== undefined ? d.accepted_dpa_version : "1.0", email_verified: d?.email_verified ?? true, auto_renew: d?.auto_renew ?? true, notification: d?.notification ?? true, encryption: d?.encryption ?? true, prompt: d?.prompt ?? true, deleted_at: d?.deleted_at ?? "", created_by: d?.created_by ?? "test_runner", created_at: d?.created_at ?? new Date(), updated_by: d?.updated_by ?? "updater", updated_at: d?.updated_at ?? new Date(), }); const result = identifiers.pop() as User; userId = bigintTransformer.from(result.id); } // Accountの管理者を設定する let secondaryAdminUserId: number | null = null; if (isPrimaryAdminNotExist && !isSecondaryAdminNotExist) { secondaryAdminUserId = userId; } await datasource.getRepository(Account).update( { id: accountId }, { primary_admin_user_id: isPrimaryAdminNotExist ? null : userId, secondary_admin_user_id: secondaryAdminUserId, } ); const account = await datasource.getRepository(Account).findOne({ where: { id: accountId, }, }); const admin = await datasource.getRepository(User).findOne({ where: { id: userId, }, }); if (!account || !admin) { throw new Error("Unexpected null"); } return { account: account, admin: admin, }; }; export const createLicense = async ( datasource: DataSource, licenseId: number, expiry_date: Date | null, accountId: number, type: string, status: string, allocated_user_id: number | null, order_id: number | null, deleted_at: Date | null, delete_order_id: number | null, created_at?: Date ): Promise => { const { identifiers } = await datasource.getRepository(License).insert({ id: licenseId, expiry_date: expiry_date, account_id: accountId, type: type, status: status, allocated_user_id: allocated_user_id, order_id: order_id, deleted_at: deleted_at, delete_order_id: delete_order_id, created_by: "test_runner", created_at: created_at ? created_at : new Date(), updated_by: "updater", updated_at: new Date(), }); identifiers.pop() as License; }; export const createAndAllocateLicense = async ( datasource: DataSource, licenseId: number, expiry_date: Date | null, accountId: number, type: string, status: string, allocated_user_id: number | null, order_id: number | null, deleted_at: Date | null, delete_order_id: number | null, created_at?: Date ): Promise => { const { identifiers } = await datasource.getRepository(License).insert({ id: licenseId, expiry_date: expiry_date, account_id: accountId, type: type, status: status, allocated_user_id: allocated_user_id, order_id: order_id, deleted_at: deleted_at, delete_order_id: delete_order_id, created_by: "test_runner", created_at: created_at ? created_at : new Date(), updated_by: "updater", updated_at: new Date(), }); identifiers.pop() as License; // 割り当てるユーザーがいない場合は履歴を作成しない if (!allocated_user_id) { return; } // switch_from_typeを作成 // typeが"CARD"の場合は"CARD","TRIAL"の場合は"TRIAL","NORMAL"の場合は"NONE"を設定 const switch_from_type = type === "CARD" ? "CARD" : type === "TRIAL" ? "TRIAL" : "NONE"; // ライセンスの割り当て履歴を作成 await datasource.getRepository(LicenseAllocationHistory).insert({ license_id: licenseId, account_id: accountId, user_id: allocated_user_id ?? -1, is_allocated: true, switch_from_type: switch_from_type, executed_at: new Date(), created_by: "test_runner", created_at: new Date(), updated_by: "updater", updated_at: new Date(), }); }; export const createLicenseAllocationHistory = async ( datasource: DataSource, id: number, user_id: number, license_id: number, is_allocated: boolean, account_id: number, executed_at: Date, switch_from_type: string ): Promise => { const { identifiers } = await datasource .getRepository(LicenseAllocationHistory) .insert({ id: id, user_id: user_id, license_id: license_id, is_allocated: is_allocated, account_id: account_id, executed_at: executed_at, switch_from_type: switch_from_type, created_by: "test_runner", created_at: new Date(), updated_by: "updater", updated_at: new Date(), }); identifiers.pop() as LicenseAllocationHistory; }; export const selectLicenseByAllocatedUser = async ( datasource: DataSource, userId: number ): Promise<{ license: License | null }> => { const license = await datasource.getRepository(License).findOne({ where: { allocated_user_id: userId, }, }); return { license }; }; export const selectLicenseAllocationHistory = async ( datasource: DataSource, userId: number, licence_id: number ): Promise<{ licenseAllocationHistory: LicenseAllocationHistory | null }> => { const licenseAllocationHistory = await datasource .getRepository(LicenseAllocationHistory) .findOne({ where: { user_id: userId, license_id: licence_id, }, order: { executed_at: "DESC", }, }); return { licenseAllocationHistory }; }; /** * テスト ユーティリティ: 指定したプロパティを上書きしたアーカイブユーザーを作成する * @param dataSource データソース * @param defaultUserValue User型と同等かつoptionalなプロパティを持つ上書き箇所指定用オブジェクト * @returns 作成したユーザー */ export const makeTestUserArchive = async ( datasource: DataSource, defaultUserValue?: UserArchiveDefault ): Promise => { const d = defaultUserValue; const { identifiers } = await datasource.getRepository(UserArchive).insert({ account_id: d?.account_id ?? -1, external_id: d?.external_id ?? uuidv4(), role: d?.role ?? `${ADMIN_ROLES.STANDARD} ${USER_ROLES.NONE}`, author_id: d?.author_id, accepted_eula_version: d?.accepted_eula_version ?? "1.0", accepted_dpa_version: d?.accepted_dpa_version ?? "1.0", email_verified: d?.email_verified ?? true, auto_renew: d?.auto_renew ?? true, notification: d?.notification ?? true, encryption: d?.encryption ?? true, prompt: d?.prompt ?? true, created_by: d?.created_by ?? "test_runner", created_at: d?.created_at ?? new Date(), updated_by: d?.updated_by ?? "updater", updated_at: d?.updated_at ?? new Date(), }); const result = identifiers.pop() as User; const userArchive = await datasource.getRepository(UserArchive).findOne({ where: { id: result.id, }, }); if (!userArchive) { throw new Error("Unexpected null"); } return userArchive; }; /** * テスト ユーティリティ: 指定したプロパティを上書きしたアカウントとその管理者ユーザーを作成する * @param dataSource データソース * @param defaultUserValue Account型と同等かつoptionalなプロパティを持つ上書き箇所指定用オブジェクト * @param defaultAdminUserValue User型と同等かつoptionalなプロパティを持つ上書き箇所指定用オブジェクト(account_id等の所属関係が破壊される上書きは無視する) * @returns 作成したアカウント */ export const makeTestAccountArchive = async ( datasource: DataSource, defaultAccountValue?: AccountArchiveDefault, defaultAdminUserValue?: UserArchiveDefault, isPrimaryAdminNotExist?: boolean, isSecondaryAdminNotExist?: boolean ): Promise<{ account: AccountArchive; admin: UserArchive }> => { let accountId: number; let userId: number; { const d = defaultAccountValue; const { identifiers } = await datasource .getRepository(AccountArchive) .insert({ tier: d?.tier ?? 1, parent_account_id: d?.parent_account_id ?? undefined, country: d?.country ?? "US", delegation_permission: d?.delegation_permission ?? false, locked: d?.locked ?? false, verified: d?.verified ?? true, deleted_at: d?.deleted_at ?? "", created_by: d?.created_by ?? "test_runner", created_at: d?.created_at ?? new Date(), updated_by: d?.updated_by ?? "updater", updated_at: d?.updated_at ?? new Date(), }); const result = identifiers.pop() as AccountArchive; accountId = result.id; } { const d = defaultAdminUserValue; const { identifiers } = await datasource.getRepository(UserArchive).insert({ external_id: d?.external_id ?? uuidv4(), account_id: accountId, role: d?.role ?? "admin none", author_id: d?.author_id ?? undefined, accepted_eula_version: d?.accepted_eula_version ?? "1.0", accepted_dpa_version: d?.accepted_dpa_version ?? "1.0", email_verified: d?.email_verified ?? true, auto_renew: d?.auto_renew ?? true, notification: d?.notification ?? true, encryption: d?.encryption ?? true, prompt: d?.prompt ?? true, deleted_at: d?.deleted_at ?? "", created_by: d?.created_by ?? "test_runner", created_at: d?.created_at ?? new Date(), updated_by: d?.updated_by ?? "updater", updated_at: d?.updated_at ?? new Date(), }); const result = identifiers.pop() as UserArchive; userId = result.id; } // Accountの管理者を設定する let secondaryAdminUserId: number | null = null; if (isPrimaryAdminNotExist && !isSecondaryAdminNotExist) { secondaryAdminUserId = userId; } await datasource.getRepository(AccountArchive).update( { id: accountId }, { primary_admin_user_id: isPrimaryAdminNotExist ? null : userId, secondary_admin_user_id: secondaryAdminUserId, } ); const account = await datasource.getRepository(AccountArchive).findOne({ where: { id: accountId, }, }); const admin = await datasource.getRepository(UserArchive).findOne({ where: { id: userId, }, }); if (!account || !admin) { throw new Error("Unexpected null"); } return { account: account, admin: admin, }; }; export const createLicenseArchive = async ( datasource: DataSource, licenseId: number, expiry_date: Date | null, accountId: number, type: string, status: string, allocated_user_id: number | null, order_id: number | null, deleted_at: Date | null, delete_order_id: number | null, created_at?: Date ): Promise => { const { identifiers } = await datasource .getRepository(LicenseArchive) .insert({ id: licenseId, expiry_date: expiry_date, account_id: accountId, type: type, status: status, allocated_user_id: allocated_user_id, order_id: order_id, deleted_at: deleted_at, delete_order_id: delete_order_id, created_by: "test_runner", created_at: created_at ? created_at : new Date(), updated_by: "updater", updated_at: new Date(), }); identifiers.pop() as LicenseArchive; }; export const createLicenseAllocationHistoryArchive = async ( datasource: DataSource, id: number, user_id: number, license_id: number, is_allocated: boolean, account_id: number, executed_at: Date, switch_from_type: string ): Promise => { const { identifiers } = await datasource .getRepository(LicenseAllocationHistoryArchive) .insert({ id: id, user_id: user_id, license_id: license_id, is_allocated: is_allocated, account_id: account_id, executed_at: executed_at, switch_from_type: switch_from_type, created_by: "test_runner", created_at: new Date(), updated_by: "updater", updated_at: new Date(), }); identifiers.pop() as LicenseAllocationHistoryArchive; }; export const makeTestTask = async ( datasource: DataSource, accountId: number, ownerUserId: number, identifierName: string, defaultTaskValue?: TaskDefault ): Promise<{ task: Task; file: AudioFile; options: AudioOptionItem[] }> => { const d = defaultTaskValue; // AudioFileを作成 const { identifiers: identifiers1 } = await datasource .getRepository(AudioFile) .insert({ account_id: accountId, owner_user_id: ownerUserId, url: `https://example.com/${identifierName}`, file_name: `test${identifierName}.wav`, raw_file_name: `test${identifierName}.wav`, author_id: "test_author", work_type_id: "test_work_type", started_at: new Date(), duration: 0, finished_at: new Date(), uploaded_at: new Date(), file_size: 1024, priority: "01", audio_format: "wav", comment: `test_comment_${identifierName}`, deleted_at: new Date(), is_encrypted: false, }); const result = identifiers1.pop() as AudioFile; const audioFileId = bigintTransformer.from(result.id); // AudioFileを取得 const file = await datasource.getRepository(AudioFile).findOne({ where: { id: audioFileId, }, }); if (!file) { throw new Error("Unexpected null"); } // Taskを作成 const { identifiers: identifiers2 } = await datasource .getRepository(Task) .insert({ job_number: d?.job_number ?? "0001", account_id: accountId, is_job_number_enabled: d?.is_job_number_enabled ?? true, audio_file_id: file.id, status: d?.status ?? "Uploaded", typist_user_id: d?.typist_user_id, priority: d?.priority ?? "01", template_file_id: d?.template_file_id, started_at: d?.started_at ?? new Date(), finished_at: d?.finished_at ?? new Date(), created_by: d?.created_by ?? "test_runner", created_at: d?.created_at ?? new Date(), updated_by: d?.updated_by ?? "updater", updated_at: d?.updated_at ?? new Date(), }); const result2 = identifiers2.pop() as Task; const taskId = bigintTransformer.from(result2.id); const task = await datasource.getRepository(Task).findOne({ where: { id: taskId, }, }); if (!task) { throw new Error("Unexpected null"); } // AudioOptionItemを作成 const item01 = await datasource.getRepository(AudioOptionItem).insert({ audio_file_id: audioFileId, label: `test_option_label_${identifierName}_01`, value: `test_option_value_${identifierName}_01`, }); const item02 = await datasource.getRepository(AudioOptionItem).insert({ audio_file_id: audioFileId, label: `test_option_label_${identifierName}_02`, value: `test_option_value_${identifierName}_02`, }); const optionItemResult01 = item01.identifiers.pop() as AudioOptionItem; const optionItemResult02 = item02.identifiers.pop() as AudioOptionItem; const optionItemID01 = bigintTransformer.from(optionItemResult01.id); const optionItemID02 = bigintTransformer.from(optionItemResult02.id); const optionItems = await datasource.getRepository(AudioOptionItem).find({ where: { id: In([optionItemID01, optionItemID02]), }, }); return { task, file, options: optionItems }; }; export const makeManyTestTasks = async ( datasource: DataSource, inputFiles: QueryDeepPartialEntity[], task_finished_at: Date ): Promise => { const fileRepository = datasource.getRepository(AudioFile); const result = await fileRepository.insert(inputFiles); const audioFileIds = result.identifiers.map((id) => id.id); const files = await fileRepository.find({ where: { id: In(audioFileIds), }, }); const tasks = files.map((file, index): QueryDeepPartialEntity => { return { job_number: `0001_${index}`, account_id: file.account_id, is_job_number_enabled: true, audio_file_id: file.id, status: TASK_STATUS.FINISHED, typist_user_id: null, priority: "01", template_file_id: null, started_at: new Date(), finished_at: task_finished_at, created_by: "test_runner", created_at: new Date(), updated_by: "updater", updated_at: new Date(), }; }); const taskRepository = datasource.getRepository(Task); const x = await taskRepository.insert(tasks); const partialOptions = files.flatMap( (file, index): QueryDeepPartialEntity[] => { return [ { audio_file_id: file.id, label: `test_option_label_${index}_01`, value: `test_option_value_${index}_01`, }, { audio_file_id: file.id, label: `test_option_label_${index}_02`, value: `test_option_value_${index}_02`, }, ]; } ); const optionRepository = datasource.getRepository(AudioOptionItem); await optionRepository.insert(partialOptions); }; export const getTasks = async (datasource: DataSource): Promise => { return await datasource.getRepository(Task).find(); }; export const getAudioFiles = async ( datasource: DataSource ): Promise => { return await datasource.getRepository(AudioFile).find(); }; export const getAudioOptionItems = async ( datasource: DataSource ): Promise => { return await datasource.getRepository(AudioOptionItem).find(); };