77 lines
3.0 KiB
Python
77 lines
3.0 KiB
Python
"""知识图谱穿透集成测试(需 PostgreSQL)。
|
||
|
||
验证 R2 关键能力:通过关系边的多跳穿透识别"疑似同一实控人",
|
||
以及本体约束对非法关系的拒绝。对应场景一(政企拆单+隐性实控人,R8)的图谱基础。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import pytest
|
||
|
||
from app.datahub.graph_repo import (
|
||
OntologyViolationError,
|
||
add_relationship,
|
||
find_related_entities,
|
||
upsert_entity,
|
||
)
|
||
from app.datahub.ontology import EntityType, RelationshipType
|
||
|
||
|
||
def test_upsert_entity_is_idempotent(session):
|
||
e1 = upsert_entity(session, EntityType.CUSTOMER, "CUST-001", "客户甲")
|
||
e2 = upsert_entity(session, EntityType.CUSTOMER, "CUST-001", "客户甲")
|
||
assert e1.id == e2.id
|
||
|
||
|
||
def test_ontology_violation_rejected(session):
|
||
contract = upsert_entity(session, EntityType.CONTRACT, "C-1")
|
||
customer = upsert_entity(session, EntityType.CUSTOMER, "CUST-2")
|
||
# 合同 —签约→ 客户 方向非法
|
||
with pytest.raises(OntologyViolationError):
|
||
add_relationship(session, RelationshipType.SIGNED, contract, customer)
|
||
|
||
|
||
def test_detect_shared_controller_across_customers(session):
|
||
"""模拟"8 个客户疑似同一实控人":多个客户经法人关联到同一实控自然人。
|
||
|
||
构图:每个客户 <-法定代表人- 各自法人;各法人 -关联-> 同一实控人。
|
||
从实控人出发,应能穿透到全部客户。
|
||
"""
|
||
controller = upsert_entity(session, EntityType.LEGAL_PERSON, "PER-CTRL", "实控人")
|
||
|
||
customers = []
|
||
for i in range(8):
|
||
cust = upsert_entity(session, EntityType.CUSTOMER, f"CUST-{i}", f"政企客户{i}")
|
||
rep = upsert_entity(session, EntityType.LEGAL_PERSON, f"PER-{i}", f"法人{i}")
|
||
# 法人 —法定代表人→ 客户
|
||
add_relationship(session, RelationshipType.LEGAL_REP_OF, rep, cust)
|
||
# 法人 —关联(亲属/实控)→ 实控人
|
||
add_relationship(session, RelationshipType.RELATED_TO, rep, controller)
|
||
customers.append(cust)
|
||
session.flush()
|
||
|
||
related = find_related_entities(session, controller.id, max_depth=3)
|
||
related_ids = {rid for rid, _ in related}
|
||
|
||
# 从实控人 3 跳内应能穿透到全部 8 个客户
|
||
for cust in customers:
|
||
assert cust.id in related_ids, f"未穿透到 {cust.business_key}"
|
||
|
||
|
||
def test_traversal_respects_max_depth(session):
|
||
a = upsert_entity(session, EntityType.LEGAL_PERSON, "A")
|
||
b = upsert_entity(session, EntityType.LEGAL_PERSON, "B")
|
||
c = upsert_entity(session, EntityType.CUSTOMER, "C")
|
||
add_relationship(session, RelationshipType.RELATED_TO, a, b)
|
||
add_relationship(session, RelationshipType.LEGAL_REP_OF, b, c)
|
||
session.flush()
|
||
|
||
# depth=1:从 A 只能到 B,到不了 C
|
||
ids_d1 = {rid for rid, _ in find_related_entities(session, a.id, max_depth=1)}
|
||
assert b.id in ids_d1
|
||
assert c.id not in ids_d1
|
||
|
||
# depth=2:能到 C
|
||
ids_d2 = {rid for rid, _ in find_related_entities(session, a.id, max_depth=2)}
|
||
assert c.id in ids_d2
|