test: improve coverage across frontend and backend
Frontend (22 → 37 tests): - canvasStore: add tests for onNodesChange, onEdgesChange, onConnect, addNode with parentId, updateEdge, deleteEdge, setProxmoxContainerMode ON/OFF, loadCanvas parent-before-child ordering - edgeColors: 6 tests for EDGE_DEFAULT_COLORS (all types, hex format, values) Backend (40 → 80 tests): - test_canvas (new): load empty canvas, default viewport, save creates/updates/ deletes nodes+edges, viewport upsert, custom_colors, edge custom_color+path_style, auth guard - test_fingerprint (new): match_port (known, unknown, wrong protocol, banner match, banner no-match, no banner), fingerprint_ports (matched, unknown, mixed, empty, default protocol), suggest_node_type (proxmox, server, generic, priority) - test_nodes: update/delete 404, custom_colors CRUD, container_mode CRUD, auth guard - test_edges: update edge, update/delete 404, custom_color, path_style, auth guard
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from app.services.fingerprint import fingerprint_ports, match_port, suggest_node_type
|
||||
|
||||
MOCK_SIGNATURES = [
|
||||
{"port": 80, "protocol": "tcp", "banner_regex": None, "service_name": "HTTP", "icon": "🌐", "category": "web", "suggested_node_type": "server"},
|
||||
{"port": 443, "protocol": "tcp", "banner_regex": None, "service_name": "HTTPS", "icon": "🔒", "category": "web", "suggested_node_type": "server"},
|
||||
{"port": 22, "protocol": "tcp", "banner_regex": None, "service_name": "SSH", "icon": "🖥", "category": "remote", "suggested_node_type": None},
|
||||
{"port": 8006, "protocol": "tcp", "banner_regex": "proxmox", "service_name": "Proxmox API", "icon": "🔧", "category": "web", "suggested_node_type": "proxmox"},
|
||||
{"port": 8006, "protocol": "tcp", "banner_regex": None, "service_name": "Web UI", "icon": "🌐", "category": "web", "suggested_node_type": "server"},
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_signatures():
|
||||
with patch("app.services.fingerprint._load", return_value=MOCK_SIGNATURES):
|
||||
yield
|
||||
|
||||
|
||||
# ── match_port ────────────────────────────────────────────────────────────────
|
||||
|
||||
def test_match_port_known_port():
|
||||
result = match_port(80, "tcp")
|
||||
assert result is not None
|
||||
assert result["service_name"] == "HTTP"
|
||||
|
||||
|
||||
def test_match_port_unknown_port():
|
||||
result = match_port(9999, "tcp")
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_match_port_wrong_protocol():
|
||||
result = match_port(80, "udp")
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_match_port_with_banner_match():
|
||||
result = match_port(8006, "tcp", banner="proxmox virtual environment")
|
||||
assert result is not None
|
||||
assert result["service_name"] == "Proxmox API"
|
||||
|
||||
|
||||
def test_match_port_with_banner_no_match_falls_through_to_next():
|
||||
# banner doesn't match proxmox regex → skips first sig, matches second (no banner_regex)
|
||||
result = match_port(8006, "tcp", banner="something else")
|
||||
assert result is not None
|
||||
assert result["service_name"] == "Web UI"
|
||||
|
||||
|
||||
def test_match_port_no_banner_matches_first_sig():
|
||||
# no banner → banner check is skipped entirely, first matching sig wins
|
||||
result = match_port(8006, "tcp", banner=None)
|
||||
assert result is not None
|
||||
assert result["service_name"] == "Proxmox API"
|
||||
|
||||
|
||||
# ── fingerprint_ports ─────────────────────────────────────────────────────────
|
||||
|
||||
def test_fingerprint_ports_known_ports():
|
||||
results = fingerprint_ports([{"port": 80, "protocol": "tcp"}, {"port": 443, "protocol": "tcp"}])
|
||||
assert len(results) == 2
|
||||
assert results[0]["service_name"] == "HTTP"
|
||||
assert results[1]["service_name"] == "HTTPS"
|
||||
assert results[0]["port"] == 80
|
||||
assert results[0]["category"] == "web"
|
||||
|
||||
|
||||
def test_fingerprint_ports_unknown_port():
|
||||
results = fingerprint_ports([{"port": 9999, "protocol": "tcp"}])
|
||||
assert len(results) == 1
|
||||
assert results[0]["service_name"] == "unknown_service"
|
||||
assert results[0]["icon"] is None
|
||||
assert results[0]["category"] is None
|
||||
|
||||
|
||||
def test_fingerprint_ports_mixed():
|
||||
results = fingerprint_ports([{"port": 22, "protocol": "tcp"}, {"port": 9999, "protocol": "tcp"}])
|
||||
assert results[0]["service_name"] == "SSH"
|
||||
assert results[1]["service_name"] == "unknown_service"
|
||||
|
||||
|
||||
def test_fingerprint_ports_empty():
|
||||
assert fingerprint_ports([]) == []
|
||||
|
||||
|
||||
def test_fingerprint_ports_defaults_protocol_to_tcp():
|
||||
results = fingerprint_ports([{"port": 80}])
|
||||
assert results[0]["service_name"] == "HTTP"
|
||||
|
||||
|
||||
# ── suggest_node_type ─────────────────────────────────────────────────────────
|
||||
|
||||
def test_suggest_node_type_returns_proxmox_from_port():
|
||||
result = suggest_node_type([{"port": 8006, "protocol": "tcp"}])
|
||||
assert result == "proxmox"
|
||||
|
||||
|
||||
def test_suggest_node_type_returns_server_from_http():
|
||||
result = suggest_node_type([{"port": 80, "protocol": "tcp"}])
|
||||
assert result == "server"
|
||||
|
||||
|
||||
def test_suggest_node_type_no_match_returns_generic():
|
||||
result = suggest_node_type([{"port": 9999, "protocol": "tcp"}])
|
||||
assert result == "generic"
|
||||
|
||||
|
||||
def test_suggest_node_type_empty_returns_generic():
|
||||
assert suggest_node_type([]) == "generic"
|
||||
|
||||
|
||||
def test_suggest_node_type_priority_proxmox_over_server():
|
||||
# both proxmox and server ports open → proxmox wins (higher priority)
|
||||
result = suggest_node_type([{"port": 80, "protocol": "tcp"}, {"port": 8006, "protocol": "tcp"}])
|
||||
assert result == "proxmox"
|
||||
Reference in New Issue
Block a user