增加充值审计日志
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
"""充值审计日志的权限、查询和脱敏测试。"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import unittest
|
||||
|
||||
os.environ.setdefault("DATABASE_URL", "sqlite://")
|
||||
os.environ.setdefault("APP_ENCRYPTION_KEY", "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=")
|
||||
|
||||
from fastapi import HTTPException
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from web.backend.database import Base
|
||||
from web.backend.models import Account, AuditLog, DouyuTask, User
|
||||
from web.backend.routers.audit import list_audit_logs, require_super_admin
|
||||
from web.backend.services.audit_service import record_audit
|
||||
|
||||
|
||||
class AuditLogTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.engine = create_engine("sqlite://")
|
||||
Base.metadata.create_all(self.engine)
|
||||
self.session = sessionmaker(bind=self.engine)()
|
||||
self.admin = User(username="admin", password_hash="hash", role="super_admin")
|
||||
self.operator = User(
|
||||
username="operator", password_hash="hash", role="operation",
|
||||
custom_permissions=["audit:view"],
|
||||
)
|
||||
self.session.add_all([self.admin, self.operator])
|
||||
self.session.commit()
|
||||
|
||||
def tearDown(self):
|
||||
self.session.close()
|
||||
Base.metadata.drop_all(self.engine)
|
||||
self.engine.dispose()
|
||||
|
||||
def test_only_super_admin_can_read_audit_logs(self):
|
||||
with self.assertRaises(HTTPException) as context:
|
||||
require_super_admin(self.operator)
|
||||
self.assertEqual(context.exception.status_code, 403)
|
||||
self.assertIs(require_super_admin(self.admin), self.admin)
|
||||
|
||||
def test_list_audit_logs_supports_pagination_and_result_filter(self):
|
||||
for index in range(3):
|
||||
record_audit(
|
||||
self.session, self.admin, action="recharge:yyb:payment",
|
||||
target=f"yyb_task:{index}", detail={"task_id": index}, success=index != 1,
|
||||
)
|
||||
self.session.commit()
|
||||
|
||||
result = list_audit_logs(
|
||||
page=1, page_size=1, username=None, action="recharge:yyb:payment",
|
||||
keyword=None, success=True, start_time=None, end_time=None,
|
||||
db=self.session, _=self.admin,
|
||||
)
|
||||
|
||||
self.assertEqual(result["total"], 2)
|
||||
self.assertEqual(len(result["items"]), 1)
|
||||
self.assertTrue(result["items"][0]["success"])
|
||||
|
||||
def test_audit_detail_masks_sensitive_fields(self):
|
||||
record_audit(
|
||||
self.session, self.admin, action="recharge:yyb:payment",
|
||||
detail={"token": "token-value", "nested": {"cookie": "cookie-value"}, "order_id": "order-1"},
|
||||
)
|
||||
self.session.commit()
|
||||
|
||||
entry = self.session.query(AuditLog).one()
|
||||
detail = json.loads(entry.detail)
|
||||
self.assertEqual(detail["token"], "[已脱敏]")
|
||||
self.assertEqual(detail["nested"]["cookie"], "[已脱敏]")
|
||||
self.assertEqual(detail["order_id"], "order-1")
|
||||
|
||||
def test_old_douyu_recharge_log_is_enriched_with_account_identity(self):
|
||||
account = Account(
|
||||
username="douyu-login", password="password", email="mail@example.com",
|
||||
email_password="mail-password", uid="10001", nickname="斗鱼昵称",
|
||||
)
|
||||
self.session.add(account)
|
||||
self.session.commit()
|
||||
self.session.add(DouyuTask(
|
||||
batch_id="batch-old", account_id=account.id, task_type="create_gold_qr",
|
||||
handbook_scope="elite", created_by=self.admin.id,
|
||||
))
|
||||
self.session.add(AuditLog(
|
||||
user_id=self.admin.id, username=self.admin.username,
|
||||
action="recharge:douyu:create", target="douyu_batch:batch-old", detail="{}", success=True,
|
||||
))
|
||||
self.session.commit()
|
||||
|
||||
result = list_audit_logs(
|
||||
page=1, page_size=50, username=None, action=None, keyword=None, success=None,
|
||||
start_time=None, end_time=None, db=self.session, _=self.admin,
|
||||
)
|
||||
|
||||
detail = json.loads(result["items"][0]["detail"])
|
||||
self.assertEqual(detail["recharge_accounts"][0]["username"], "douyu-login")
|
||||
self.assertEqual(detail["recharge_accounts"][0]["douyu_uid"], "10001")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -12,9 +12,10 @@ from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from web.backend.database import Base
|
||||
from web.backend.models import Account, DouyuTask, User
|
||||
from web.backend.models import Account, AuditLog, DouyuTask, User
|
||||
from web.backend.routers.douyu import supplier_recharge_callback
|
||||
from web.backend.services.douyu_runner import DouyuBatchRunner
|
||||
from web.backend.services.audit_service import record_audit
|
||||
from core.douyu import FishFinRechargeClient, FishFinRechargeConfig
|
||||
|
||||
|
||||
@@ -123,6 +124,26 @@ class DouyuGoldRechargeChannelTests(unittest.TestCase):
|
||||
"DYGFHISTORICAL-001",
|
||||
)
|
||||
|
||||
def test_recharge_audit_detail_includes_safe_account_identity(self):
|
||||
record_audit(
|
||||
self.session,
|
||||
self.session.get(User, self.task.created_by),
|
||||
action="recharge:douyu:create",
|
||||
detail={
|
||||
"recharge_accounts": [{
|
||||
"username": self.account.username,
|
||||
"douyu_uid": self.account.uid,
|
||||
"douyu_nickname": self.account.nickname,
|
||||
}],
|
||||
},
|
||||
)
|
||||
self.session.commit()
|
||||
|
||||
audit = self.session.query(AuditLog).one()
|
||||
self.assertIn("douyu_uid", audit.detail)
|
||||
self.assertIn("10001", audit.detail)
|
||||
self.assertNotIn("password", audit.detail)
|
||||
|
||||
@patch("web.backend.services.douyu_runner.FishFinRechargeClient")
|
||||
def test_supplier_channel_marks_non_200_create_response_as_failed(self, client_class):
|
||||
supplier = Mock()
|
||||
@@ -174,6 +195,10 @@ class DouyuGoldRechargeChannelTests(unittest.TestCase):
|
||||
self.assertEqual(self.account.bind_status, "gold_recharged")
|
||||
self.assertEqual(self.task.result["order_id"], "supplier-001")
|
||||
self.assertEqual(self.task.result["supplier_order_status"], 2)
|
||||
audit = self.session.query(AuditLog).filter(AuditLog.action == "recharge:douyu:callback").one()
|
||||
self.assertEqual(audit.username, "supplier")
|
||||
self.assertTrue(audit.success)
|
||||
self.assertNotIn("sign", audit.detail)
|
||||
# 同一终态回调可重复投递,不会改写已结束任务。
|
||||
asyncio.run(supplier_recharge_callback(request, self.session))
|
||||
self.session.refresh(self.task)
|
||||
|
||||
Reference in New Issue
Block a user