Files
H3ConuMS-v2/backend/tests/test_ssh_parser.py
T
v6ole e5d6d843c3 fix: 修复环路检测失败 + 多项UX增强
修复:
- 环路检测正则 \s+(Onu\S+)\s+ → \s+(Onu\S+) (splitlines移除换行后尾随\s无法匹配)
- 权限中间件 Header(...) → Header(None) 避免缺失Auth头返回422而非401
- 环路检测请求超时30s→120s (SSH连接30+台OLT实测需58秒)

重构 (ssh_service.py):
- 提取 _send_and_wait 为私有方法,消除3处重复内部函数
- 添加 __enter__/__exit__ 上下文管理器支持
- 加固 execute_command prompt检测 (按行匹配<DEVICE_NAME>)
- 移除未使用的settings import
- olt.py/devices.py 调用方改用 with 语法

新功能:
- 侧边栏退出登录上方显示当前用户名和角色
- 版本号从VERSION文件自动读取 (后端/health返回,前端动态显示)
- 基于广西南宁经纬度计算日落时间,自动切换深色/浅色主题
- /api/olt/loopback-detection 响应增加raw字段便于排查

基础设施:
- CLAUDE.md 加入 .gitignore
- 新增 .claude/rules/07-remote-operations.md (远程部署操作)
- 新增 .claude/rules/08-frp-notes.md (frp隧道注意事项)
- 新增 VERSION 文件 (版本号 0.10.0)
- 新增环路检测解析测试用例 (5个)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-12 11:07:29 +08:00

163 lines
5.4 KiB
Python

"""SSH 输出解析测试"""
import re
import pytest
from app.services.ssh_service import SSHService
def _make_svc():
return SSHService("10.0.0.1", "admin", "pass")
class TestParseOnuInfo:
def test_single_online(self):
svc = _make_svc()
output = """
Flags: S-Switched L-Loopback N-Not exist U-Up D-Down
Port MAC Status OAM State LOID Model Distance
0/0/1 1484-778f-aa60 Up OAM_Up test_loid H3C_ET704 1234m
"""
onu_dict, unknown = svc.parse_onu_info(output)
assert len(onu_dict) == 1
assert "1484-778f-aa60" in onu_dict
assert onu_dict["1484-778f-aa60"].status == "online"
def test_mixed_online_offline(self):
svc = _make_svc()
output = """
Flags: S-Switched L-Loopback N-Not exist U-Up D-Down
Port MAC Status OAM State LOID Model Distance
0/0/1 1484-778f-aa60 Up OAM_Up loid_a H3C_ET704 500m
0/0/2 1484-778f-bb70 Down OAM_Down loid_b Unknown <1000m
"""
onu_dict, unknown = svc.parse_onu_info(output)
mac_a = "1484-778f-aa60"
mac_b = "1484-778f-bb70"
assert onu_dict[mac_a].status == "online"
assert onu_dict[mac_b].status == "offline"
def test_more_marker_removal(self):
svc = _make_svc()
output = """
Flags: S-Switched L-Loopback N-Not exist U-Up D-Down
Port MAC Status OAM State LOID Model Distance
---- More ----
0/0/1 1484-778f-aa60 Up OAM_Up loid H3C_ET704 500m
---- More ----
0/0/2 1484-778f-bb70 Up OAM_Up loid2 H3C_ET704 800m
"""
onu_dict, _ = svc.parse_onu_info(output)
assert len(onu_dict) == 2
def test_more_inline_with_device_line(self):
"""More 标记与下一条设备数据同行时,不应丢弃该行"""
svc = _make_svc()
output = """
---- More ---- 1484-778f-aa60 Up OAM_Up loid H3C_ET704 500m
"""
onu_dict, _ = svc.parse_onu_info(output)
mac = "1484-778f-aa60"
assert mac in onu_dict
def test_empty_output(self):
svc = _make_svc()
onu_dict, unknown = svc.parse_onu_info("")
assert len(onu_dict) == 0
def test_header_only(self):
svc = _make_svc()
output = " Flags: S-Switched L-Loopback N-Not exist U-Up D-Down\n Port MAC Status"
onu_dict, _ = svc.parse_onu_info(output)
assert len(onu_dict) == 0
class TestCleanOutput:
def test_strips_ansi_codes(self):
svc = _make_svc()
cleaned = svc._clean_output("\x1b[37D\x1b[K 1484-778f-aa60 Up")
assert "\x1b[37D" not in cleaned
assert "\x1b[K" not in cleaned
assert "1484-778f-aa60" in cleaned
def test_removes_more_marker(self):
svc = _make_svc()
output = "---- More ----\n1484-778f-aa60 Up"
cleaned = svc._clean_output(output)
assert "---- More ----" not in cleaned
assert "1484-778f-aa60" in cleaned
def test_preserves_device_line_after_more(self):
"""More 标记同行后续设备数据不应被删除"""
svc = _make_svc()
output = "---- More ----\r\r 1484-778f-aa60 Up"
cleaned = svc._clean_output(output)
assert "1484-778f-aa60" in cleaned
assert "---- More ----" not in cleaned
class TestDetectLoopback:
"""环路检测输出解析测试"""
def _parse(self, output: str):
"""模拟 detect_loopback 中的解析逻辑"""
has_loop = "Loop is detected on following interfaces" in output
interfaces = []
if has_loop:
for line in output.splitlines():
m = re.match(r'\s+(Onu\S+)', line)
if m:
interfaces.append(m.group(1))
return has_loop, interfaces
def test_no_loop(self):
output = """
Loopback detection is enabled.
Loopback detection interval is 30 second(s).
No loopback is detected.
"""
has_loop, interfaces = self._parse(output)
assert not has_loop
assert interfaces == []
def test_has_loop_single(self):
output = """
Loopback detection is enabled.
Loopback detection interval is 30 second(s).
Loop is detected on following interfaces:
Onu1/0/1:1
"""
has_loop, interfaces = self._parse(output)
assert has_loop
assert interfaces == ["Onu1/0/1:1"]
def test_has_loop_multiple(self):
output = """
Loop is detected on following interfaces:
Onu1/0/1:1
Onu1/0/2:3
Onu2/0/5:10
"""
has_loop, interfaces = self._parse(output)
assert has_loop
assert interfaces == ["Onu1/0/1:1", "Onu1/0/2:3", "Onu2/0/5:10"]
def test_has_loop_with_extra_whitespace(self):
"""接口行有多余空白字符"""
output = """
Loop is detected on following interfaces:
Onu1/0/1:1
"""
has_loop, interfaces = self._parse(output)
assert has_loop
assert interfaces == ["Onu1/0/1:1"]
def test_no_false_positive_on_prompt(self):
"""确保设备提示符不被误识别为接口"""
output = """
Loop is detected on following interfaces:
Onu1/0/1:1
<H3C_Device>
"""
has_loop, interfaces = self._parse(output)
assert has_loop
assert interfaces == ["Onu1/0/1:1"]