/** * Runtime coverage for the in-house markit document engine (src/markit), which * replaced the `markit-ai` package. Each format is generated in-memory via the * shared zip util (src/utils/zip) — no external fixtures — and converted through * the public wrapper (src/utils/markit), locking: docx text, xlsx tables, pptx * slides, epub metadata+spine, shared HTML-table normalization, image * extraction, the nested/relative zip path resolution regression surface, and * the unsupported-format error contract. */ import { describe, expect, it } from "bun:test"; import * as fs from "node:fs/promises"; import * as os from "node:os"; import * as path from "node:path"; import { convertBufferWithMarkit, convertFileWithMarkit } from "@oh-my-pi/pi-coding-agent/utils/markit"; import { zip } from "@oh-my-pi/pi-coding-agent/utils/zip"; import { removeWithRetries } from "@oh-my-pi/pi-utils"; const enc = (s: string): Uint8Array => new TextEncoder().encode(s); const WML = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; const repoRoot = path.resolve(import.meta.dir, "..", "..", ".."); const cliEntry = path.join(repoRoot, "packages", "coding-agent", "src", "cli.ts"); function makeDocx(bodyXml: string): Uint8Array { return zip({ "[Content_Types].xml": enc( ``, ), "_rels/.rels": enc( ``, ), "word/document.xml": enc( `${bodyXml}`, ), }); } describe("markit converters", () => { it("converts docx paragraphs to markdown", async () => { const docx = makeDocx( `First paragraph.Second paragraph.`, ); const result = await convertBufferWithMarkit(docx, ".docx"); expect(result.ok).toBe(true); expect(result.content).toBe("First paragraph.\n\nSecond paragraph."); }); it("converts xlsx sheets to markdown tables", async () => { const xlsx = zip({ "xl/workbook.xml": enc( ``, ), "xl/_rels/workbook.xml.rels": enc( ``, ), "xl/worksheets/sheet1.xml": enc( `NameAgeAlice30`, ), }); const result = await convertBufferWithMarkit(xlsx, ".xlsx"); expect(result.ok).toBe(true); expect(result.content).toContain("## People"); expect(result.content).toContain("| Name | Age |"); expect(result.content).toContain("| --- | --- |"); expect(result.content).toContain("| Alice | 30 |"); }); it("reads an xlsx worksheet through an absolute (/-prefixed) rel target", async () => { const xlsx = zip({ "xl/workbook.xml": enc( ``, ), "xl/_rels/workbook.xml.rels": enc( ``, ), "xl/worksheets/sheet1.xml": enc( `Header42`, ), }); const result = await convertBufferWithMarkit(xlsx, ".xlsx"); expect(result.ok).toBe(true); expect(result.content).toContain("| Header |"); expect(result.content).toContain("| 42 |"); }); it("converts pptx slides with a title heading and body text", async () => { const pptx = zip({ "ppt/presentation.xml": enc( ``, ), "ppt/_rels/presentation.xml.rels": enc( ``, ), "ppt/slides/slide1.xml": enc( `The TitleBody line`, ), }); const result = await convertBufferWithMarkit(pptx, ".pptx"); expect(result.ok).toBe(true); expect(result.content).toContain("# The Title"); expect(result.content).toContain("Body line"); }); it("extracts a pptx image through a ../media relative rel target into imageDir", async () => { const dir = await fs.mkdtemp(path.join(os.tmpdir(), "markit-pptx-")); try { const pptx = zip({ "ppt/presentation.xml": enc( ``, ), "ppt/_rels/presentation.xml.rels": enc( ``, ), "ppt/slides/slide1.xml": enc( ``, ), "ppt/slides/_rels/slide1.xml.rels": enc( ``, ), "ppt/media/image1.png": new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]), }); const pptxPath = path.join(dir, "deck.pptx"); const imageDir = path.join(dir, "imgs"); await Bun.write(pptxPath, pptx); const result = await convertFileWithMarkit(pptxPath, undefined, { imageDir }); expect(result.ok).toBe(true); const written = await fs.readdir(imageDir); expect(written).toHaveLength(1); expect(result.content).toContain(`](${path.join(imageDir, written[0]!)})`); } finally { await removeWithRetries(dir); } }); it("converts epub spine, normalizes HTML tables, and resolves a non-root OPF basePath", async () => { const epub = zip({ "META-INF/container.xml": enc( ``, ), "OEBPS/content.opf": enc( `Nested BookAda`, ), "OEBPS/text/ch1.xhtml": enc( `

Chapter One

Body text.

AB
12
`, ), }); const result = await convertBufferWithMarkit(epub, ".epub"); expect(result.ok).toBe(true); expect(result.content).toContain("**Title:** Nested Book"); expect(result.content).toContain("**Authors:** Ada"); expect(result.content).toContain("## Chapter One"); expect(result.content).toContain("Body text."); // normalizeTablesHtml promotes the first row to a header so GFM renders a table. expect(result.content).toContain("| A | B |"); expect(result.content).toContain("| --- | --- |"); }); it("reads PDF text after inline image binary data containing delimiter bytes", async () => { const homeDir = await fs.mkdtemp(path.join(os.tmpdir(), "omp-pdf-inline-home-")); const homeRoot = path.parse(homeDir).root; const homeDrive = homeRoot.endsWith(path.sep) ? homeRoot.slice(0, -1) : homeRoot; const homePath = homeDir.slice(homeDrive.length) || path.sep; try { const pdfPath = path.join( import.meta.dir, "fixtures", "pdf-inline-image-repro", "bad-inline-image-delimiter.pdf", ); const proc = Bun.spawn([process.execPath, cliEntry, "read", pdfPath], { cwd: repoRoot, stdout: "pipe", stderr: "pipe", env: { ...process.env, APPDATA: path.join(homeDir, "AppData", "Roaming"), HOME: homeDir, HOMEDRIVE: homeDrive, HOMEPATH: homePath, LOCALAPPDATA: path.join(homeDir, "AppData", "Local"), USERPROFILE: homeDir, NO_COLOR: "1", OMP_PROFILE: "", PI_CODING_AGENT_DIR: path.join(homeDir, ".omp", "agent"), PI_CONFIG_DIR: ".omp", PI_NO_TITLE: "1", PI_PROFILE: "", XDG_CACHE_HOME: path.join(homeDir, ".cache"), XDG_DATA_HOME: path.join(homeDir, ".local", "share"), XDG_STATE_HOME: path.join(homeDir, ".local", "state"), }, }); const stdout = new Response(proc.stdout).text(); const stderr = new Response(proc.stderr).text(); // The regression is a synchronous child-process spin; there is no in-process signal to await. const outcome = await Promise.race([ proc.exited.then(exitCode => ({ type: "exit" as const, exitCode })), Bun.sleep(5_000).then(() => ({ type: "timeout" as const })), ]); if (outcome.type === "timeout") { try { proc.kill("SIGKILL"); } catch { // already exited } await proc.exited; throw new Error("read command timed out on inline image PDF"); } const [out, err] = await Promise.all([stdout, stderr]); expect(outcome.exitCode).toBe(0); expect(err).toBe(""); expect(out).toContain("Inline image tokenizer repro issue"); expect(out).toContain("| Name | Qty |"); expect(out).toContain("| Wire | 12 |"); } finally { await removeWithRetries(homeDir); } }); it("reports an unsupported format instead of emitting garbage", async () => { const rtf = enc("{\\rtf1\\ansi binary-ish}"); const result = await convertBufferWithMarkit(rtf, ".rtf"); expect(result.ok).toBe(false); expect(result.error).toContain("Unsupported format"); }); });