mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 11:50:44 +08:00
Decouple cutover HF from portrait and tighten ops UI.
Portrait stays on biz-monitor; cutover create/ensure owns purpose=cutover_hf slots with project collect override, multi-interval bindings, window pause, and industrial detail UX. Also expand ZTE collect/config parsers and TextFSM for route/BGP/L2VPN/VRRP coverage. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
a65c9fc420
commit
285c19e5a3
67 changed files with 9510 additions and 723 deletions
80
tests/test_iface_normalize.py
Normal file
80
tests/test_iface_normalize.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
"""Unit tests for iface normalize + subinterface port-map inheritance."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from netx_api.biz_state.compare_engine import apply_port_map, compare_rows
|
||||
from netx_api.biz_state.iface_normalize import (
|
||||
default_zte_iface_normalize_rules,
|
||||
normalize_iface_name,
|
||||
normalize_iface_rules,
|
||||
resolve_mapped_iface,
|
||||
)
|
||||
|
||||
|
||||
class IfaceNormalizeTests(unittest.TestCase):
|
||||
def test_prefix_rules_longest_first(self) -> None:
|
||||
rules = default_zte_iface_normalize_rules()
|
||||
self.assertEqual(normalize_iface_name("GE1.100", rules), "gei1.100")
|
||||
self.assertEqual(normalize_iface_name("SG11", rules), "smartgroup11")
|
||||
self.assertEqual(normalize_iface_name("XGE-0/1/0/1", rules), "xgei-0/1/0/1")
|
||||
self.assertEqual(normalize_iface_name("XXVGE-0/2/0/10", rules), "xxvgei-0/2/0/10")
|
||||
self.assertEqual(normalize_iface_name("CGE0/4/1/3", rules), "cgei0/4/1/3")
|
||||
|
||||
def test_does_not_mangle_canonical(self) -> None:
|
||||
rules = default_zte_iface_normalize_rules()
|
||||
self.assertEqual(normalize_iface_name("gei-0/0/0/1", rules), "gei-0/0/0/1")
|
||||
self.assertEqual(normalize_iface_name("smartgroup11", rules), "smartgroup11")
|
||||
self.assertEqual(normalize_iface_name("xgei-0/3/1/20.219", rules), "xgei-0/3/1/20.219")
|
||||
|
||||
def test_parse_text_rules(self) -> None:
|
||||
rules = normalize_iface_rules("GE,gei\n# comment\nSG\tsmartgroup\n")
|
||||
self.assertEqual(rules[0]["from"], "GE")
|
||||
self.assertEqual(rules[1]["from"], "SG")
|
||||
|
||||
|
||||
class PortMapSubifTests(unittest.TestCase):
|
||||
def test_exact_wins_over_parent(self) -> None:
|
||||
pmap = {
|
||||
"gei-0/0/0/1": "xgei-0/1/0/1",
|
||||
"gei-0/0/0/1.100": "xgei-0/1/0/9.100",
|
||||
}
|
||||
self.assertEqual(resolve_mapped_iface("gei-0/0/0/1.100", pmap), "xgei-0/1/0/9.100")
|
||||
self.assertEqual(resolve_mapped_iface("gei-0/0/0/1.200", pmap), "xgei-0/1/0/1.200")
|
||||
|
||||
def test_parent_inherits_suffix(self) -> None:
|
||||
pmap = {"gei-0/0/0/1": "xgei-0/1/0/1"}
|
||||
self.assertEqual(resolve_mapped_iface("gei-0/0/0/1", pmap), "xgei-0/1/0/1")
|
||||
self.assertEqual(resolve_mapped_iface("gei-0/0/0/1.100", pmap), "xgei-0/1/0/1.100")
|
||||
self.assertEqual(resolve_mapped_iface("gei-0/0/0/2.100", pmap), "gei-0/0/0/2.100")
|
||||
|
||||
def test_apply_port_map_row(self) -> None:
|
||||
row = apply_port_map(
|
||||
{"interface": "gei-0/0/0/1.55", "admin": "up"},
|
||||
iface_fields=["interface"],
|
||||
port_map={"gei-0/0/0/1": "xgei-0/1/0/1"},
|
||||
)
|
||||
self.assertEqual(row["interface"], "xgei-0/1/0/1.55")
|
||||
|
||||
|
||||
class ComparePipelineTests(unittest.TestCase):
|
||||
def test_normalize_then_map_matches(self) -> None:
|
||||
before = [{"interface": "GE-0/0/0/1.100", "admin": "up"}]
|
||||
after = [{"interface": "xgei-0/1/0/1.100", "admin": "up"}]
|
||||
out = compare_rows(
|
||||
before_rows=before,
|
||||
after_rows=after,
|
||||
key_fields=["interface"],
|
||||
iface_fields=["interface"],
|
||||
compare_fields=["admin"],
|
||||
port_map={"gei-0/0/0/1": "xgei-0/1/0/1"},
|
||||
iface_normalize_rules=default_zte_iface_normalize_rules(),
|
||||
)
|
||||
self.assertEqual(out["summary"]["unchanged"], 1)
|
||||
self.assertEqual(out["summary"]["added"], 0)
|
||||
self.assertEqual(out["summary"]["removed"], 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue