The username entered at the connect-time prompt (added for issue #21)
was updating SessionTab's own in-memory Profile copy, but
SshSessionBackend/RdpSessionBackend are constructed with -- and only
ever read from -- their own separate Profile copy on a worker thread,
which never saw that edit. Authentication was still built from the
original (blank) username regardless of what was typed into the
prompt.
SessionConnectOptions gains a username field, populated by SessionTab
on every connect attempt and threaded through the same way password
already is; both backends now prefer options.username over
profile().username. Covered by a new SSH regression test using an
exact-match fixture host that only succeeds for a specific
user@host target.
Bump version to v2026.9.16.5.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds two kinds of coverage, continuing #1's remaining scope:
1. Pure-function tests for mapSshError() and escapeForShellSingleQuotes(),
promoted from private members to public statics purely so tests can
call them without spinning up a process. escapeForShellSingleQuotes()
is the actual security boundary for password auth (it's what stops a
password containing a single quote from breaking out of the askpass
script's quoting), so it gets a real adversarial test, not just a
happy-path one.
2. State-machine tests (connect -> Connected, auth failure -> Failed with
the right mapped message, connection refused -> Failed, input
round-tripping, reconnect) driven against tests/fixtures/fake_ssh.sh,
a small controllable stand-in for the real ssh binary, instead of a
real network/SSH server. This needed one small testability seam: a new
constructor overload that overrides the launched program ("ssh" in
production, the fixture script in tests).
POSIX-only for now: the fixture is a shell script, so the state-machine
tests QSKIP on Windows until an equivalent fixture exists there; the
pure-function tests run everywhere.
RdpSessionBackend coverage is still open -- it's a bigger lift again
(FreeRDP's own event loop, not just a QProcess), left for a follow-up.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>