金村 勇祐 2ded5b8498 Merged PR 1058: File Lengthのソートが正しく行われない_Funtion側修正漏れの修正
Funtion側もデータ型の変更に合わせる修正をいたしました。

動作確認状況
```
Test Suites: 8 passed, 8 total
Tests:       46 passed, 46 total
Snapshots:   0 total
Time:        85.851 s
Ran all test suites.
```
2025-04-23 02:57:38 +00:00

719 lines
23 KiB
TypeScript

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<Task, "id">;
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<User> => {
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<void> => {
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<void> => {
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<void> => {
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<UserArchive> => {
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<void> => {
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<void> => {
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<AudioFile>[],
task_finished_at: Date
): Promise<void> => {
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<Task> => {
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<AudioOptionItem>[] => {
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<Task[]> => {
return await datasource.getRepository(Task).find();
};
export const getAudioFiles = async (
datasource: DataSource
): Promise<AudioFile[]> => {
return await datasource.getRepository(AudioFile).find();
};
export const getAudioOptionItems = async (
datasource: DataSource
): Promise<AudioOptionItem[]> => {
return await datasource.getRepository(AudioOptionItem).find();
};