Skip to content

feat: add configurable mock responses to TCP/SSH/Exec fake clients #48

Description

@rhuss

Problem

The fake client's streaming sub-clients (TCP, SSH, Exec, Files) always return Unimplemented after input validation. This is correct for compile-time interface checks and input validation testing, but it prevents consumers from writing meaningful integration tests that exercise their business logic beyond the "call SDK method" boundary.

For example, the OpenShell Dashboard BFF uses TCP().Forward() and could benefit from testing its connection handling logic without a real gateway.

Current state

CRUD sub-clients (Sandboxes, Providers, Workspaces, Policy) already support pre-seeded data via AddSandbox(), AddProvider(), etc., and their fake implementations handle full Create/Get/List/Delete cycles with in-memory stores.

Streaming sub-clients return Unimplemented unconditionally (after input validation). There is no mechanism to configure alternative responses.

Proposal

Add ClientOption constructors that let tests configure what streaming methods return:

// Example: configure RemoteListen to return nil (success) or a specific error
fake.NewClient(
    fake.WithRemoteListenResult(nil),  // RemoteListen returns success
)

// Example: configure Forward to return a mock ReadWriteCloser
fake.NewClient(
    fake.WithForwardResult(mockRWC, nil),  // Forward returns the mock connection
)

Scope

Methods to consider (all currently return Unimplemented):

Sub-client Method Return type Mock complexity
TCP Forward io.ReadWriteCloser, error Medium (need mock RWC)
TCP Listen net.Listener, error Medium (need mock listener)
TCP RemoteListen error Low (just an error)
SSH Tunnel io.ReadWriteCloser, error Medium (need mock RWC)
SSH CreateSession *SSHSession, error Low (just a struct)
SSH RevokeSession bool, error Low (just bool+error)
Exec Command *ExecResult, error Low (just a struct)
Files Upload/Download various Medium

Design considerations

  • Start with the simplest methods first (RemoteListen, CreateSession, RevokeSession, Command) since they return simple types
  • For methods returning io.ReadWriteCloser or net.Listener, provide helper constructors (e.g., NewMockReadWriteCloser(data []byte)) that return pipe-based implementations
  • Follow the existing WithHealthResult pattern for consistency
  • Input validation should still run before returning the configured mock response (fake-real parity)

Out of scope

  • Simulating full bidirectional streaming behavior (connection lifecycle, concurrent reads/writes)
  • Stateful mock sequences (e.g., "first call returns X, second call returns Y")

These could be added later if concrete use cases emerge.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions