feat(client): initialize procurement tool skeleton

This commit is contained in:
QiuSW
2026-08-03 18:32:00 +08:00
parent 2ab5baa776
commit bcec012c8b
18 changed files with 516 additions and 31 deletions
+1
View File
@@ -0,0 +1 @@
"""采购工具的离线单元测试。"""
+24
View File
@@ -0,0 +1,24 @@
"""验证桌面入口的纯参数处理,不启动 PySide6。"""
from __future__ import annotations
import sys
from pathlib import Path
import unittest
from unittest import mock
CLIENT_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(CLIENT_ROOT / "src"))
from cmbuyer_client.app import select_application_argv
class ApplicationArgumentsTests(unittest.TestCase):
def test_empty_argument_list_is_not_replaced_with_process_arguments(self) -> None:
with mock.patch("cmbuyer_client.app.sys.argv", ["process-name", "--process-option"]):
self.assertEqual([], select_application_argv([]))
def test_none_uses_process_arguments(self) -> None:
with mock.patch("cmbuyer_client.app.sys.argv", ["process-name", "--process-option"]):
self.assertEqual(["process-name", "--process-option"], select_application_argv(None))
+49
View File
@@ -0,0 +1,49 @@
"""验证日志脱敏边界,不需要 PySide6 或真机。"""
from __future__ import annotations
import sys
from pathlib import Path
import tempfile
import unittest
CLIENT_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(CLIENT_ROOT / "src"))
from cmbuyer_client.logging_policy import configure_application_logger, redact_text
from cmbuyer_client.runtime import RuntimePaths
class LoggingPolicyTests(unittest.TestCase):
def test_redact_text_hides_required_sensitive_values(self) -> None:
message = (
"token=secret-value authorization: Bearer-value "
"address='浙江省杭州市' phone=13800138000 payment=card-value"
)
redacted = redact_text(message)
for raw_value in ("secret-value", "Bearer-value", "浙江省杭州市", "13800138000", "card-value"):
self.assertNotIn(raw_value, redacted)
self.assertIn("token=[已隐藏]", redacted)
def test_file_handler_writes_only_redacted_text(self) -> None:
with tempfile.TemporaryDirectory() as directory:
paths = RuntimePaths.from_root(Path(directory))
logger = configure_application_logger(paths)
try:
logger.info("token=not-for-log phone=13900139000 payment=not-for-log")
for handler in logger.handlers:
handler.flush()
content = (paths.logs / "client.log").read_text(encoding="utf-8")
finally:
# Windows 不允许在 FileHandler 持有文件时删除临时目录。
for handler in tuple(logger.handlers):
logger.removeHandler(handler)
handler.close()
self.assertNotIn("not-for-log", content)
self.assertNotIn("13900139000", content)
self.assertIn("[已隐藏]", content)
+25
View File
@@ -0,0 +1,25 @@
"""验证本地运行目录策略,不需要连接设备。"""
from __future__ import annotations
import sys
from pathlib import Path
import tempfile
import unittest
CLIENT_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(CLIENT_ROOT / "src"))
from cmbuyer_client.runtime import RuntimePaths
class RuntimePathsTests(unittest.TestCase):
def test_ensure_exists_creates_only_runtime_directories(self) -> None:
with tempfile.TemporaryDirectory() as directory:
paths = RuntimePaths.from_root(Path(directory) / "runtime")
paths.ensure_exists()
self.assertTrue(paths.logs.is_dir())
self.assertTrue(paths.artifacts.is_dir())
+29
View File
@@ -0,0 +1,29 @@
"""验证 wheel 元数据检查脚本,不构建 wheel 或安装运行时依赖。"""
from __future__ import annotations
import sys
from pathlib import Path
import tempfile
import unittest
import zipfile
CLIENT_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(CLIENT_ROOT / "scripts"))
from verify_wheel_metadata import dependencies_from_requirements, verify_wheel_metadata
class WheelMetadataTests(unittest.TestCase):
def test_metadata_checker_accepts_dependencies_from_the_single_requirements_file(self) -> None:
expected_dependencies = dependencies_from_requirements(CLIENT_ROOT / "requirements.txt")
with tempfile.TemporaryDirectory() as directory:
wheel_file = Path(directory) / "cmbuyer_client-0.1.0-py3-none-any.whl"
metadata = "Metadata-Version: 2.3\n" + "".join(
f"Requires-Dist: {dependency}\n" for dependency in sorted(expected_dependencies)
)
with zipfile.ZipFile(wheel_file, "w") as wheel:
wheel.writestr("cmbuyer_client-0.1.0.dist-info/METADATA", metadata)
self.assertEqual(set(), verify_wheel_metadata(wheel_file, CLIENT_ROOT / "requirements.txt"))