From fd4e73e76c9c238619b824db32ea6b205c7949ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Krzeslak?= Date: Thu, 24 Jul 2025 11:24:12 +0200 Subject: [PATCH] test: add unit test for calibre.ts --- src/converters/calibre.ts | 33 ++++++++-------- src/converters/types.ts | 9 +++++ tests/converters/assimp.test.ts | 64 +++++--------------------------- tests/converters/calibre.test.ts | 19 ++++++++++ tests/helpers/converters.test.ts | 64 ++++++++++++++++++++++++++++++++ 5 files changed, 116 insertions(+), 73 deletions(-) create mode 100644 tests/converters/calibre.test.ts create mode 100644 tests/helpers/converters.test.ts diff --git a/src/converters/calibre.ts b/src/converters/calibre.ts index 8ebdbd1..153de16 100644 --- a/src/converters/calibre.ts +++ b/src/converters/calibre.ts @@ -1,4 +1,5 @@ -import { execFile } from "node:child_process"; +import { execFile as execFileOriginal } from "node:child_process"; +import { ExecFileFn } from "./types.ts"; export const properties = { from: { @@ -62,28 +63,24 @@ export async function convert( fileType: string, convertTo: string, targetPath: string, - // eslint-disable-next-line @typescript-eslint/no-unused-vars options?: unknown, + execFile: ExecFileFn = execFileOriginal, // to make it mockable ): Promise { return new Promise((resolve, reject) => { - execFile( - "ebook-convert", - [filePath, targetPath], - (error, stdout, stderr) => { - if (error) { - reject(`error: ${error}`); - } + execFile("ebook-convert", [filePath, targetPath], (error, stdout, stderr) => { + if (error) { + reject(`error: ${error}`); + } - if (stdout) { - console.log(`stdout: ${stdout}`); - } + if (stdout) { + console.log(`stdout: ${stdout}`); + } - if (stderr) { - console.error(`stderr: ${stderr}`); - } + if (stderr) { + console.error(`stderr: ${stderr}`); + } - resolve("Done"); - }, - ); + resolve("Done"); + }); }); } diff --git a/src/converters/types.ts b/src/converters/types.ts index a80a873..5d7dc9f 100644 --- a/src/converters/types.ts +++ b/src/converters/types.ts @@ -3,3 +3,12 @@ export type ExecFileFn = ( args: string[], callback: (err: Error | null, stdout: string, stderr: string) => void, ) => void; + +export type ConvertFnWithExecFile = ( + filePath: string, + fileType: string, + convertTo: string, + targetPath: string, + options: unknown, + execFileOverride?: ExecFileFn, +) => Promise; diff --git a/tests/converters/assimp.test.ts b/tests/converters/assimp.test.ts index 98024a0..5b9f82d 100644 --- a/tests/converters/assimp.test.ts +++ b/tests/converters/assimp.test.ts @@ -1,65 +1,19 @@ -import type { ExecFileException } from "node:child_process"; -import { expect, test } from "bun:test"; +import { test } from "bun:test"; import { convert } from "../../src/converters/assimp.ts"; -import { ExecFileFn } from "../../src/converters/types.ts"; +import { + runConvertFailTest, + runConvertLogsStderror, + runConvertSuccessTest, +} from "../helpers/converters.test.ts"; test("convert resolves when execFile succeeds", async () => { - const originalConsoleLog = console.log; - - let loggedMessage = ""; - console.log = (msg) => { - loggedMessage = msg; - }; - - const mockExecFile: ExecFileFn = ( - _cmd: string, - _args: string[], - callback: (err: ExecFileException | null, stdout: string, stderr: string) => void, - ) => { - callback(null, "Fake stdout", ""); - }; - - const result = await convert("input.obj", "obj", "stl", "output.stl", undefined, mockExecFile); - - console.log = originalConsoleLog; - - expect(result).toBe("Done"); - expect(loggedMessage).toBe("stdout: Fake stdout"); + await runConvertSuccessTest(convert); }); test("convert rejects when execFile fails", async () => { - const mockExecFile: ExecFileFn = ( - _cmd: string, - _args: string[], - callback: (err: ExecFileException | null, stdout: string, stderr: string) => void, - ) => { - callback(new Error("Test error"), "", ""); - }; - - expect(convert("input.obj", "obj", "stl", "output.stl", undefined, mockExecFile)).rejects.toMatch( - /error: Error: Test error/, - ); + await runConvertFailTest(convert); }); test("convert logs stderr when present", async () => { - const originalConsoleError = console.error; - - let loggedMessage = ""; - console.error = (msg) => { - loggedMessage = msg; - }; - - const mockExecFile = ( - _cmd: string, - _args: string[], - cb: (err: Error | null, stdout: string, stderr: string) => void, - ) => { - cb(null, "", "Fake stderr"); - }; - - await convert("file.obj", "obj", "stl", "out.stl", undefined, mockExecFile); - - console.error = originalConsoleError; - - expect(loggedMessage).toBe("stderr: Fake stderr"); + await runConvertLogsStderror(convert); }); diff --git a/tests/converters/calibre.test.ts b/tests/converters/calibre.test.ts new file mode 100644 index 0000000..b927bfe --- /dev/null +++ b/tests/converters/calibre.test.ts @@ -0,0 +1,19 @@ +import { test } from "bun:test"; +import { convert } from "../../src/converters/calibre.ts"; +import { + runConvertFailTest, + runConvertLogsStderror, + runConvertSuccessTest, +} from "../helpers/converters.test.ts"; + +test("convert resolves when execFile succeeds", async () => { + await runConvertSuccessTest(convert); +}); + +test("convert rejects when execFile fails", async () => { + await runConvertFailTest(convert); +}); + +test("convert logs stderr when present", async () => { + await runConvertLogsStderror(convert); +}); diff --git a/tests/helpers/converters.test.ts b/tests/helpers/converters.test.ts new file mode 100644 index 0000000..f533d8a --- /dev/null +++ b/tests/helpers/converters.test.ts @@ -0,0 +1,64 @@ +import type { ExecFileException } from "node:child_process"; +import { expect } from "bun:test"; +import { ConvertFnWithExecFile, ExecFileFn } from "../../src/converters/types.ts"; + +export async function runConvertSuccessTest(convertFn: ConvertFnWithExecFile) { + const originalConsoleLog = console.log; + + let loggedMessage = ""; + console.log = (msg) => { + loggedMessage = msg; + }; + + const mockExecFile: ExecFileFn = ( + _cmd: string, + _args: string[], + callback: (err: ExecFileException | null, stdout: string, stderr: string) => void, + ) => { + callback(null, "Fake stdout", ""); + }; + + const result = await convertFn("input.obj", "obj", "stl", "output.stl", undefined, mockExecFile); + + console.log = originalConsoleLog; + + expect(result).toBe("Done"); + expect(loggedMessage).toBe("stdout: Fake stdout"); +} + +export async function runConvertFailTest(convertFn: ConvertFnWithExecFile) { + const mockExecFile: ExecFileFn = ( + _cmd: string, + _args: string[], + callback: (err: ExecFileException | null, stdout: string, stderr: string) => void, + ) => { + callback(new Error("Test error"), "", ""); + }; + + expect( + convertFn("input.obj", "obj", "stl", "output.stl", undefined, mockExecFile), + ).rejects.toMatch(/error: Error: Test error/); +} + +export async function runConvertLogsStderror(convertFn: ConvertFnWithExecFile) { + const originalConsoleError = console.error; + + let loggedMessage = ""; + console.error = (msg) => { + loggedMessage = msg; + }; + + const mockExecFile = ( + _cmd: string, + _args: string[], + cb: (err: Error | null, stdout: string, stderr: string) => void, + ) => { + cb(null, "", "Fake stderr"); + }; + + await convertFn("file.obj", "obj", "stl", "out.stl", undefined, mockExecFile); + + console.error = originalConsoleError; + + expect(loggedMessage).toBe("stderr: Fake stderr"); +}