fix: 统一mock流水可识别身份证来源
This commit is contained in:
@@ -2,8 +2,121 @@
|
||||
StatementService 主绑定注入测试
|
||||
"""
|
||||
|
||||
from collections import Counter, defaultdict
|
||||
|
||||
from services.file_service import FileService
|
||||
from services.statement_service import StatementService
|
||||
from services.statement_rule_samples import (
|
||||
DEFAULT_LARGE_TRANSACTION_THRESHOLDS,
|
||||
build_large_transaction_seed_statements,
|
||||
)
|
||||
|
||||
|
||||
def test_generate_statements_should_include_seeded_samples_before_noise():
|
||||
"""生成流水时必须先混入固定命中样本,而不是纯随机噪声。"""
|
||||
service = StatementService()
|
||||
|
||||
statements = service._generate_statements(group_id=1000, log_id=20001, count=30)
|
||||
|
||||
assert len(statements) >= 30
|
||||
assert any(item["userMemo"] == "购买房产首付款" for item in statements)
|
||||
|
||||
|
||||
def test_large_transaction_seed_should_cover_all_eight_rules():
|
||||
"""大额交易样本生成器必须覆盖 8 条已实现规则的关键口径。"""
|
||||
statements = build_large_transaction_seed_statements(group_id=1000, log_id=20001)
|
||||
|
||||
assert any(
|
||||
item["userMemo"] == "购买房产首付款" and item["drAmount"] > 0
|
||||
for item in statements
|
||||
)
|
||||
assert any(
|
||||
"税" in item["userMemo"] and item["drAmount"] > 0
|
||||
for item in statements
|
||||
)
|
||||
assert any(
|
||||
item["crAmount"] > DEFAULT_LARGE_TRANSACTION_THRESHOLDS["SINGLE_TRANSACTION_AMOUNT"]
|
||||
for item in statements
|
||||
)
|
||||
assert sum(
|
||||
1
|
||||
for item in statements
|
||||
if item["customerName"] == "浙江远望贸易有限公司" and item["crAmount"] > 0
|
||||
) >= 3
|
||||
assert sum(
|
||||
1
|
||||
for item in statements
|
||||
if item["cashType"] == "现金存款"
|
||||
and item["crAmount"] > DEFAULT_LARGE_TRANSACTION_THRESHOLDS["LARGE_CASH_DEPOSIT"]
|
||||
) >= 1
|
||||
assert any(
|
||||
item["userMemo"] == "手机银行转账"
|
||||
and item["drAmount"] > DEFAULT_LARGE_TRANSACTION_THRESHOLDS["FREQUENT_TRANSFER"]
|
||||
for item in statements
|
||||
)
|
||||
|
||||
same_day_cash_deposits = [
|
||||
item for item in statements
|
||||
if item["cretNo"] == "330101198801010011"
|
||||
and item["trxDate"].startswith("2026-03-10")
|
||||
and item["crAmount"] > DEFAULT_LARGE_TRANSACTION_THRESHOLDS["LARGE_CASH_DEPOSIT"]
|
||||
]
|
||||
assert len(same_day_cash_deposits) >= (
|
||||
DEFAULT_LARGE_TRANSACTION_THRESHOLDS["FREQUENT_CASH_DEPOSIT"] + 1
|
||||
)
|
||||
|
||||
|
||||
def test_large_transaction_seed_income_should_avoid_salary_exclusion():
|
||||
"""收入样本不得误带工资代发关键词,否则会被后端过滤。"""
|
||||
statements = build_large_transaction_seed_statements(group_id=1000, log_id=20001)
|
||||
|
||||
income_samples = [item for item in statements if item["crAmount"] > 0]
|
||||
assert income_samples
|
||||
assert all(item["customerName"] != "浙江兰溪农村商业银行股份有限公司" for item in income_samples)
|
||||
assert all(
|
||||
keyword not in item["userMemo"]
|
||||
for item in income_samples
|
||||
for keyword in ("代发", "工资", "奖金", "薪酬", "薪金")
|
||||
)
|
||||
|
||||
|
||||
def test_generate_statements_should_fill_noise_up_to_requested_count():
|
||||
"""样本不足请求总数时,服务层需要自动补齐噪声流水。"""
|
||||
service = StatementService()
|
||||
|
||||
statements = service._generate_statements(group_id=1000, log_id=20001, count=80)
|
||||
|
||||
assert len(statements) == 80
|
||||
|
||||
|
||||
def test_generate_statements_should_only_use_recognizable_identity_cards():
|
||||
"""命中样本和随机噪声都只能使用现库可识别的身份证号。"""
|
||||
service = StatementService()
|
||||
|
||||
statements = service._generate_statements(group_id=1000, log_id=20005, count=1600)
|
||||
|
||||
assert {item["cretNo"] for item in statements}.issubset(
|
||||
{
|
||||
"330101198801010011",
|
||||
"330101199001010022",
|
||||
"330101198802020033",
|
||||
"330101199202020044",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def test_get_bank_statement_should_keep_same_cached_result_for_same_log_id():
|
||||
"""同一 logId 首次生成后应复用缓存,避免分页结果漂移。"""
|
||||
service = StatementService()
|
||||
|
||||
page1 = service.get_bank_statement(
|
||||
{"groupId": 1000, "logId": 30001, "pageNow": 1, "pageSize": 20}
|
||||
)
|
||||
page2 = service.get_bank_statement(
|
||||
{"groupId": 1000, "logId": 30001, "pageNow": 1, "pageSize": 20}
|
||||
)
|
||||
|
||||
assert page1["data"]["bankStatementList"] == page2["data"]["bankStatementList"]
|
||||
|
||||
|
||||
def test_get_bank_statement_uses_primary_binding_from_file_service(monkeypatch):
|
||||
@@ -45,3 +158,72 @@ def test_get_bank_statement_uses_primary_binding_from_file_service(monkeypatch):
|
||||
assert all(item["leName"] == record.primary_enterprise_name for item in statements)
|
||||
assert all(item["accountMaskNo"] == record.primary_account_no for item in statements)
|
||||
|
||||
|
||||
def test_get_bank_statement_contains_large_transaction_hit_samples(monkeypatch):
|
||||
"""流水 Mock 首次生成时必须稳定包含可命中大额交易规则的样本簇。"""
|
||||
file_service = FileService()
|
||||
statement_service = StatementService(file_service=file_service)
|
||||
|
||||
monkeypatch.setattr(
|
||||
file_service,
|
||||
"_generate_primary_binding",
|
||||
lambda: ("命中主体", "6222000099998888"),
|
||||
)
|
||||
|
||||
response = file_service.fetch_inner_flow(
|
||||
{
|
||||
"groupId": 1001,
|
||||
"customerNo": "customer_large_transaction",
|
||||
"dataChannelCode": "test",
|
||||
"requestDateId": 20240101,
|
||||
"dataStartDateId": 20240101,
|
||||
"dataEndDateId": 20240131,
|
||||
"uploadUserId": 902001,
|
||||
}
|
||||
)
|
||||
log_id = response["data"][0]
|
||||
|
||||
statement_response = statement_service.get_bank_statement(
|
||||
{
|
||||
"groupId": 1001,
|
||||
"logId": log_id,
|
||||
"pageNow": 1,
|
||||
"pageSize": 2000,
|
||||
}
|
||||
)
|
||||
statements = statement_response["data"]["bankStatementList"]
|
||||
|
||||
assert statements
|
||||
assert any(
|
||||
item["cretNo"] in {
|
||||
"330101198801010011",
|
||||
"330101199001010022",
|
||||
"330101198802020033",
|
||||
"330101199202020044",
|
||||
}
|
||||
for item in statements
|
||||
)
|
||||
assert any("房产首付款" in item["userMemo"] for item in statements)
|
||||
assert any("税" in item["userMemo"] or "税务" in item["customerName"] for item in statements)
|
||||
|
||||
income_amounts = defaultdict(float)
|
||||
cash_deposit_daily_counter = Counter()
|
||||
has_large_transfer = False
|
||||
|
||||
for item in statements:
|
||||
if (
|
||||
item["cretNo"] == "330101198802020033"
|
||||
and item["customerName"] == "浙江远望贸易有限公司"
|
||||
and item["crAmount"] > 0
|
||||
):
|
||||
income_amounts[(item["cretNo"], item["customerName"])] += item["crAmount"]
|
||||
|
||||
if item["crAmount"] > 2000001 and "现金" in item["cashType"]:
|
||||
cash_deposit_daily_counter[(item["cretNo"], item["trxDate"][:10])] += 1
|
||||
|
||||
if item["drAmount"] > 100001 and item["userMemo"] == "手机银行转账":
|
||||
has_large_transfer = True
|
||||
|
||||
assert any(amount > 50000001 for amount in income_amounts.values())
|
||||
assert any(count >= 6 for count in cash_deposit_daily_counter.values())
|
||||
assert has_large_transfer
|
||||
|
||||
Reference in New Issue
Block a user