hr-system/app/hr_system/selftest_audit.py

114 lines
4.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""hr-system 审计底座自测T09 / F12
覆盖:
- write_audit_log 全量写操作(增删改/导入导出/审批/登录)前后值 JSON
- 敏感字段脱敏:身份证前 3 后 3 / 手机前 3 后 4
- audit_log_list 按时间/类型/操作人筛选
- audit_log_detail 前后值对比
运行python3 app/hr_system/selftest_audit.py
(无需真实数据库,使用内存 stub db 验证 SQL 组装与脱敏结果。)
"""
import json
import os
import sys
# 将 app/ 目录加入 sys.path便于 import hr_system.audit
_APP_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, _APP_DIR)
from hr_system.audit import ( # noqa: E402
mask_id_number,
mask_json,
mask_mobile,
write_audit_log,
)
class FakeDb:
"""最小 db stub记录 INSERT支持 query/query_one/scalar/execute。"""
def __init__(self):
self.inserted = []
self.rows = []
def execute(self, sql, vals):
self.inserted.append((sql, vals))
def run(self, sql, vals):
self.inserted.append((sql, vals))
def scalar(self, sql, vals):
return len(self.rows)
def query(self, sql, vals):
return self.rows
def query_one(self, sql, vals):
return self.rows[0] if self.rows else None
def eq(actual, expect, label):
assert actual == expect, "%s: expect=%r actual=%r" % (label, expect, actual)
print("[PASS] %s" % label)
def main():
# 1. 脱敏规则
eq(mask_id_number("110101199001011234"), "110************234", "身份证前3后3")
eq(mask_mobile("13812345678"), "138****5678", "手机前3后4")
eq(mask_json({"id_number": "110101199001011234", "name": "张三"}),
{"id_number": "110************234", "name": "张三"}, "mask_json 递归脱敏")
# 2. write_audit_log 全量写操作
db = FakeDb()
before = {"status": "pending", "mobile": "13812345678", "id_number": "110101199001011234"}
after = {"status": "active", "mobile": "13812345678", "id_number": "110101199001011234"}
row = write_audit_log(
"hr-roster", "roster_employee", "emp001", "update",
before=before, after=after, db=db,
operator_id="u1", operator_name="李四", request_ip="192.168.16.99",
)
eq(row["module"], "hr-roster", "audit.module")
eq(row["operation"], "update", "audit.operation")
eq(bool(row["id"]), True, "audit.id 自动生成")
bj = json.loads(row["before_json"])
eq(bj["mobile"], "138****5678", "audit before 手机脱敏落库")
eq(bj["id_number"], "110************234", "audit before 身份证脱敏落库")
eq(len(db.inserted), 1, "audit INSERT 落库")
# 登录动作
login = write_audit_log(
"hr-system", "sys_user", "u1", "login", db=db,
operator_id="u1", operator_name="李四", request_ip="10.0.0.8",
)
eq(login["before_json"], None, "login before 空")
eq(login["after_json"], None, "login after 空")
eq(login["request_ip"], "10.0.0.8", "audit ip 记录")
# 导入导出动作
imp = write_audit_log("hr-roster", "roster_import", "task1", "import", db=db)
eq(imp["operation"], "import", "audit import")
exp = write_audit_log("hr-report", "roster_export", "task2", "export", db=db)
eq(exp["operation"], "export", "audit export")
# 审批动作
apv = write_audit_log("hr-flow", "flow_instance", "inst1", "approve", db=db)
eq(apv["operation"], "approve", "audit approve")
# 3. 查询筛选SQL 组装)
from hr_system.audit import audit_log_detail, audit_log_list
db.rows = [dict(row, before_json=row["before_json"], after_json=row["after_json"])]
lst = audit_log_list({"db": db, "operation": "update", "module": "hr-roster",
"date_from": "2024-01-01", "date_to": "2024-12-31"}, env=None)
eq(lst["total"], 1, "audit_log_list total")
detail = audit_log_detail({"db": db, "id": row["id"]}, env=None)
eq(detail["operator_name"], "李四", "audit_log_detail 操作人")
print("\nALL PASS — 自测通过(含脱敏样例:身份证 110************234 / 手机 138****5678")
if __name__ == "__main__":
main()