convertor/tests/converters/mineru.test.ts
Your Name f7ebc084ea refactor: update MinerU to use tar.gz and mineru CLI
- Change archive format from zip to tar.gz
- Use 'mineru' CLI command instead of 'magic-pdf'
- Install mineru[all] via pipx in Dockerfile
- Remove zip dependency from Dockerfile
- Update normalizeFiletype to output tar.gz extension
- Update all tests for tar.gz output format
- Simplify README MinerU section
2026-01-21 12:20:40 +08:00

240 lines
7.8 KiB
TypeScript

import { test, expect, describe, beforeEach, afterEach } from "bun:test";
import { convert, properties } from "../../src/converters/mineru";
import type { ExecFileException } from "node:child_process";
import { ExecFileFn } from "../../src/converters/types";
import { mkdirSync, existsSync, writeFileSync, rmSync } from "node:fs";
import { join } from "node:path";
// Skip common tests as MinerU has different behavior (archive output)
test.skip("dummy - required to trigger test detection", () => {});
describe("MinerU converter properties", () => {
test("should have correct input formats", () => {
expect(properties.from.document).toContain("pdf");
expect(properties.from.document).toContain("ppt");
expect(properties.from.document).toContain("pptx");
expect(properties.from.document).toContain("xls");
expect(properties.from.document).toContain("xlsx");
expect(properties.from.document).toContain("doc");
expect(properties.from.document).toContain("docx");
});
test("should have correct output formats", () => {
expect(properties.to.document).toContain("md-t");
expect(properties.to.document).toContain("md-i");
});
test("should have archive output mode", () => {
expect(properties.outputMode).toBe("archive");
});
});
describe("MinerU converter md-t mode", () => {
const testDir = "./test-output-mineru-t";
beforeEach(() => {
if (!existsSync(testDir)) {
mkdirSync(testDir, { recursive: true });
}
});
afterEach(() => {
if (existsSync(testDir)) {
rmSync(testDir, { recursive: true, force: true });
}
});
test("should call mineru with markdown table mode for md-t", async () => {
let mineruArgs: string[] = [];
const mockExecFile: ExecFileFn = (
cmd: string,
args: string[],
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
options?: any,
) => {
if (cmd === "mineru") {
mineruArgs = args;
// Simulate MinerU creating output
const outputDir = args[3]; // -o argument
if (outputDir && !existsSync(outputDir)) {
mkdirSync(outputDir, { recursive: true });
}
const autoDir = join(outputDir, "auto");
if (!existsSync(autoDir)) {
mkdirSync(autoDir, { recursive: true });
}
writeFileSync(join(autoDir, "output.md"), "# Test\n\n| Col1 | Col2 |\n|---|---|\n| A | B |");
callback(null, "MinerU conversion complete", "");
} else if (cmd === "tar") {
// Simulate tar.gz creation
callback(null, "Archive created", "");
}
};
const targetPath = join(testDir, "output.tar.gz");
await convert("test.pdf", "pdf", "md-t", targetPath, undefined, mockExecFile);
expect(mineruArgs).toContain("--table-mode");
expect(mineruArgs).toContain("markdown");
});
});
describe("MinerU converter md-i mode", () => {
const testDir = "./test-output-mineru-i";
beforeEach(() => {
if (!existsSync(testDir)) {
mkdirSync(testDir, { recursive: true });
}
});
afterEach(() => {
if (existsSync(testDir)) {
rmSync(testDir, { recursive: true, force: true });
}
});
test("should call mineru with image table mode for md-i", async () => {
let capturedArgs: string[] = [];
const mockExecFile: ExecFileFn = (
cmd: string,
args: string[],
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
options?: any,
) => {
if (cmd === "mineru") {
capturedArgs = args;
// Simulate MinerU creating output
const outputDir = args[3]; // -o argument
if (outputDir && !existsSync(outputDir)) {
mkdirSync(outputDir, { recursive: true });
}
const autoDir = join(outputDir, "auto");
if (!existsSync(autoDir)) {
mkdirSync(autoDir, { recursive: true });
}
writeFileSync(join(autoDir, "output.md"), "# Test\n\n![Table](images/table_1.png)");
callback(null, "MinerU conversion complete", "");
} else if (cmd === "tar") {
// Simulate tar.gz creation
callback(null, "Archive created", "");
}
};
const targetPath = join(testDir, "output.tar.gz");
await convert("test.pdf", "pdf", "md-i", targetPath, undefined, mockExecFile);
expect(capturedArgs).toContain("--table-mode");
expect(capturedArgs).toContain("image");
});
});
describe("MinerU converter tar.gz output", () => {
const testDir = "./test-output-mineru-targz";
beforeEach(() => {
if (!existsSync(testDir)) {
mkdirSync(testDir, { recursive: true });
}
});
afterEach(() => {
if (existsSync(testDir)) {
rmSync(testDir, { recursive: true, force: true });
}
});
test("should create tar.gz archive from output", async () => {
let tarCalled = false;
let tarArgs: string[] = [];
const mockExecFile: ExecFileFn = (
cmd: string,
args: string[],
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
options?: any,
) => {
if (cmd === "mineru") {
// Simulate MinerU creating output
const outputDir = args[3]; // -o argument
if (outputDir && !existsSync(outputDir)) {
mkdirSync(outputDir, { recursive: true });
}
const autoDir = join(outputDir, "auto");
if (!existsSync(autoDir)) {
mkdirSync(autoDir, { recursive: true });
}
writeFileSync(join(autoDir, "output.md"), "# Test content");
mkdirSync(join(autoDir, "images"), { recursive: true });
writeFileSync(join(autoDir, "images", "img1.png"), "fake image data");
callback(null, "MinerU conversion complete", "");
} else if (cmd === "tar") {
tarCalled = true;
tarArgs = args;
callback(null, "Archive created", "");
}
};
const targetPath = join(testDir, "output.tar.gz");
const result = await convert("test.pdf", "pdf", "md-t", targetPath, undefined, mockExecFile);
expect(result).toBe("Done");
expect(tarCalled).toBe(true);
expect(tarArgs).toContain("-czf");
});
test("should reject on mineru error", async () => {
const mockExecFile: ExecFileFn = (
cmd: string,
args: string[],
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
options?: any,
) => {
if (cmd === "mineru") {
callback(new Error("MinerU failed") as ExecFileException, "", "Error processing file");
}
};
const targetPath = join(testDir, "output.tar.gz");
expect(convert("test.pdf", "pdf", "md-t", targetPath, undefined, mockExecFile))
.rejects.toMatch(/mineru error/);
});
test("should use correct tar arguments for compression", async () => {
let tarArgs: string[] = [];
const mockExecFile: ExecFileFn = (
cmd: string,
args: string[],
callback: (err: ExecFileException | null, stdout: string, stderr: string) => void,
options?: any,
) => {
if (cmd === "mineru") {
const outputDir = args[3];
if (outputDir && !existsSync(outputDir)) {
mkdirSync(outputDir, { recursive: true });
}
const autoDir = join(outputDir, "auto");
if (!existsSync(autoDir)) {
mkdirSync(autoDir, { recursive: true });
}
writeFileSync(join(autoDir, "output.md"), "# Test");
callback(null, "Done", "");
} else if (cmd === "tar") {
tarArgs = args;
callback(null, "Archive created", "");
}
};
const targetPath = join(testDir, "test_MINERU_md-t.tar.gz");
await convert("test.pdf", "pdf", "md-t", targetPath, undefined, mockExecFile);
// Verify tar is called with correct compression flags
expect(tarArgs[0]).toBe("-czf");
expect(tarArgs[1]).toContain(".tar.gz");
expect(tarArgs[2]).toBe("-C");
expect(tarArgs[4]).toBe(".");
});
});