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>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user