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>
Adds a Qt6::Test-based unit test target (tests/test_profile_repository.cpp,
21 cases), gated behind an ORBITHUB_BUILD_TESTS option that no-ops
gracefully if Qt6::Test isn't available, so it can't break app-only
builds. Covers profile CRUD, validation rules, search/sort, tag
normalization, and folder handling, each against an isolated temporary
SQLite file (new ProfileRepository(databasePathOverride) constructor
overload added for exactly this).
Caught and fixed a real bug along the way: normalizedTags()'s result was
bound directly without the nonNullTrimmed() null-guard every other field
already uses, so creating a profile with no tags at all hit the `tags
NOT NULL` constraint and silently failed -- including via the Import
Profiles feature for any export where a profile has no tags key.
Partial progress on #1 (RdpSessionBackend/SshSessionBackend state-machine
coverage still open -- much larger lift, needs a testability pass on
those backends first).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>