package office import ( "archive/zip" "bytes" "context" "io" "strings" "testing" ) func TestRenderReport_ValidDocx(t *testing.T) { secs := []Section{ {Heading: "第一章", Body: "正文内容 A&B"}, {Heading: "第二章", Body: "正文 <内容> B"}, } data, err := NewRenderer().RenderReport(context.Background(), "测试报告", secs) if err != nil { t.Fatalf("RenderReport err: %v", err) } zr, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) if err != nil { t.Fatalf("产物不是合法 zip/docx: %v", err) } parts := map[string]string{} for _, f := range zr.File { rc, _ := f.Open() b, _ := io.ReadAll(rc) rc.Close() parts[f.Name] = string(b) } for _, need := range []string{"[Content_Types].xml", "_rels/.rels", "word/document.xml"} { if _, ok := parts[need]; !ok { t.Errorf("docx 缺少部件 %s", need) } } doc := parts["word/document.xml"] if !strings.Contains(doc, "测试报告") || !strings.Contains(doc, "第一章") || !strings.Contains(doc, "第二章") { t.Error("document.xml 应含标题与各章标题") } // XML 特殊字符必须转义,避免破坏文档。 if strings.Contains(doc, "A&B") || !strings.Contains(doc, "A&B") { t.Error("正文 & 应被转义为 &") } if strings.Contains(doc, "<内容>") || !strings.Contains(doc, "<内容>") { t.Error("正文尖括号应被转义") } } func TestEscapeXML(t *testing.T) { got := escapeXML(`a&b"d'`) want := "a&b<c>"d'" if got != want { t.Errorf("escapeXML = %q, want %q", got, want) } }