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
98 lines
4.3 KiB
Swift
98 lines
4.3 KiB
Swift
import XCTest
|
|
@testable import RouterOSAssistant
|
|
|
|
final class SavedRoutersStoreTests: XCTestCase {
|
|
/// A dedicated suite name per test avoids bleeding state between tests / real app defaults.
|
|
private func makeStore() -> SavedRoutersStore {
|
|
let suiteName = "SavedRoutersStoreTests.\(UUID().uuidString)"
|
|
let defaults = UserDefaults(suiteName: suiteName)!
|
|
return SavedRoutersStore(defaults: defaults)
|
|
}
|
|
|
|
func testFirstSuccessfulConnectionAddsEntryWithDefaultName() {
|
|
let store = makeStore()
|
|
let routers = store.recordSuccessfulConnection(host: "192.168.88.1", username: "admin", defaultName: "hEX")
|
|
|
|
XCTAssertEqual(routers.count, 1)
|
|
XCTAssertEqual(routers[0].host, "192.168.88.1")
|
|
XCTAssertEqual(routers[0].username, "admin")
|
|
XCTAssertEqual(routers[0].name, "hEX")
|
|
XCTAssertEqual(routers[0].location, "")
|
|
}
|
|
|
|
func testUpdateLocationRoundTrip() {
|
|
let store = makeStore()
|
|
store.recordSuccessfulConnection(host: "192.168.88.1", username: "admin", defaultName: "hEX")
|
|
let id = store.load()[0].id
|
|
|
|
let updated = store.updateLocation(id, to: "Keller, Serverschrank")
|
|
XCTAssertEqual(updated.first?.location, "Keller, Serverschrank")
|
|
// Renaming afterwards must not clobber the location, and vice versa.
|
|
let renamed = store.rename(id, to: "Hauptrouter")
|
|
XCTAssertEqual(renamed.first?.name, "Hauptrouter")
|
|
XCTAssertEqual(renamed.first?.location, "Keller, Serverschrank")
|
|
}
|
|
|
|
/// A list saved by an earlier app version (before `location` existed) must keep loading
|
|
/// instead of the whole list silently vanishing — see `SavedRouter.init(from:)`.
|
|
func testDecodingAnEntryWithoutALocationFieldDefaultsToEmpty() {
|
|
let suiteName = "SavedRoutersStoreTests.\(UUID().uuidString)"
|
|
let defaults = UserDefaults(suiteName: suiteName)!
|
|
let legacyJSON = """
|
|
[{"id":"\(UUID().uuidString)","host":"192.168.88.1","username":"admin","name":"hEX","lastConnectedAt":0}]
|
|
"""
|
|
defaults.set(Data(legacyJSON.utf8), forKey: "RouterOSAssistant.SavedRouters")
|
|
|
|
let store = SavedRoutersStore(defaults: defaults)
|
|
let routers = store.load()
|
|
|
|
XCTAssertEqual(routers.count, 1)
|
|
XCTAssertEqual(routers[0].name, "hEX")
|
|
XCTAssertEqual(routers[0].location, "")
|
|
}
|
|
|
|
func testReconnectingToKnownRouterUpdatesRecencyNotName() {
|
|
let store = makeStore()
|
|
store.recordSuccessfulConnection(host: "192.168.88.1", username: "admin", defaultName: "hEX")
|
|
store.rename(store.load()[0].id, to: "Büro-Router")
|
|
|
|
// A later connection reports a possibly-different board name (e.g. after a hardware
|
|
// swap behind the same IP) — the user's own rename must survive regardless.
|
|
let routers = store.recordSuccessfulConnection(host: "192.168.88.1", username: "admin", defaultName: "RB750Gr3")
|
|
|
|
XCTAssertEqual(routers.count, 1)
|
|
XCTAssertEqual(routers[0].name, "Büro-Router")
|
|
}
|
|
|
|
func testDifferentUsernameOnSameHostIsATwoEntries() {
|
|
let store = makeStore()
|
|
store.recordSuccessfulConnection(host: "192.168.88.1", username: "admin", defaultName: "hEX")
|
|
let routers = store.recordSuccessfulConnection(host: "192.168.88.1", username: "gast", defaultName: "hEX")
|
|
|
|
XCTAssertEqual(routers.count, 2)
|
|
}
|
|
|
|
func testRenameAndRemoveRoundTrip() {
|
|
let store = makeStore()
|
|
store.recordSuccessfulConnection(host: "192.168.88.1", username: "admin", defaultName: "hEX")
|
|
let id = store.load()[0].id
|
|
|
|
let renamed = store.rename(id, to: "Wohnzimmer")
|
|
XCTAssertEqual(renamed.first?.name, "Wohnzimmer")
|
|
|
|
let removed = store.remove(id)
|
|
XCTAssertTrue(removed.isEmpty)
|
|
XCTAssertTrue(store.load().isEmpty)
|
|
}
|
|
|
|
func testLoadOrdersMostRecentlyConnectedFirst() {
|
|
let store = makeStore()
|
|
store.recordSuccessfulConnection(host: "10.0.0.1", username: "admin", defaultName: "Router A")
|
|
store.recordSuccessfulConnection(host: "10.0.0.2", username: "admin", defaultName: "Router B")
|
|
// Reconnecting to the first one should move it back to the front.
|
|
let routers = store.recordSuccessfulConnection(host: "10.0.0.1", username: "admin", defaultName: "Router A")
|
|
|
|
XCTAssertEqual(routers.first?.host, "10.0.0.1")
|
|
}
|
|
}
|