feat: 初始化高校人事请假考勤技能 (process-leave-attendance-default)
All checks were successful
技能自动化发布 / release (push) Successful in 15s

基于 skill-template 脚手架开发,包含:
- 5 张业务表(教职工、请假政策、考勤日历、请假记录、假期余额)
- 请假核算引擎(扣薪计算、审批层级判定、年假抵扣)
- CLI 入口(import-employees / calculate-leave / render-html)
- HTML 审批与扣薪报告生成
- 示例数据与完整测试套件(295 tests passed)
This commit is contained in:
chenfuqiang
2026-09-07 16:59:24 +08:00
commit ee2a4d9e22
206 changed files with 29510 additions and 0 deletions

114
tests/test_cli_smoke.py Normal file
View File

@@ -0,0 +1,114 @@
# -*- coding: utf-8 -*-
"""通用 CLI 冒烟:用法、health、version、init-db(不触网、不深测 run 占位)。"""
from __future__ import annotations
import io
import json
import os
import unittest
from contextlib import redirect_stderr, redirect_stdout
from _support import IsolatedDataRoot, platform_kit_version_patch
from jiangchang_skill_core import config
# scripts/ 已由 _support 注入 sys.path
from cli.app import main
from util.constants import SKILL_SLUG, SKILL_VERSION
class TestCliSmoke(unittest.TestCase):
def test_main_empty_argv_shows_usage_and_nonzero(self) -> None:
buf = io.StringIO()
err = io.StringIO()
with redirect_stdout(buf), redirect_stderr(err):
rc = main([])
self.assertEqual(rc, 1)
out = buf.getvalue()
self.assertIn("高校人事请假考勤", out)
self.assertIn("calculate-leave", out)
self.assertIn("health", out)
self.assertIn("init-db", out)
def test_health_zero(self) -> None:
old_record = os.environ.get("OPENCLAW_RECORD_VIDEO")
os.environ["OPENCLAW_RECORD_VIDEO"] = "0"
try:
buf = io.StringIO()
with platform_kit_version_patch(), redirect_stdout(buf), redirect_stderr(io.StringIO()):
rc = main(["health"])
self.assertEqual(rc, 0)
out = buf.getvalue()
self.assertIn("health:", out)
self.assertIn("python_executable:", out)
self.assertIn("platform_kit_version:", out)
self.assertIn("jiangchang_skill_core_file:", out)
finally:
if old_record is None:
os.environ.pop("OPENCLAW_RECORD_VIDEO", None)
else:
os.environ["OPENCLAW_RECORD_VIDEO"] = old_record
def test_version_json_and_matches_constants_slug(self) -> None:
buf = io.StringIO()
with redirect_stdout(buf), redirect_stderr(io.StringIO()):
rc = main(["version"])
self.assertEqual(rc, 0)
payload = json.loads(buf.getvalue().strip())
self.assertIn("version", payload)
self.assertIn("skill", payload)
self.assertEqual(payload["skill"], SKILL_SLUG)
self.assertEqual(payload["version"], SKILL_VERSION)
def test_init_db_creates_database_and_is_idempotent(self) -> None:
with IsolatedDataRoot(user_id="_cli_init_db"):
config.reset_cache()
buf = io.StringIO()
with redirect_stdout(buf), redirect_stderr(io.StringIO()):
rc = main(["init-db"])
self.assertEqual(rc, 0)
payload = json.loads(buf.getvalue().strip())
self.assertTrue(payload["ok"])
self.assertEqual(payload["skill"], SKILL_SLUG)
self.assertTrue(os.path.isfile(payload["db_path"]))
buf2 = io.StringIO()
with redirect_stdout(buf2), redirect_stderr(io.StringIO()):
rc2 = main(["init-db"])
self.assertEqual(rc2, 0)
payload2 = json.loads(buf2.getvalue().strip())
self.assertTrue(payload2["ok"])
self.assertEqual(payload2["db_path"], payload["db_path"])
def test_unknown_logs_subcommand_is_rejected(self) -> None:
buf = io.StringIO()
err = io.StringIO()
with redirect_stdout(buf), redirect_stderr(err):
with self.assertRaises(SystemExit) as caught:
main(["logs"])
self.assertNotEqual(caught.exception.code, 0)
def test_run_without_employee_returns_validation_error(self) -> None:
"""无 employee_id 时 run 应提示缺少参数。"""
old_auth = os.environ.pop("JIANGCHANG_AUTH_BASE_URL", None)
old_record = os.environ.get("OPENCLAW_RECORD_VIDEO")
os.environ["OPENCLAW_RECORD_VIDEO"] = "0"
try:
with IsolatedDataRoot(user_id="_cli_run"):
config.reset_cache()
buf = io.StringIO()
with redirect_stdout(buf), redirect_stderr(io.StringIO()):
rc = main(["run"])
self.assertNotEqual(rc, 0)
self.assertIn("employee_id", buf.getvalue())
finally:
if old_auth is not None:
os.environ["JIANGCHANG_AUTH_BASE_URL"] = old_auth
if old_record is None:
os.environ.pop("OPENCLAW_RECORD_VIDEO", None)
else:
os.environ["OPENCLAW_RECORD_VIDEO"] = old_record
if __name__ == "__main__":
unittest.main()