Why: Task 10 replaces Backend's independent Markdown-to-Word production path with the reviewed canonical parser, embedded organization-default candidate, and Word adapter. Evidence: Canonical parity, deterministic package, routing, safety, cancellation, comparison, persistence, affected-package race, full test, vet, build, and static build checks passed locally. Impact: Ordinary workspace renders now persist Markdown, deterministic DOCX, manifest, and archive with hashes; controlled/project/review/approved routes fail closed to RDM, and legacy Go rendering is comparison-only. Refs: Unified Document Renderer Task 10; Renderer d449b9f779f2f968f8ac556b89940a6c9aaf3fdc
69 lines
2.4 KiB
Go
69 lines
2.4 KiB
Go
package ingest
|
||
|
||
import (
|
||
"context"
|
||
"testing"
|
||
"time"
|
||
|
||
"agent-document-renderer/render"
|
||
core "agent-system-core"
|
||
|
||
"agent-system-backend/internal/docgen"
|
||
)
|
||
|
||
// TestParseDocxFromDocgen 回读契约:docgen 产出的 docx 必须能被 builtin:docx
|
||
// 解析器还原为块结构(标题层级、表格、列表项文本)——这是「生成→检索索引→
|
||
// 预览」链路闭合的保证(生成侧改动须两头痛检查)。
|
||
func TestParseDocxFromDocgen(t *testing.T) {
|
||
md := "# 周会纪要\n\n## 一、基本信息\n\n| 项目 | 内容 |\n|---|---|\n| 时间 | 2026-09-30 |\n\n## 二、要点\n\n- 架构定案\n- 排版齐全\n\n结论段。"
|
||
delivery, err := docgen.Generate(context.Background(), docgen.Request{
|
||
DocumentID: "roundtrip", Title: "周会纪要", DocumentType: "general", Language: "zh-CN",
|
||
Markdown: []byte(md), Mode: render.ModePreview, FrozenAt: time.Date(2026, 10, 15, 12, 0, 0, 0, time.UTC), RequestedBy: "test",
|
||
})
|
||
if err != nil {
|
||
t.Fatalf("docgen.Generate: %v", err)
|
||
}
|
||
blocks, err := parseDocx(delivery.DOCX.Bytes)
|
||
if err != nil {
|
||
t.Fatalf("parseDocx 回读失败: %v", err)
|
||
}
|
||
|
||
var h2, title int
|
||
var sawTable bool
|
||
var tableRows int
|
||
var sawArch bool
|
||
for _, blk := range blocks {
|
||
switch {
|
||
case blk.Type == core.BlockHeading && blk.Level == 1 && blk.Text == "周会纪要":
|
||
title++ // shared renderer preserves the canonical H1 instead of adding a duplicate Title paragraph.
|
||
case blk.Type == core.BlockHeading && blk.Level == 2:
|
||
h2++
|
||
if blk.Text != "一、基本信息" && blk.Text != "二、要点" {
|
||
t.Errorf("H2 文本不符: %q", blk.Text)
|
||
}
|
||
case blk.Type == core.BlockTable:
|
||
sawTable = true
|
||
tableRows = len(blk.Rows)
|
||
if len(blk.Rows) >= 2 {
|
||
if blk.Rows[0][0] != "项目" || blk.Rows[1][1] != "2026-09-30" {
|
||
t.Errorf("表格内容不符: %+v", blk.Rows)
|
||
}
|
||
}
|
||
case blk.Type == core.BlockPara && blk.Text == "架构定案":
|
||
sawArch = true // 列表项以 ListParagraph 落盘,回读侧按段落文本取回。
|
||
}
|
||
}
|
||
if title != 1 {
|
||
t.Errorf("canonical H1 回读数 = %d, want 1(blocks: %+v)", title, blocks)
|
||
}
|
||
if h2 != 2 {
|
||
t.Errorf("H2 数量 = %d, want 2(blocks: %+v)", h2, blocks)
|
||
}
|
||
if !sawTable || tableRows < 2 {
|
||
t.Errorf("表格未回读: sawTable=%v rows=%d", sawTable, tableRows)
|
||
}
|
||
if !sawArch {
|
||
t.Errorf("列表项文本未回读(blocks: %+v)", blocks)
|
||
}
|
||
}
|