Skip to content

chore: Improve SSH unit tests#381

Draft
minhtule wants to merge 3 commits into
masterfrom
feat/mt/improve-ssh-unit-test-v2
Draft

chore: Improve SSH unit tests#381
minhtule wants to merge 3 commits into
masterfrom
feat/mt/improve-ssh-unit-test-v2

Conversation

@minhtule

@minhtule minhtule commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Changes

TBD

minhtule added 3 commits July 6, 2026 17:08
Wire concrete types directly instead of the factory/interface seams
(sshConnFactory, netDialerFactory, connPairFactory, ConnPair,
ChannelPairFactory, ChannelPair, Request, RequestHandler). Request
handling uses *ssh.Request directly, dropping the wrapper goroutine
per SSH channel. Delete mocks.go and the mock-based tests; real-SSH
behavior tests replace them in follow-up commits.
Rewrite the channel-layer tests (TestChannelPair_*) against real SSH
connections instead of mocks; conn_pair_test.go carries the shared
transport-base helpers.

Bundle the enabling refactor: channel/connection value types replace the
positional trios across the sshhandler package, and the copier/session
timeouts move from package vars to struct fields with defaults so tests
configure them per instance rather than mutating globals.
@minhtule minhtule marked this pull request as draft July 8, 2026 05:39
@minhtule minhtule requested a review from Copilot July 8, 2026 05:41
@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.00000% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.37%. Comparing base (105f62b) to head (4c9a480).
⚠️ Report is 3 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
internal/sshhandler/proxy.go 53.33% 7 Missing ⚠️
internal/sshhandler/conn_pair.go 78.94% 2 Missing and 2 partials ⚠️
internal/sshhandler/channel_copier.go 50.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #381      +/-   ##
==========================================
- Coverage   86.06%   85.37%   -0.69%     
==========================================
  Files          40       39       -1     
  Lines        2828     2640     -188     
==========================================
- Hits         2434     2254     -180     
+ Misses        269      261       -8     
  Partials      125      125              
Flag Coverage Δ
integration 57.45% <73.33%> (+2.55%) ⬆️
unit 73.25% <56.00%> (-5.28%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
internal/sshhandler/channel_pair.go 98.07% <100.00%> (+6.15%) ⬆️
internal/sshhandler/request_handler.go 89.53% <100.00%> (+8.58%) ⬆️
internal/sshhandler/channel_copier.go 79.48% <50.00%> (-5.13%) ⬇️
internal/sshhandler/conn_pair.go 61.11% <78.94%> (-19.85%) ⬇️
internal/sshhandler/proxy.go 52.42% <53.33%> (-27.40%) ⬇️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the SSH handler internals to use concrete *ssh.Request / connection/channel structs instead of test-focused interfaces, and replaces several mock-heavy unit tests with more realistic SSH integration-style tests that exercise request forwarding, session gating, teardown, and recording behaviors end-to-end.

Changes:

  • Refactors request and channel handling to use concrete *ssh.Request plus new connection/channel structs, simplifying forwarding and plumbing.
  • Makes SSH channel copy/teardown timeouts configurable via per-pair fields (with defaults) rather than package-level vars.
  • Reworks SSH channel-pair tests to use real SSH connections over loopback TCP, and removes older mock-based tests/mocks.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
internal/sshhandler/request_handler.go Removes request abstraction and operates directly on *ssh.Request for parsing/logging/forwarding.
internal/sshhandler/request_handler_test.go Removes prior mock-based request handler unit tests.
internal/sshhandler/proxy.go Simplifies proxy connection setup (direct ssh.NewServerConn/ssh.NewClientConn and net.DialTimeout) and tightens types.
internal/sshhandler/proxy_test.go Removes prior proxy unit tests that relied on injectable factories/mocks.
internal/sshhandler/mocks.go Removes mock implementations previously used by SSH handler tests.
internal/sshhandler/conn_pair.go Introduces connection struct and removes channel-pair factory indirection; uses concrete SSHChannelPair.
internal/sshhandler/conn_pair_test.go Converts to shared SSH test helpers (net+SSH pipe helpers) used by other tests.
internal/sshhandler/channel_pair.go Introduces channel struct, adds configurable timeouts on SSHChannelPair, and wires through copier timeouts.
internal/sshhandler/channel_pair_test.go Adds comprehensive integration-style tests using real SSH channels for forwarding/gating/recording/teardown.
internal/sshhandler/channel_copier.go Replaces global timeout vars with defaults and per-copier timeout fields.
internal/sshhandler/channel_copier_test.go Removes prior mock-based copier unit tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 148 to 185
@@ -214,6 +155,8 @@ func (p *SSHProxy) serveConn(ctx context.Context, conn connect.Conn) error {
return err
}

downstreamConn := connection{conn: downstreamSSHConn, channels: downstreamChannels, requests: downstreamRequests}

sshCtx := &sshContext{
id: hex.EncodeToString(downstreamSSHConn.SessionID()),
username: upstream.username,
@@ -222,40 +165,40 @@ func (p *SSHProxy) serveConn(ctx context.Context, conn connect.Conn) error {

upstreamConfig, err := p.config.GetUpstreamConfig(ctx, upstream)
if err != nil {
closeDownstreamSSH(downstreamSSHConn, downstreamSSHChannelsChan, logger, sshCtx)
closeDownstreamSSH(downstreamConn, logger, sshCtx)

return err
}

// Start connection to upstream SSH server
upstreamConn, err := p.netDialer.DialTimeout("tcp", upstream.address, upstreamConnTimeout)
upstreamNetConn, err := net.DialTimeout("tcp", upstream.address, upstreamConnTimeout)
if err != nil {
logger.Error("Failed to connect to upstream SSH server", zap.Error(err))

closeDownstreamSSH(downstreamSSHConn, downstreamSSHChannelsChan, logger, sshCtx)
closeDownstreamSSH(downstreamConn, logger, sshCtx)

return err
}

// Open the SSH connection to the upstream server
upstreamSSHConn, upstreamSSHChannelsChan, upstreamSSHRequestsChan, err := p.sshConnFactory.NewClientConn(upstreamConn, upstream.address, upstreamConfig)
upstreamSSHConn, upstreamChannels, upstreamRequests, err := ssh.NewClientConn(upstreamNetConn, upstream.address, upstreamConfig)
if err != nil {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants