fix: recover bgp anomaly and incident generation
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"""Tests for BGP observability helpers."""
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from datetime import UTC, datetime, timedelta
|
||||
|
||||
import pytest
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
@@ -10,7 +10,10 @@ from app.api.v1.bgp import BGP_SOURCES
|
||||
from app.core.security import get_current_user
|
||||
from app.db.session import get_db
|
||||
from app.main import app
|
||||
from app.services.bgp_detectors import detect_mass_withdrawal_anomalies
|
||||
from app.services.bgp_detectors import (
|
||||
detect_mass_withdrawal_anomalies,
|
||||
detect_origin_change_anomalies,
|
||||
)
|
||||
from app.services.collectors.bgp_common import (
|
||||
create_bgp_anomalies_for_batch,
|
||||
save_bgp_observations_for_batch,
|
||||
@@ -223,6 +226,95 @@ def test_detect_mass_withdrawal_anomalies():
|
||||
assert anomalies[0].prefix == "203.0.113.0/24"
|
||||
|
||||
|
||||
def test_detect_origin_change_anomalies_creates_conflict_without_baseline():
|
||||
events = [
|
||||
{
|
||||
"metadata": {
|
||||
"prefix": "203.0.113.0/24",
|
||||
"origin_asn": 64496,
|
||||
"collector": "rrc00",
|
||||
"collector_location": {
|
||||
"country": "Netherlands",
|
||||
"city": "Amsterdam",
|
||||
"latitude": 52.3676,
|
||||
"longitude": 4.9041,
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"prefix": "203.0.113.0/24",
|
||||
"origin_asn": 64497,
|
||||
"collector": "rrc01",
|
||||
"collector_location": {
|
||||
"country": "United Kingdom",
|
||||
"city": "London",
|
||||
"latitude": 51.5072,
|
||||
"longitude": -0.1276,
|
||||
},
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
anomalies = detect_origin_change_anomalies(
|
||||
source="ris_live_bgp",
|
||||
snapshot_id=1,
|
||||
task_id=2,
|
||||
events=events,
|
||||
previous_origin_map={},
|
||||
)
|
||||
|
||||
assert len(anomalies) == 2
|
||||
assert {item.anomaly_type for item in anomalies} == {"origin_conflict"}
|
||||
assert anomalies[0].peer_scope == ["rrc00", "rrc01"]
|
||||
|
||||
|
||||
def test_detect_mass_withdrawal_anomalies_accepts_cross_collector_pair():
|
||||
events = [
|
||||
{
|
||||
"metadata": {
|
||||
"prefix": "203.0.113.0/24",
|
||||
"origin_asn": 64496,
|
||||
"event_type": "withdrawal",
|
||||
"collector": "rrc00",
|
||||
"peer_asn": 3333,
|
||||
"collector_location": {
|
||||
"country": "Netherlands",
|
||||
"city": "Amsterdam",
|
||||
"latitude": 52.3676,
|
||||
"longitude": 4.9041,
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"prefix": "203.0.113.0/24",
|
||||
"origin_asn": 64496,
|
||||
"event_type": "withdrawal",
|
||||
"collector": "rrc01",
|
||||
"peer_asn": 3334,
|
||||
"collector_location": {
|
||||
"country": "United Kingdom",
|
||||
"city": "London",
|
||||
"latitude": 51.5072,
|
||||
"longitude": -0.1276,
|
||||
},
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
anomalies = detect_mass_withdrawal_anomalies(
|
||||
source="ris_live_bgp",
|
||||
snapshot_id=1,
|
||||
task_id=2,
|
||||
events=events,
|
||||
)
|
||||
|
||||
assert len(anomalies) == 1
|
||||
assert anomalies[0].severity == "medium"
|
||||
assert anomalies[0].evidence["collector_count"] == 2
|
||||
|
||||
|
||||
def test_bgp_incident_to_dict():
|
||||
incident = BGPIncident(
|
||||
source="ris_live_bgp",
|
||||
@@ -405,6 +497,7 @@ async def test_infer_related_infrastructure_links_nearby_cables():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_build_bgp_collector_coverage_summarizes_observations():
|
||||
now = datetime.now(UTC)
|
||||
obs_one = BGPObservation(
|
||||
source="ris_live_bgp",
|
||||
collector="rrc00",
|
||||
@@ -412,7 +505,7 @@ async def test_build_bgp_collector_coverage_summarizes_observations():
|
||||
origin_asn=64496,
|
||||
peer_asn=3333,
|
||||
event_type="announcement",
|
||||
observed_at=datetime(2026, 3, 30, 10, 0, tzinfo=UTC),
|
||||
observed_at=now,
|
||||
collector_geo={"city": "Amsterdam", "country": "Netherlands"},
|
||||
)
|
||||
obs_two = BGPObservation(
|
||||
@@ -422,7 +515,7 @@ async def test_build_bgp_collector_coverage_summarizes_observations():
|
||||
origin_asn=64497,
|
||||
peer_asn=3334,
|
||||
event_type="withdrawal",
|
||||
observed_at=datetime(2026, 3, 30, 10, 5, tzinfo=UTC),
|
||||
observed_at=now + timedelta(minutes=5),
|
||||
collector_geo={"city": "Amsterdam", "country": "Netherlands"},
|
||||
)
|
||||
db = _FakeAsyncSession([[obs_one, obs_two]])
|
||||
@@ -543,11 +636,22 @@ async def test_create_bgp_anomalies_for_batch_skips_existing_entity_keys():
|
||||
extra_data={"prefix": "203.0.113.0/24", "origin_asn": 64496},
|
||||
)
|
||||
existing_key = ("origin_change:203.0.113.0/24:64497",)
|
||||
existing_anomaly = BGPAnomaly(
|
||||
source="ris_live_bgp",
|
||||
anomaly_type="origin_change",
|
||||
severity="critical",
|
||||
status="active",
|
||||
entity_key="origin_change:203.0.113.0/24:64497",
|
||||
prefix="203.0.113.0/24",
|
||||
origin_asn=64496,
|
||||
new_origin_asn=64497,
|
||||
)
|
||||
db = _FakeAsyncSession([
|
||||
[],
|
||||
[],
|
||||
[previous_record],
|
||||
[existing_key],
|
||||
[existing_anomaly],
|
||||
])
|
||||
events = [
|
||||
{
|
||||
@@ -578,7 +682,7 @@ async def test_create_bgp_anomalies_for_batch_skips_existing_entity_keys():
|
||||
|
||||
assert created == 0
|
||||
assert len(db.added) == 0
|
||||
incident_mock.assert_not_awaited()
|
||||
incident_mock.assert_awaited_once()
|
||||
|
||||
|
||||
async def _bgp_test_client(db_session):
|
||||
@@ -740,6 +844,7 @@ async def test_bgp_event_summary_api_returns_aggregates():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bgp_collectors_api_returns_coverage():
|
||||
now = datetime.now(UTC)
|
||||
observation = BGPObservation(
|
||||
id=1,
|
||||
source="ris_live_bgp",
|
||||
@@ -748,7 +853,7 @@ async def test_bgp_collectors_api_returns_coverage():
|
||||
prefix="203.0.113.0/24",
|
||||
event_type="announcement",
|
||||
origin_asn=64496,
|
||||
observed_at=datetime(2026, 3, 30, 10, 0, tzinfo=UTC),
|
||||
observed_at=now,
|
||||
collector_geo={"city": "Amsterdam", "country": "Netherlands"},
|
||||
)
|
||||
db = _FakeAsyncSession([[observation], [observation]])
|
||||
|
||||
Reference in New Issue
Block a user