79 lines
4.0 KiB
Python
79 lines
4.0 KiB
Python
"""验证文档批次三个独立实现的核心行为和安全边界。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from docx import Document
|
|
from openpyxl import Workbook
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
MD_SCRIPT = ROOT / "plugins/doc/skills/md-to-docx/scripts/convert.py"
|
|
XLSX_SCRIPT = ROOT / "plugins/doc/skills/xlsx-to-md/scripts/convert.py"
|
|
ARCHIVE_SCRIPT = ROOT / "plugins/doc/skills/archive/scripts/archive.py"
|
|
|
|
|
|
class DocumentBatchTests(unittest.TestCase):
|
|
"""使用临时目录执行真实转换,不依赖仓库外测试材料。"""
|
|
|
|
def run_script(self, script: Path, *args: object) -> subprocess.CompletedProcess[str]:
|
|
"""使用当前受控 Python 运行时执行目标脚本。"""
|
|
|
|
return subprocess.run(
|
|
[str(Path(__import__("sys").executable)), str(script), *(str(value) for value in args)],
|
|
capture_output=True, text=True, encoding="utf-8", check=False,
|
|
)
|
|
|
|
def test_markdown_to_docx_preserves_common_blocks(self) -> None:
|
|
"""标题、列表、表格和代码块应生成可重新打开的 DOCX。"""
|
|
|
|
with tempfile.TemporaryDirectory() as temp:
|
|
folder = Path(temp); source = folder / "sample.md"; output = folder / "sample.docx"
|
|
source.write_text("# 标题\n\n- 项目\n\n| 名称 | 值 |\n| --- | --- |\n| A | 1 |\n\n```py\nprint('ok')\n```\n", encoding="utf-8")
|
|
result = self.run_script(MD_SCRIPT, source)
|
|
self.assertEqual(0, result.returncode, result.stderr)
|
|
document = Document(output)
|
|
self.assertEqual("标题", document.paragraphs[0].text)
|
|
self.assertEqual(1, len(document.tables))
|
|
self.assertNotEqual(0, self.run_script(MD_SCRIPT, source).returncode)
|
|
|
|
def test_xlsx_to_markdown_handles_merges_and_escaping(self) -> None:
|
|
"""合并区域应填充值,表格特殊字符应安全转义。"""
|
|
|
|
with tempfile.TemporaryDirectory() as temp:
|
|
folder = Path(temp); source = folder / "sample.xlsx"
|
|
workbook = Workbook(); sheet = workbook.active; sheet.title = "数据"
|
|
sheet.append(["名称", "值"]); sheet.append(["A|B", "=1+1"])
|
|
sheet.merge_cells("A3:B3"); sheet["A3"] = "合并"
|
|
workbook.create_sheet("空表"); workbook.save(source)
|
|
result = self.run_script(XLSX_SCRIPT, source)
|
|
self.assertEqual(0, result.returncode, result.stderr)
|
|
markdown = source.with_suffix(".md").read_text(encoding="utf-8")
|
|
self.assertIn(r"A\|B", markdown)
|
|
self.assertIn("| 合并 | 合并 |", markdown)
|
|
self.assertIn("_空工作表_", markdown)
|
|
|
|
def test_archive_is_dry_run_first_and_keeps_source(self) -> None:
|
|
"""预演不得写目标,执行后仍须保留来源并只抽取指定章节。"""
|
|
|
|
with tempfile.TemporaryDirectory() as temp:
|
|
root = Path(temp); source = root / "specs/api.md"; source.parent.mkdir()
|
|
source.write_text("---\nowner: team\n---\n# 概述\n正文\n## API\n接口\n## 其他\n忽略\n", encoding="utf-8")
|
|
config = root / "rules.json"
|
|
config.write_text(json.dumps({"version": 1, "archiveRoot": "archive", "rules": [{"match": "specs/*.md", "target": "{stem}.md", "section": "API", "stripFrontmatter": True, "requiredText": ["接口"]}]}, ensure_ascii=False), encoding="utf-8")
|
|
target = root / "archive/api.md"
|
|
preview = self.run_script(ARCHIVE_SCRIPT, "--root", root, "--config", config)
|
|
self.assertEqual(0, preview.returncode, preview.stderr); self.assertFalse(target.exists())
|
|
applied = self.run_script(ARCHIVE_SCRIPT, "--root", root, "--config", config, "--apply")
|
|
self.assertEqual(0, applied.returncode, applied.stderr)
|
|
self.assertTrue(source.exists()); self.assertEqual("## API\n接口\n", target.read_text(encoding="utf-8"))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|