from __future__ import annotations import json import os import sys import tempfile import unittest from pathlib import Path from unittest.mock import Mock, patch ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "dev_scripts")) from new_task_archive import build_archive, safe_title # noqa: E402 from wiki_docs import ( # noqa: E402 Config, Mapping, WikiClient, WikiDocsError, WikiPage, dirty_mirror_paths, load_config, parse_mirror, render_mirror, sync_all, validate_mappings, ) class MappingTests(unittest.TestCase): def test_rejects_path_outside_docs(self) -> None: with self.assertRaisesRegex(WikiDocsError, "docs/"): validate_mappings([{"page": "Home", "path": "README.md"}]) def test_rejects_duplicate_page(self) -> None: with self.assertRaisesRegex(WikiDocsError, "重复映射"): validate_mappings( [ {"page": "Home", "path": "docs/README.md"}, {"page": "Home", "path": "docs/other.md"}, ] ) def test_normalizes_api_suffix_from_environment(self) -> None: with tempfile.TemporaryDirectory() as directory: path = Path(directory) / "wiki-docs.json" path.write_text( json.dumps( { "schema_version": 1, "gitea_url": "http://configured.example", "owner": "owner", "repository": "repo", "mappings": [ {"page": "Home", "path": "docs/README.md"} ], } ), encoding="utf-8", ) with patch.dict( os.environ, {"GITEA_URL": "http://gitea.example/api/v1"}, clear=False ): config = load_config(path) self.assertEqual(config.gitea_url, "http://gitea.example") class MirrorTests(unittest.TestCase): def setUp(self) -> None: self.page = WikiPage( title="Home", sub_url="Home", text="# 首页\n", revision="a" * 40, html_url="http://gitea.example/o/r/wiki/Home", ) def test_render_includes_traceable_metadata(self) -> None: rendered = render_mirror(self.page) metadata, body = parse_mirror(rendered) self.assertEqual(metadata["wiki_page"], "Home") self.assertEqual(metadata["wiki_revision"], "a" * 40) self.assertTrue(metadata["synchronized_at"].endswith("Z")) self.assertEqual(body, "# 首页\n") def test_unchanged_revision_preserves_sync_time(self) -> None: first = render_mirror(self.page) second = render_mirror(self.page, first) self.assertEqual(first, second) @patch("wiki_docs.subprocess.run") def test_dirty_mirror_paths_are_reported(self, run) -> None: run.return_value.stdout = " M docs/README.md\n" config = Config( path=Path("wiki-docs.json"), gitea_url="http://gitea.example", owner="o", repository="r", mappings=(Mapping("Home", "docs/README.md"),), ) self.assertEqual(dirty_mirror_paths(config), [" M docs/README.md"]) @patch("wiki_docs.dirty_mirror_paths", return_value=[" M docs/README.md"]) def test_sync_stops_before_reading_wiki_when_mirror_is_dirty(self, _dirty) -> None: config = Config( path=Path("wiki-docs.json"), gitea_url="http://gitea.example", owner="o", repository="r", mappings=(Mapping("Home", "docs/README.md"),), ) client = Mock() with self.assertRaisesRegex(WikiDocsError, "未提交改动"): sync_all(config, client) client.get_page.assert_not_called() class WikiClientTests(unittest.TestCase): def test_encoded_unicode_sub_url_is_not_double_encoded(self) -> None: config = Config( path=Path("wiki-docs.json"), gitea_url="http://gitea.example", owner="o", repository="r", mappings=(Mapping("中文", "docs/chinese.md"),), ) client = WikiClient(config, token="") client.list_pages = Mock( return_value=[{"title": "中文", "sub_url": "%E4%B8%AD%E6%96%87.-"}] ) encoded = __import__("base64").b64encode("# 中文\n".encode()).decode() with patch.object( client, "_request", return_value={ "title": "中文", "content_base64": encoded, "last_commit": {"sha": "b" * 40}, }, ) as request: page = client.get_page("中文") api_path = request.call_args.args[1] self.assertIn("%E4%B8%AD%E6%96%87.-", api_path) self.assertNotIn("%25E4", api_path) self.assertTrue(page.html_url.endswith("/%E4%B8%AD%E6%96%87.-")) class ArchiveTests(unittest.TestCase): def test_safe_title_handles_windows_characters(self) -> None: self.assertEqual(safe_title(' 修复:"登录" / 超时 '), "修复-登录-超时") def test_build_archive_replaces_known_fields(self) -> None: template = "# <工单号> <标题>\nYYYY-MM-DD\n<链接>\n<页面名>\n" result = build_archive(template, "12", "修复登录", "Task-12-login", "http://i/12") self.assertIn("# 12 修复登录", result) self.assertIn("http://i/12", result) self.assertIn("Task-12-login", result) self.assertNotIn("YYYY-MM-DD", result) if __name__ == "__main__": unittest.main()