forked from kay/RouterOS
M17: "Bekannte Router" im Verbinden-Tab (SavedRouter/SavedRoutersStore), Standort-Freitextfeld, Scroll-Cap ab 4 Einträgen. Bugfix: Umbenennen- TextField steckte in einem sich selbst deaktivierenden Button. M18: Live-Traffic-Punkt an Interfaces (InterfaceTrafficMonitor, eigene SSH-Verbindung, monitor-traffic-Polling). Dabei zwei reale CLI-Parser-Bugs gefunden und gefixt: running/disabled-Flags werden als Buchstaben vor dem ersten Feld codiert, nicht als key=value; monitor-traffic liefert "50.7kbps" statt einer reinen Zahl. M19: Übersicht-Tab — animierte Flussrichtung auf allen Verbindungslinien (TimelineView+dashPhase), frei verschiebbare Knoten mit Live-folgenden Linien, Zurücksetzen-Button. Zusätzlich (noch nicht live getestet, nur Build+Unit-Tests grün): LAN-Port-Konflikt-Prüfung im Einrichten-Assistenten mit doppelter Sicherheitsbestätigung, "Fertig"-Button nach erfolgreichem Anwenden. 82 Tests grün. HANDOFF.md/README.md/Manual.md/CHATLOG.md aktualisiert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDmUd93KxsYGr2kLTotWnG
89 lines
4.9 KiB
Swift
89 lines
4.9 KiB
Swift
import XCTest
|
|
@testable import RouterOSAssistant
|
|
|
|
final class RouterOSCliParserTests: XCTestCase {
|
|
func testParseDeviceInfo() {
|
|
let raw = """
|
|
uptime: 1w2d3h4m5s
|
|
version: 7.24.2 (stable)
|
|
architecture-name: arm64
|
|
board-name: RB750Gr3
|
|
"""
|
|
|
|
let info = RouterOSCliParser.parseDeviceInfo(raw)
|
|
|
|
XCTAssertEqual(info.boardName, "RB750Gr3")
|
|
XCTAssertEqual(info.routerOSVersion, "7.24.2 (stable)")
|
|
XCTAssertEqual(info.architecture, "arm64")
|
|
XCTAssertEqual(info.uptime, "1w2d3h4m5s")
|
|
}
|
|
|
|
/// "running"/"disabled" are never present as `key=value` pairs in real `/interface print
|
|
/// terse` output — confirmed live (2026-09-15, hEX/RouterOS 7.x) against a router with a mix
|
|
/// of running, bridge-slave, and (synthetically added here) disabled ports:
|
|
/// 0 R name=ether1 ... (running)
|
|
/// 2 S name=ether3 ... (not running, bridge slave)
|
|
/// RouterOS encodes them as single-letter flags in a fixed-width column before the first
|
|
/// key=value pair instead ("X" = disabled, "R" = running, "S" = slave). An earlier version of
|
|
/// this parser (and this test) assumed invented `running=`/`disabled=` keys that never
|
|
/// actually appear — every interface's `running` silently read as `false` as a result.
|
|
func testParseInterfaces() {
|
|
let raw = """
|
|
0 R name=ether1 type=ether mac-address=AA:BB:CC:DD:EE:01
|
|
1 S name=ether2 type=ether mac-address=AA:BB:CC:DD:EE:02
|
|
2 X name=ether3 type=ether mac-address=AA:BB:CC:DD:EE:03
|
|
"""
|
|
|
|
let interfaces = RouterOSCliParser.parseInterfaces(raw)
|
|
|
|
XCTAssertEqual(interfaces.count, 3)
|
|
XCTAssertEqual(interfaces[0].name, "ether1")
|
|
XCTAssertTrue(interfaces[0].running)
|
|
XCTAssertFalse(interfaces[0].disabled)
|
|
XCTAssertEqual(interfaces[1].name, "ether2")
|
|
XCTAssertFalse(interfaces[1].running)
|
|
XCTAssertFalse(interfaces[1].disabled)
|
|
XCTAssertEqual(interfaces[2].name, "ether3")
|
|
XCTAssertFalse(interfaces[2].running)
|
|
XCTAssertTrue(interfaces[2].disabled)
|
|
}
|
|
|
|
/// The exact live output pasted by the user (2026-09-15, hEX) that surfaced the flag-column
|
|
/// bug — a mix of running/slave/plain ports plus the router's own bridge and loopback.
|
|
func testParseInterfacesMatchesLiveHexOutput() {
|
|
let raw = """
|
|
0 R name=ether1 default-name=ether1 type=ether mtu=1500 actual-mtu=1500 l2mtu=1596 max-l2mtu=2026 vrf=main mac-address=F4:1E:57:1B:37:D5 last-link-up-time=2026-09-15 20:19:19 link-downs=0
|
|
1 RS name=ether2 default-name=ether2 type=ether mtu=1500 actual-mtu=1500 l2mtu=1596 max-l2mtu=2026 vrf=main mac-address=F4:1E:57:1B:37:D6 last-link-up-time=2026-09-15 20:19:19 link-downs=0
|
|
2 S name=ether3 default-name=ether3 type=ether mtu=1500 actual-mtu=1500 l2mtu=1596 max-l2mtu=2026 vrf=main mac-address=F4:1E:57:1B:37:D7 link-downs=0
|
|
3 R name=ether4 default-name=ether4 type=ether mtu=1500 actual-mtu=1500 l2mtu=1596 max-l2mtu=2026 vrf=main mac-address=F4:1E:57:1B:37:D8 last-link-up-time=2026-09-15 20:19:19 link-downs=0
|
|
4 S name=ether5 default-name=ether5 type=ether mtu=1500 actual-mtu=1500 l2mtu=1596 max-l2mtu=2026 vrf=main mac-address=F4:1E:57:1B:37:D9 link-downs=0
|
|
5 R comment=defconf name=bridge type=bridge mtu=auto actual-mtu=1500 l2mtu=1596 vrf=main mac-address=F4:1E:57:1B:37:D6 last-link-up-time=2026-09-15 20:19:27 link-downs=0
|
|
6 R name=lo type=loopback mtu=65536 actual-mtu=65536 vrf=main mac-address=00:00:00:00:00:00 last-link-up-time=2026-09-15 20:19:15 link-downs=0
|
|
"""
|
|
|
|
let interfaces = RouterOSCliParser.parseInterfaces(raw)
|
|
|
|
XCTAssertEqual(interfaces.map(\.name), ["ether1", "ether2", "ether3", "ether4", "ether5", "bridge", "lo"])
|
|
XCTAssertEqual(interfaces.map(\.running), [true, true, false, true, false, true, true])
|
|
XCTAssertTrue(interfaces.allSatisfy { !$0.disabled })
|
|
}
|
|
|
|
/// Regression test for a real bug found on a physical hEX device: its SSH output didn't
|
|
/// split on bare "\n", collapsing every interface into one "line" whose repeated keys
|
|
/// (name=, type=, ...) overwrote each other, leaving only the last interface ("lo").
|
|
func testParseInterfacesHandlesCarriageReturnLineEndings() {
|
|
let raw = [
|
|
"0 R name=ether1 default-name=ether1 type=ether mtu=1500",
|
|
"1 RS name=ether2 default-name=ether2 type=ether mtu=1500",
|
|
"5 R comment=defconf name=bridge type=bridge mtu=auto",
|
|
"6 R name=lo type=loopback mtu=65536"
|
|
].joined(separator: "\r\n")
|
|
|
|
let interfaces = RouterOSCliParser.parseInterfaces(raw)
|
|
|
|
XCTAssertEqual(interfaces.count, 4)
|
|
XCTAssertEqual(interfaces.map(\.name), ["ether1", "ether2", "bridge", "lo"])
|
|
XCTAssertEqual(interfaces.map(\.type), ["ether", "ether", "bridge", "loopback"])
|
|
}
|
|
}
|