package ingest // 真实生成器轮测:openpyxl / python-pptx 现场生成 xlsx/pptx 再解析,防止 // 手工夹具只验证"自己写的 XML"。生成器缺失时 t.Skip(诚实出口);CI 已装 // 这两个包(.gitea/workflows/ci.yaml 安装 python-docx python-pptx openpyxl)。 import ( "os" "os/exec" "path/filepath" "strings" "testing" core "agent-system-core" ) func pythonModuleAvailable(t *testing.T, module string) bool { t.Helper() if _, err := exec.LookPath("python3"); err != nil { return false } return exec.Command("python3", "-c", "import "+module).Run() == nil } // runPythonScript 把脚本写到临时目录执行;失败即 t.Fatal(脚本属测试自身)。 func runPythonScript(t *testing.T, script string) { t.Helper() dir := t.TempDir() path := filepath.Join(dir, "gen.py") if err := os.WriteFile(path, []byte(script), 0o644); err != nil { t.Fatal(err) } out, err := exec.Command("python3", path).CombinedOutput() if err != nil { t.Fatalf("python generator failed: %v\n%s", err, out) } } func TestParseXlsxFromOpenpyxl(t *testing.T) { if !pythonModuleAvailable(t, "openpyxl") { t.Skip("python3 openpyxl not available") } dir := t.TempDir() file := filepath.Join(dir, "报价单.xlsx") runPythonScript(t, ` import openpyxl from datetime import datetime wb = openpyxl.Workbook() ws = wb.active ws.title = "报价" ws.append(["物料", "单价", "日期"]) ws.append(["支架", 12.5, datetime(2024, 1, 1)]) ws["C2"].number_format = "yyyy-mm-dd" ws["D2"] = "=A2&\"-x\"" ws["D3"] = True wb.create_sheet("说明").append(["备注行"]) wb.save(r"`+file+`") `) data, err := os.ReadFile(file) if err != nil { t.Fatal(err) } doc := Parse(file, "", data) if doc.Meta.Parser != "builtin:xlsx" || len(doc.Warnings) != 0 { t.Fatalf("meta: %+v warnings: %v", doc.Meta, doc.Warnings) } var heads []string for _, b := range doc.Blocks { if b.Type == core.BlockHeading { heads = append(heads, b.Text) } } if len(heads) != 2 || heads[0] != "报价" || heads[1] != "说明" { t.Fatalf("headings: %v", heads) } plain := doc.PlainText() for _, want := range []string{"物料", "支架", "12.5", "2024-01-01"} { if !strings.Contains(plain, want) { t.Errorf("missing %q in: %s", want, plain) } } // openpyxl 公式不带缓存值:回显公式本身(可检索、不冒充结果)。 if !strings.Contains(plain, "=A2") { t.Errorf("formula without cache should echo: %s", plain) } if !strings.Contains(plain, "TRUE") { t.Errorf("boolean cell should render TRUE: %s", plain) } } func TestParsePptxFromPythonPptx(t *testing.T) { if !pythonModuleAvailable(t, "pptx") { t.Skip("python3 pptx not available") } dir := t.TempDir() file := filepath.Join(dir, "方案评审.pptx") runPythonScript(t, ` from pptx import Presentation from pptx.util import Inches prs = Presentation() s1 = prs.slides.add_slide(prs.slide_layouts[0]) s1.shapes.title.text = "评审结论" s1.placeholders[1].text = "通过" s2 = prs.slides.add_slide(prs.slide_layouts[5]) s2.shapes.title.text = "报价明细" tbl = s2.shapes.add_table(2, 2, Inches(1), Inches(2), Inches(6), Inches(1)).table tbl.cell(0, 0).text = "物料" tbl.cell(0, 1).text = "单价" tbl.cell(1, 0).text = "支架" tbl.cell(1, 1).text = "12.5" notes = s1.notes_slide.notes_text_frame notes.text = "需要补充法规依据" prs.save(r"`+file+`") `) data, err := os.ReadFile(file) if err != nil { t.Fatal(err) } doc := Parse(file, "", data) if doc.Meta.Parser != "builtin:pptx" || len(doc.Warnings) != 0 { t.Fatalf("meta: %+v warnings: %v", doc.Meta, doc.Warnings) } var heads []string for _, b := range doc.Blocks { if b.Type == core.BlockHeading { heads = append(heads, b.Text) } } if len(heads) != 2 || !strings.HasPrefix(heads[0], "幻灯片 1:评审结论") || !strings.HasPrefix(heads[1], "幻灯片 2:报价明细") { t.Fatalf("headings: %v", heads) } plain := doc.PlainText() for _, want := range []string{"通过", "物料", "支架", "12.5", "备注:需要补充法规依据"} { if !strings.Contains(plain, want) { t.Errorf("missing %q in: %s", want, plain) } } }