deep-risk/backend/tests/services/risk_detection/engine/test_dependency_resolver.py
2025-12-14 20:08:27 +08:00

305 lines
10 KiB
Python
Raw 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.

"""
依赖解析器单元测试
"""
import pytest
from typing import List
from unittest.mock import Mock, MagicMock
from app.models.risk_detection import DetectionRule, RiskLevel
from app.services.risk_detection.engine.dependency_resolver import (
DependencyResolver,
DependencyGraph,
DependencyNode,
)
class TestDependencyNode:
"""依赖节点测试"""
def test_init(self):
"""测试节点初始化"""
node = DependencyNode(
rule_id="rule_1",
algorithm_code="ALGO_1",
dependencies=["rule_0"],
dependents=["rule_2"]
)
assert node.rule_id == "rule_1"
assert node.algorithm_code == "ALGO_1"
assert node.dependencies == ["rule_0"]
assert node.dependents == ["rule_2"]
assert node.level == 0
class TestDependencyGraph:
"""依赖图测试"""
def setup_method(self):
"""每个测试方法前执行"""
self.graph = DependencyGraph()
def test_add_node(self):
"""测试添加节点"""
self.graph.add_node("rule_1", "ALGO_1")
assert "rule_1" in self.graph.nodes
assert self.graph.nodes["rule_1"].rule_id == "rule_1"
assert self.graph.nodes["rule_1"].algorithm_code == "ALGO_1"
assert self.graph.graph["rule_1"] == []
assert self.graph.reverse_graph["rule_1"] == []
assert self.graph.in_degree["rule_1"] == 0
def test_add_edge(self):
"""测试添加依赖边"""
# 添加节点
self.graph.add_node("rule_1", "ALGO_1")
self.graph.add_node("rule_2", "ALGO_2")
# 添加边rule_2 依赖 rule_1
self.graph.add_edge("rule_2", "rule_1")
# 验证依赖关系
assert "rule_1" in self.graph.nodes["rule_2"].dependencies
assert "rule_2" in self.graph.nodes["rule_1"].dependents
# 验证入度
assert self.graph.in_degree["rule_1"] == 0
assert self.graph.in_degree["rule_2"] == 1
def test_add_edge_invalid_rule(self):
"""测试添加边时规则不存在"""
self.graph.add_node("rule_1", "ALGO_1")
with pytest.raises(ValueError, match="规则不存在"):
self.graph.add_edge("rule_2", "rule_1")
def test_has_cycle_no_cycle(self):
"""测试无循环依赖"""
self.graph.add_node("rule_1", "ALGO_1")
self.graph.add_node("rule_2", "ALGO_2")
self.graph.add_node("rule_3", "ALGO_3")
# rule_2 依赖 rule_1, rule_3 依赖 rule_2
self.graph.add_edge("rule_2", "rule_1")
self.graph.add_edge("rule_3", "rule_2")
has_cycle, cycle_path = self.graph.has_cycle()
assert not has_cycle
assert cycle_path is None
def test_has_cycle_with_cycle(self):
"""测试有循环依赖"""
self.graph.add_node("rule_1", "ALGO_1")
self.graph.add_node("rule_2", "ALGO_2")
self.graph.add_node("rule_3", "ALGO_3")
# 创建循环rule_1 -> rule_2 -> rule_3 -> rule_1
self.graph.add_edge("rule_2", "rule_1")
self.graph.add_edge("rule_3", "rule_2")
self.graph.add_edge("rule_1", "rule_3")
has_cycle, cycle_path = self.graph.has_cycle()
assert has_cycle
assert cycle_path is not None
assert len(cycle_path) >= 3 # 至少包含3个节点
def test_calculate_levels(self):
"""测试计算层级"""
self.graph.add_node("rule_1", "ALGO_1")
self.graph.add_node("rule_2", "ALGO_2")
self.graph.add_node("rule_3", "ALGO_3")
# rule_2 依赖 rule_1, rule_3 依赖 rule_2
self.graph.add_edge("rule_2", "rule_1")
self.graph.add_edge("rule_3", "rule_2")
self.graph.calculate_levels()
# 验证层级
assert self.graph.nodes["rule_1"].level == 0
assert self.graph.nodes["rule_2"].level == 1
assert self.graph.nodes["rule_3"].level == 2
def test_get_levels(self):
"""测试获取层级分组"""
self.graph.add_node("rule_1", "ALGO_1")
self.graph.add_node("rule_2", "ALGO_2")
self.graph.add_node("rule_3", "ALGO_3")
# rule_2 依赖 rule_1, rule_3 依赖 rule_2
self.graph.add_edge("rule_2", "rule_1")
self.graph.add_edge("rule_3", "rule_2")
self.graph.calculate_levels()
levels = self.graph.get_levels()
# 验证层级结构
assert len(levels) == 3 # 3个层级
assert "rule_1" in levels[0]
assert "rule_2" in levels[1]
assert "rule_3" in levels[2]
def test_to_dict(self):
"""测试转换为字典"""
self.graph.add_node("rule_1", "ALGO_1")
self.graph.add_node("rule_2", "ALGO_2")
self.graph.add_edge("rule_2", "rule_1")
self.graph.calculate_levels()
result = self.graph.to_dict()
assert "nodes" in result
assert "levels" in result
assert len(result["nodes"]) == 2
assert len(result["levels"]) >= 1
class TestDependencyResolver:
"""依赖解析器测试"""
def setup_method(self):
"""每个测试方法前执行"""
self.resolver = DependencyResolver()
def _create_mock_rule(self, rule_id: str, algorithm_code: str, dependencies: List[str] = None) -> DetectionRule:
"""创建模拟规则"""
rule = Mock(spec=DetectionRule)
rule.rule_id = rule_id
rule.algorithm_code = algorithm_code
rule.parameters = {"dependencies": dependencies} if dependencies else None
return rule
def test_analyze_simple_dependencies(self):
"""测试简单依赖分析"""
rules = [
self._create_mock_rule("rule_1", "ALGO_1"),
self._create_mock_rule("rule_2", "ALGO_2", ["rule_1"]),
]
graph = self.resolver.analyze(rules)
assert graph is not None
assert "rule_1" in graph.nodes
assert "rule_2" in graph.nodes
assert "rule_1" in graph.nodes["rule_2"].dependencies
def test_analyze_with_cycle(self):
"""测试循环依赖检测"""
rules = [
self._create_mock_rule("rule_1", "ALGO_1", ["rule_2"]),
self._create_mock_rule("rule_2", "ALGO_2", ["rule_1"]),
]
with pytest.raises(ValueError, match="检测到循环依赖"):
self.resolver.analyze(rules)
def test_analyze_with_default_dependencies(self):
"""测试使用默认依赖关系"""
# 设置默认依赖
self.resolver.DEFAULT_ALGORITHM_DEPENDENCIES = {
"ALGO_2": ["ALGO_1"],
}
rules = [
self._create_mock_rule("rule_1", "ALGO_1"),
self._create_mock_rule("rule_2", "ALGO_2"), # 无显式依赖,使用默认
]
graph = self.resolver.analyze(rules, use_default_dependencies=True)
assert "rule_1" in graph.nodes["rule_2"].dependencies
def test_validate_valid_dependencies(self):
"""测试有效依赖验证"""
rules = [
self._create_mock_rule("rule_1", "ALGO_1"),
self._create_mock_rule("rule_2", "ALGO_2", ["rule_1"]),
]
is_valid, error = self.resolver.validate(rules)
assert is_valid
assert error is None
def test_validate_invalid_dependencies(self):
"""测试无效依赖验证"""
rules = [
self._create_mock_rule("rule_1", "ALGO_1", ["rule_2"]),
self._create_mock_rule("rule_2", "ALGO_2", ["rule_1"]),
]
is_valid, error = self.resolver.validate(rules)
assert not is_valid
assert error is not None
assert "循环依赖" in error
def test_extract_dependencies_from_params(self):
"""测试从参数中提取依赖"""
rule = self._create_mock_rule("rule_1", "ALGO_1", ["rule_2", "rule_3"])
rule_dict = {"ALGO_2": "rule_2", "ALGO_3": "rule_3"}
deps = self.resolver._extract_dependencies(rule, rule_dict, False)
assert "rule_2" in deps
assert "rule_3" in deps
def test_extract_dependencies_from_algorithm_code(self):
"""测试从算法代码提取依赖"""
rule = self._create_mock_rule("rule_1", "ALGO_1") # 无参数依赖
rule_dict = {"ALGO_2": "rule_2"}
# 设置默认依赖
self.resolver.DEFAULT_ALGORITHM_DEPENDENCIES = {
"ALGO_1": ["ALGO_2"],
}
deps = self.resolver._extract_dependencies(rule, rule_dict, True)
assert "rule_2" in deps
def test_get_execution_order(self):
"""测试获取执行顺序"""
rules = [
self._create_mock_rule("rule_1", "ALGO_1"),
self._create_mock_rule("rule_2", "ALGO_2", ["rule_1"]),
self._create_mock_rule("rule_3", "ALGO_3", ["rule_2"]),
]
self.resolver.analyze(rules)
execution_order = self.resolver.get_execution_order()
assert len(execution_order) == 3
# 验证顺序rule_1 -> rule_2 -> rule_3
assert execution_order.index("rule_1") < execution_order.index("rule_2")
assert execution_order.index("rule_2") < execution_order.index("rule_3")
def test_get_execution_levels(self):
"""测试获取执行层级"""
rules = [
self._create_mock_rule("rule_1", "ALGO_1"),
self._create_mock_rule("rule_2", "ALGO_2", ["rule_1"]),
self._create_mock_rule("rule_3", "ALGO_3", ["rule_1"]),
]
self.resolver.analyze(rules)
levels = self.resolver.get_execution_levels()
assert len(levels) == 2 # 两个层级
assert "rule_1" in levels[0]
assert "rule_2" in levels[1] or "rule_3" in levels[1]
def test_get_execution_order_without_analyze(self):
"""测试未分析依赖时获取执行顺序"""
with pytest.raises(ValueError, match="尚未进行依赖分析"):
self.resolver.get_execution_order()
def test_get_execution_levels_without_analyze(self):
"""测试未分析依赖时获取执行层级"""
with pytest.raises(ValueError, match="尚未进行依赖分析"):
self.resolver.get_execution_levels()