Files
CraftKit/migration/tests/test_doc_batch.py
T

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()