init: 项目初始提交 - NeoZQYY Monorepo 完整代码
This commit is contained in:
177
apps/etl/pipelines/feiqiu/tests/unit/test_audit_run.py
Normal file
177
apps/etl/pipelines/feiqiu/tests/unit/test_audit_run.py
Normal file
@@ -0,0 +1,177 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
run_audit 主入口的单元测试。
|
||||
|
||||
验证:
|
||||
- docs/audit/ 目录自动创建
|
||||
- 三份报告文件正确生成
|
||||
- 报告头部包含时间戳和仓库路径
|
||||
- 目录创建失败时抛出 RuntimeError
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
class TestEnsureReportDir:
|
||||
"""测试 _ensure_report_dir 目录创建逻辑。"""
|
||||
|
||||
def test_creates_dir_when_missing(self, tmp_path: Path):
|
||||
from scripts.audit.run_audit import _ensure_report_dir
|
||||
|
||||
result = _ensure_report_dir(tmp_path)
|
||||
expected = tmp_path / "docs" / "audit" / "repo"
|
||||
assert result == expected
|
||||
assert expected.is_dir()
|
||||
|
||||
def test_returns_existing_dir(self, tmp_path: Path):
|
||||
from scripts.audit.run_audit import _ensure_report_dir
|
||||
|
||||
audit_dir = tmp_path / "docs" / "audit" / "repo"
|
||||
audit_dir.mkdir(parents=True)
|
||||
result = _ensure_report_dir(tmp_path)
|
||||
assert result == audit_dir
|
||||
|
||||
def test_raises_on_creation_failure(self, tmp_path: Path):
|
||||
from scripts.audit.run_audit import _ensure_report_dir
|
||||
|
||||
# 在 docs/audit 位置放一个文件,使 mkdir 失败
|
||||
docs = tmp_path / "docs"
|
||||
docs.mkdir()
|
||||
(docs / "audit").write_text("block", encoding="utf-8")
|
||||
|
||||
with pytest.raises(RuntimeError, match="无法创建报告输出目录"):
|
||||
_ensure_report_dir(tmp_path)
|
||||
|
||||
|
||||
class TestInjectHeader:
|
||||
"""测试 _inject_header 兜底注入逻辑。"""
|
||||
|
||||
def test_skips_when_header_present(self):
|
||||
from scripts.audit.run_audit import _inject_header
|
||||
|
||||
report = "# 标题\n\n- 生成时间: 2025-01-01T00:00:00Z\n- 仓库路径: `/repo`\n"
|
||||
result = _inject_header(report, "2025-06-01T00:00:00Z", "/other")
|
||||
# 不应修改已有头部
|
||||
assert result == report
|
||||
|
||||
def test_injects_when_header_missing(self):
|
||||
from scripts.audit.run_audit import _inject_header
|
||||
|
||||
report = "# 无头部报告\n\n内容..."
|
||||
result = _inject_header(report, "2025-06-01T00:00:00Z", "/repo")
|
||||
assert "生成时间: 2025-06-01T00:00:00Z" in result
|
||||
assert "仓库路径: `/repo`" in result
|
||||
|
||||
|
||||
class TestRunAudit:
|
||||
"""测试 run_audit 完整流程(使用最小仓库结构)。"""
|
||||
|
||||
def _make_minimal_repo(self, tmp_path: Path) -> Path:
|
||||
"""构造一个最小仓库结构,足以让 run_audit 跑通。"""
|
||||
repo = tmp_path / "repo"
|
||||
repo.mkdir()
|
||||
|
||||
# 核心代码目录
|
||||
cli_dir = repo / "cli"
|
||||
cli_dir.mkdir()
|
||||
(cli_dir / "__init__.py").write_text("", encoding="utf-8")
|
||||
(cli_dir / "main.py").write_text(
|
||||
"# -*- coding: utf-8 -*-\ndef main(): pass\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
# config 目录
|
||||
config_dir = repo / "config"
|
||||
config_dir.mkdir()
|
||||
(config_dir / "__init__.py").write_text("", encoding="utf-8")
|
||||
(config_dir / "defaults.py").write_text("DEFAULTS = {}\n", encoding="utf-8")
|
||||
|
||||
# docs 目录
|
||||
docs_dir = repo / "docs"
|
||||
docs_dir.mkdir()
|
||||
(docs_dir / "README.md").write_text("# 文档\n", encoding="utf-8")
|
||||
|
||||
# 根目录文件
|
||||
(repo / "README.md").write_text("# 项目\n", encoding="utf-8")
|
||||
|
||||
return repo
|
||||
|
||||
def test_creates_three_reports(self, tmp_path: Path):
|
||||
from scripts.audit.run_audit import run_audit
|
||||
|
||||
repo = self._make_minimal_repo(tmp_path)
|
||||
run_audit(repo)
|
||||
|
||||
audit_dir = repo / "docs" / "audit" / "repo"
|
||||
assert (audit_dir / "file_inventory.md").is_file()
|
||||
assert (audit_dir / "flow_tree.md").is_file()
|
||||
assert (audit_dir / "doc_alignment.md").is_file()
|
||||
|
||||
def test_reports_contain_timestamp(self, tmp_path: Path):
|
||||
from scripts.audit.run_audit import run_audit
|
||||
|
||||
repo = self._make_minimal_repo(tmp_path)
|
||||
run_audit(repo)
|
||||
|
||||
audit_dir = repo / "docs" / "audit" / "repo"
|
||||
# ISO 时间戳格式
|
||||
ts_pattern = re.compile(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z")
|
||||
|
||||
for name in ("file_inventory.md", "flow_tree.md", "doc_alignment.md"):
|
||||
content = (audit_dir / name).read_text(encoding="utf-8")
|
||||
assert ts_pattern.search(content), f"{name} 缺少时间戳"
|
||||
|
||||
def test_reports_contain_repo_path(self, tmp_path: Path):
|
||||
from scripts.audit.run_audit import run_audit
|
||||
|
||||
repo = self._make_minimal_repo(tmp_path)
|
||||
run_audit(repo)
|
||||
|
||||
audit_dir = repo / "docs" / "audit" / "repo"
|
||||
repo_str = str(repo.resolve())
|
||||
|
||||
for name in ("file_inventory.md", "flow_tree.md", "doc_alignment.md"):
|
||||
content = (audit_dir / name).read_text(encoding="utf-8")
|
||||
assert repo_str in content, f"{name} 缺少仓库路径"
|
||||
|
||||
def test_writes_only_to_docs_audit(self, tmp_path: Path):
|
||||
"""验证所有写操作仅限 docs/audit/ 目录(Property 15)。"""
|
||||
from scripts.audit.run_audit import run_audit
|
||||
|
||||
repo = self._make_minimal_repo(tmp_path)
|
||||
|
||||
# 记录运行前的文件快照(排除 docs/audit/)
|
||||
before = set()
|
||||
for p in repo.rglob("*"):
|
||||
rel = p.relative_to(repo).as_posix()
|
||||
if not rel.startswith("docs/audit"):
|
||||
before.add((rel, p.stat().st_mtime if p.is_file() else None))
|
||||
|
||||
run_audit(repo)
|
||||
|
||||
# 运行后检查:docs/audit/ 外的文件不应被修改
|
||||
for p in repo.rglob("*"):
|
||||
rel = p.relative_to(repo).as_posix()
|
||||
if rel.startswith("docs/audit"):
|
||||
continue
|
||||
if p.is_file():
|
||||
# 文件应在之前的快照中
|
||||
found = any(r == rel for r, _ in before)
|
||||
assert found, f"意外创建了 docs/audit/ 外的文件: {rel}"
|
||||
|
||||
def test_auto_creates_docs_audit_dir(self, tmp_path: Path):
|
||||
from scripts.audit.run_audit import run_audit
|
||||
|
||||
repo = self._make_minimal_repo(tmp_path)
|
||||
# 确保 docs/audit/ 不存在
|
||||
audit_dir = repo / "docs" / "audit" / "repo"
|
||||
assert not audit_dir.exists()
|
||||
|
||||
run_audit(repo)
|
||||
assert audit_dir.is_dir()
|
||||
Reference in New Issue
Block a user