netx/tests/test_port_traffic_compare.py
oliver 3908b78dbb Add ops task overview, chart ahead window, and WebCRT lifecycle status.
Unify live runners in /audit/tasks (including UME sync and connect tests), extend port-traffic compare past now via ahead_hours, and distinguish WebCRT connecting/ready/detached.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 14:05:59 +08:00

281 lines
9.2 KiB
Python

"""Unit tests for port traffic compare: interface + optional mapped baseline."""
from __future__ import annotations
import unittest
from datetime import datetime, timedelta
from uuid import uuid4
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool
from netx_api.db import Base
from netx_api.models import PortTrafficDevice, PortTrafficSample, PortTrafficSeries, PortTrafficTarget
from netx_api.port_traffic_service import baseline_offset_hours, compare_targets
class BaselineOffsetTests(unittest.TestCase):
def test_presets(self):
self.assertIsNone(baseline_offset_hours("off", 24, None))
self.assertEqual(baseline_offset_hours("shift", 24, None), 24.0)
self.assertEqual(baseline_offset_hours("day", 6, None), 24.0)
self.assertEqual(baseline_offset_hours("week", 24, None), 168.0)
self.assertEqual(baseline_offset_hours("custom", 24, 36), 36.0)
class CompareTargetsTests(unittest.TestCase):
def setUp(self):
engine = create_engine(
"sqlite://",
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
Base.metadata.create_all(bind=engine)
self.Session = sessionmaker(bind=engine)
self.db = self.Session()
self.device_id = uuid4().hex
self.device2_id = uuid4().hex
self.series_id = uuid4().hex
self.target_id = uuid4().hex
self.mapped_id = uuid4().hex
now = datetime(2026, 7, 31, 12, 0, 0)
self.now = now
self.db.add(
PortTrafficDevice(
id=self.device_id,
source="managed",
ne_id="ne1",
ne_name="R1",
vendor="Huawei",
status="running",
created_at=now,
updated_at=now,
)
)
self.db.add(
PortTrafficDevice(
id=self.device2_id,
source="managed",
ne_id="ne2",
ne_name="R2",
vendor="ZTE",
status="running",
created_at=now,
updated_at=now,
)
)
self.db.add(
PortTrafficSeries(
id=self.series_id,
device_id=self.device_id,
title="uplink1",
status="active",
created_at=now,
)
)
self.db.add(
PortTrafficTarget(
id=self.target_id,
device_id=self.device_id,
series_id=self.series_id,
source="managed",
target_id="ne1",
ne_name="R1",
vendor="Huawei",
ifname="Ethernet1/0/0",
status="active",
created_at=now,
)
)
self.db.add(
PortTrafficTarget(
id=self.mapped_id,
device_id=self.device2_id,
series_id="",
source="managed",
target_id="ne2",
ne_name="R2",
vendor="ZTE",
ifname="gei-0/0/1",
status="active",
created_at=now,
)
)
for i in range(3):
self.db.add(
PortTrafficSample(
id=uuid4().hex,
target_row_id=self.target_id,
series_id=self.series_id,
ts=now - timedelta(hours=1, minutes=30) + timedelta(minutes=30 * i),
in_bps=1000 + i,
out_bps=2000 + i,
raw_ok=True,
)
)
for i in range(3):
self.db.add(
PortTrafficSample(
id=uuid4().hex,
target_row_id=self.target_id,
series_id=self.series_id,
ts=now - timedelta(days=1, hours=1, minutes=30) + timedelta(minutes=30 * i),
in_bps=500 + i,
out_bps=800 + i,
raw_ok=True,
)
)
for i in range(2):
self.db.add(
PortTrafficSample(
id=uuid4().hex,
target_row_id=self.mapped_id,
series_id="",
ts=now - timedelta(hours=1) + timedelta(minutes=30 * i),
in_bps=90 + i,
out_bps=110 + i,
raw_ok=True,
)
)
self.db.commit()
def tearDown(self):
self.db.close()
def test_off_returns_current_only(self):
out = compare_targets(
self.db,
target_row_id=self.target_id,
range_hours=2,
baseline="off",
to_ts=self.now,
)
self.assertEqual(len(out.current), 3)
self.assertEqual(len(out.baseline), 0)
self.assertEqual(out.meta.target_id, self.target_id)
def test_day_baseline_same_port(self):
out = compare_targets(
self.db,
target_row_id=self.target_id,
range_hours=2,
baseline="day",
to_ts=self.now,
)
self.assertEqual(len(out.current), 3)
self.assertEqual(len(out.baseline), 3)
self.assertEqual(out.meta.offset_hours, 24.0)
for p in out.baseline:
self.assertIsNotNone(p.ts_raw)
self.assertGreaterEqual(p.ts, self.now - timedelta(hours=2))
self.assertLessEqual(p.ts, self.now + timedelta(seconds=5))
self.assertEqual(out.baseline[0].in_bps, 500.0)
def test_mapped_port_same_window(self):
out = compare_targets(
self.db,
target_row_id=self.target_id,
range_hours=2,
baseline="off",
baseline_target_id=self.mapped_id,
to_ts=self.now,
)
self.assertEqual(len(out.current), 3)
self.assertEqual(len(out.baseline), 2)
self.assertEqual(out.meta.baseline_target_id, self.mapped_id)
self.assertEqual(out.baseline[0].in_bps, 90.0)
self.assertIsNone(out.baseline[0].ts_raw)
def test_mapped_port_with_day_offset(self):
for i in range(2):
self.db.add(
PortTrafficSample(
id=uuid4().hex,
target_row_id=self.mapped_id,
ts=self.now - timedelta(days=1, hours=1) + timedelta(minutes=30 * i),
in_bps=40 + i,
out_bps=50 + i,
raw_ok=True,
)
)
self.db.commit()
out = compare_targets(
self.db,
target_row_id=self.target_id,
range_hours=2,
baseline="day",
baseline_target_id=self.mapped_id,
to_ts=self.now,
)
self.assertEqual(len(out.baseline), 2)
self.assertEqual(out.baseline[0].in_bps, 40.0)
self.assertIsNotNone(out.baseline[0].ts_raw)
def test_shift_baseline(self):
for i in range(2):
self.db.add(
PortTrafficSample(
id=uuid4().hex,
target_row_id=self.target_id,
series_id=self.series_id,
ts=self.now - timedelta(hours=3, minutes=30) + timedelta(minutes=30 * i),
in_bps=50 + i,
out_bps=60 + i,
raw_ok=True,
)
)
self.db.commit()
out = compare_targets(
self.db,
target_row_id=self.target_id,
range_hours=2,
baseline="shift",
to_ts=self.now,
)
self.assertGreaterEqual(len(out.baseline), 2)
self.assertEqual(out.meta.offset_hours, 2.0)
def test_ahead_hours_extends_baseline_past_now(self):
# Yesterday sample 30min after "now" clock — only visible when ahead_hours > 0.
future_raw = self.now - timedelta(days=1) + timedelta(minutes=30)
self.db.add(
PortTrafficSample(
id=uuid4().hex,
target_row_id=self.target_id,
series_id=self.series_id,
ts=future_raw,
in_bps=777.0,
out_bps=888.0,
raw_ok=True,
)
)
self.db.commit()
without = compare_targets(
self.db,
target_row_id=self.target_id,
range_hours=2,
baseline="day",
ahead_hours=0,
to_ts=self.now,
)
self.assertFalse(any(abs((p.ts_raw or p.ts) - future_raw).total_seconds() < 1 for p in without.baseline))
with_ahead = compare_targets(
self.db,
target_row_id=self.target_id,
range_hours=2,
baseline="day",
ahead_hours=1,
to_ts=self.now,
)
self.assertEqual(with_ahead.meta.ahead_hours, 1.0)
hit = [p for p in with_ahead.baseline if p.in_bps == 777.0]
self.assertEqual(len(hit), 1)
self.assertGreater(hit[0].ts, self.now)
self.assertLessEqual(hit[0].ts, self.now + timedelta(hours=1, seconds=5))
if __name__ == "__main__":
unittest.main()