diff --git a/dictation_function/src/test/common/context.ts b/dictation_function/src/test/common/context.ts new file mode 100644 index 0000000..7d2f017 --- /dev/null +++ b/dictation_function/src/test/common/context.ts @@ -0,0 +1,16 @@ +import { InvocationContext } from "@azure/functions"; + +export class TestInvocationContext extends InvocationContext { + contents: string[] = []; + getLogs(): string[] { + return this.contents; + } + log(...args: any[]): void { + super.log(args); + this.contents.push(args.toString()); + } + error(...args: any[]): void { + super.error(args); + this.contents.push(args.toString()); + } +} diff --git a/dictation_function/src/test/deleteAudioFiles.spec.ts b/dictation_function/src/test/deleteAudioFiles.spec.ts index f165e4b..a6f2ad7 100644 --- a/dictation_function/src/test/deleteAudioFiles.spec.ts +++ b/dictation_function/src/test/deleteAudioFiles.spec.ts @@ -15,7 +15,7 @@ import { makeTestAccount, makeTestTask, } from "./common/utility"; -import { TASK_STATUS } from "../constants"; +import { MANUAL_RECOVERY_REQUIRED, TASK_STATUS } from "../constants"; import { TestLogger } from "./common/logger"; import { AudioFile } from "../entity/audio_file.entity"; import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"; @@ -30,6 +30,7 @@ import { LicenseArchive, } from "../entity/license.entity"; import { AudioOptionItem } from "../entity/audio_option_item.entity"; +import { TestInvocationContext } from "./common/context"; describe("getProcessTargets | 削除対象を特定するQueryが正常に動作するか確認する", () => { let source: DataSource | null = null; @@ -1493,4 +1494,136 @@ describe("deleteAudioFilesProcessing", () => { expect(optionItems.length).toEqual(4); } }); + + it("Blobの削除でエラーが発生した場合、アラーム発報用のログが出力される", async () => { + if (!source) fail(); + + // 2024/02/29 00:00:00を"今"とする + const now = new Date("2024-02-29T00:00:00Z"); + + const { account: account01, admin: admin01 } = await makeTestAccount( + source, + { + file_retention_days: 2, + auto_file_delete: true, + } + ); + + // 2日と1秒前(削除対象)のタスクを作成 + const { file: file1 } = await makeTestTask( + source, + account01.id, + admin01.id, + "case03", + { + status: TASK_STATUS.FINISHED, + job_number: "job03", + finished_at: new Date("2024-02-26T23:59:59Z"), + } + ); + + const args: { accountId: number; fileName: string; country: string }[] = []; + const blobstorage = new AudioBlobStorageService(); + Object.defineProperty(blobstorage, blobstorage.deleteFile.name, { + value: async ( + context: InvocationContext, + accountId: number, + country: string, + fileName: string + ): Promise => { + throw new Error( + `delete blob failed. succeeded: ${false}, errorCode: ${"ERROR_CODE"}, date: ${"DATE"}` + ); + }, + writable: true, + }); + + { + // DB全体のレコードを確認 + const tasks = await getTasks(source); + expect(tasks.length).toEqual(1); + const files = await getAudioFiles(source); + expect(files.length).toEqual(1); + const optionItems = await getAudioOptionItems(source); + expect(optionItems.length).toEqual(2); + } + + const context = new TestInvocationContext(); + await deleteAudioFilesProcessing(context, source, blobstorage, now); + + const log = context + .getLogs() + .find((log) => log.includes(MANUAL_RECOVERY_REQUIRED)); + expect(log).toBeDefined(); + expect(log).toEqual( + `[MANUAL_RECOVERY_REQUIRED] file delete failed. target={"id":"1","audio_file_id":"1","account_id":"1","country":"US","file_name":"testcase03.wav"}` + ); + }); + + it("DBの削除でエラーが発生した場合、アラーム発報用のログが出力される", async () => { + if (!source) fail(); + + // 2024/02/29 00:00:00を"今"とする + const now = new Date("2024-02-29T00:00:00Z"); + + const { account: account01, admin: admin01 } = await makeTestAccount( + source, + { + file_retention_days: 2, + auto_file_delete: true, + } + ); + + // 2日と1秒前(削除対象)のタスクを作成 + await makeTestTask(source, account01.id, admin01.id, "case03", { + status: TASK_STATUS.FINISHED, + job_number: "job03", + finished_at: new Date("2024-02-26T23:59:59Z"), + }); + + const args: { accountId: number; fileName: string; country: string }[] = []; + const blobstorage = new AudioBlobStorageService(); + Object.defineProperty(blobstorage, blobstorage.deleteFile.name, { + value: async ( + context: InvocationContext, + accountId: number, + country: string, + fileName: string + ): Promise => { + args.push({ accountId, country, fileName }); + }, + writable: true, + }); + + { + // DB全体のレコードを確認 + const tasks = await getTasks(source); + expect(tasks.length).toEqual(1); + const files = await getAudioFiles(source); + expect(files.length).toEqual(1); + const optionItems = await getAudioOptionItems(source); + expect(optionItems.length).toEqual(2); + } + + Object.defineProperty(source, "transaction", { + value: async () => { + throw new Error("transaction error"); + }, + writable: true, + }); + + const context = new TestInvocationContext(); + + await expect( + deleteAudioFilesProcessing(context, source, blobstorage, now) + ).rejects.toThrow(); + + const log = context + .getLogs() + .find((log) => log.includes(MANUAL_RECOVERY_REQUIRED)); + expect(log).toBeDefined(); + expect(log).toEqual( + `[MANUAL_RECOVERY_REQUIRED] Failed to execute auto file deletion function. error=Error: transaction error` + ); + }); });