Files
RouterOS/RouterOSAssistantTests/SSHTransportTimeoutTests.swift
T
KayandClaude Sonnet 5 cae59db39e Fix Haenger beim Anlegen eines DHCP-Pools (bugs.md #11)
SSHTransport.connect()/run() hatten keinen Timeout - im Gegensatz zu
RestTransport (timeoutInterval=5). Jeder erste Schreibvorgang einer
Session loest ueber ensureSessionBackup() eine dedizierte SSH-
Verbindung aus; haengt die, blieb isApplying unbegrenzt aktiv (kein
Fehler, kein Recovery, nur Force-Quit). Live vom Nutzer bestaetigt
(Experte-Tab, dauerhaft haengend) bevor der Fix geschrieben wurde.

Neuer genererischer SSHTransport.withTimeout(_:operation:) (Task-
Group-Race gegen eine Deadline), angewendet auf connect() (10s) und
run() (30s). 3 neue Regressionstests fuer die Race-Logik isoliert.

Nebenbefund: xcodegen generate ueberschreibt Info.plist komplett aus
project.yml (kein Merge) - ein Regenerieren fuer die neue Testdatei
setzte die Version stillschweigend von 1.1.0 auf 1.0 zurueck. Version
jetzt explizit in project.yml verankert.

Build + alle 106 Unit-Tests gruen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 21:51:11 +02:00

46 lines
1.8 KiB
Swift

import XCTest
@testable import RouterOSAssistant
/// Regression tests for bugs.md #11 (2026-09-17): creating an `/ip pool` entry in the Experte tab
/// hung the app permanently, traced to `SSHTransport.connect()`/`run(_:)` having no timeout at
/// all — a stalled connection attempt or command execution had no way to ever resolve. These
/// tests exercise the generic race mechanism (`SSHTransport.withTimeout`) directly, independent
/// of Citadel/real network I/O, since that's the actual bug: the race logic itself, not anything
/// SSH-specific.
final class SSHTransportTimeoutTests: XCTestCase {
func testFastOperationReturnsItsResultBeforeTheDeadline() async throws {
let result = try await SSHTransport.withTimeout(.seconds(1)) {
"done"
}
XCTAssertEqual(result, "done")
}
func testHangingOperationThrowsAfterTheDeadlineInsteadOfBlockingForever() async {
let start = ContinuousClock.now
do {
_ = try await SSHTransport.withTimeout(.milliseconds(200)) {
try await Task.sleep(for: .seconds(60))
return "never reached"
}
XCTFail("Expected a timeout error")
} catch {
let elapsed = ContinuousClock.now - start
XCTAssertLessThan(elapsed, .seconds(5), "Timeout should fire close to the deadline, not wait for the full 60s operation")
}
}
func testOperationsOwnThrownErrorPropagatesUnchangedWhenItFinishesFirst() async {
struct SampleError: Error, Equatable {}
do {
_ = try await SSHTransport.withTimeout(.seconds(1)) {
throw SampleError()
}
XCTFail("Expected SampleError to propagate")
} catch is SampleError {
// expected
} catch {
XCTFail("Expected SampleError, got \(error)")
}
}
}