Skip to content

Commit 16877fe

Browse files
authored
Working on Test Harness for Remote Connections (#2829)
This pull request introduces a new `test-conn` command-line tool for testing SSH connection flows and user input handling, along with improvements to user input provider extensibility and several configuration and validation enhancements. * New `test-conn` CLI Tool * User Input Provider Abstraction * Refactored user input handling to use a pluggable `UserInputProvider` interface, with a default `FrontendProvider` and the ability to set a custom provider * AI Configuration and Verbosity updates * Enforced that a `SwapToken` must be present in `CommandOptsType` when starting a remote shell with wsh, improving validation and error handling. (`pkg/shellexec/shellexec.go`) * Improved config directory watcher logic to log the config directory path and avoid logging errors for non-existent subdirectories
1 parent f95d781 commit 16877fe

File tree

10 files changed

+538
-6
lines changed

10 files changed

+538
-6
lines changed

cmd/test-conn/cliprovider.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2026, Command Line Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import (
7+
"bufio"
8+
"context"
9+
"fmt"
10+
"os"
11+
"strings"
12+
13+
"github.com/wavetermdev/waveterm/pkg/userinput"
14+
)
15+
16+
type CLIProvider struct {
17+
AutoAccept bool
18+
}
19+
20+
func (p *CLIProvider) GetUserInput(ctx context.Context, request *userinput.UserInputRequest) (*userinput.UserInputResponse, error) {
21+
response := &userinput.UserInputResponse{
22+
Type: request.ResponseType,
23+
RequestId: request.RequestId,
24+
}
25+
26+
if request.Title != "" {
27+
fmt.Printf("\n=== %s ===\n", request.Title)
28+
}
29+
fmt.Printf("%s\n", request.QueryText)
30+
31+
if p.AutoAccept {
32+
fmt.Printf("Auto-accepting (use -i for interactive mode)\n")
33+
response.Confirm = true
34+
response.Text = "yes"
35+
return response, nil
36+
}
37+
38+
reader := bufio.NewReader(os.Stdin)
39+
fmt.Printf("Accept? [y/n]: ")
40+
text, err := reader.ReadString('\n')
41+
if err != nil {
42+
response.ErrorMsg = fmt.Sprintf("error reading input: %v", err)
43+
return response, err
44+
}
45+
46+
text = strings.TrimSpace(strings.ToLower(text))
47+
if text == "y" || text == "yes" {
48+
response.Confirm = true
49+
response.Text = "yes"
50+
} else {
51+
response.Confirm = false
52+
response.Text = "no"
53+
}
54+
55+
return response, nil
56+
}

cmd/test-conn/main-test-conn.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright 2026, Command Line Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import (
7+
"flag"
8+
"fmt"
9+
"log"
10+
"os"
11+
"time"
12+
)
13+
14+
var (
15+
WaveVersion = "0.0.0"
16+
BuildTime = "0"
17+
)
18+
19+
func usage() {
20+
fmt.Fprintf(os.Stderr, `Test Harness for SSH Connection Flows
21+
22+
Usage:
23+
test-conn [flags] <command> <user@host> [args...]
24+
25+
Commands:
26+
connect <user@host> - Test basic SSH connection with wsh
27+
ssh <user@host> - Test basic SSH connection
28+
exec <user@host> <command> - Execute command and show output (no wsh)
29+
wshexec <user@host> <command> - Execute command with wsh enabled
30+
shell <user@host> - Start interactive shell session
31+
32+
Flags:
33+
-t duration Connection timeout (default: 60s)
34+
-i Interactive mode (prompt for user input instead of auto-accept)
35+
-v Show version and exit
36+
37+
Examples:
38+
test-conn ssh user@example.com
39+
test-conn exec user@example.com "ls -la"
40+
test-conn wshexec user@example.com "wsh version"
41+
test-conn -i connect user@example.com
42+
test-conn shell user@example.com
43+
44+
`)
45+
os.Exit(1)
46+
}
47+
48+
func main() {
49+
timeoutFlag := flag.Duration("t", 60*time.Second, "connection timeout")
50+
interactiveFlag := flag.Bool("i", false, "interactive mode (prompt for user input)")
51+
versionFlag := flag.Bool("v", false, "show version")
52+
53+
flag.Usage = usage
54+
flag.Parse()
55+
56+
if *versionFlag {
57+
fmt.Printf("test-conn version %s (built %s)\n", WaveVersion, BuildTime)
58+
os.Exit(0)
59+
}
60+
61+
args := flag.Args()
62+
if len(args) < 2 {
63+
usage()
64+
}
65+
66+
command := args[0]
67+
connName := args[1]
68+
69+
autoAccept := !*interactiveFlag
70+
71+
err := initTestHarness(autoAccept)
72+
if err != nil {
73+
log.Fatalf("Failed to initialize: %v", err)
74+
}
75+
76+
switch command {
77+
case "ssh", "connect":
78+
err = testBasicConnect(connName, *timeoutFlag)
79+
80+
case "exec":
81+
if len(args) < 3 {
82+
log.Fatalf("exec command requires a command argument")
83+
}
84+
cmd := args[2]
85+
err = testShellWithCommand(connName, cmd, *timeoutFlag)
86+
87+
case "wshexec":
88+
if len(args) < 3 {
89+
log.Fatalf("wshexec command requires a command argument")
90+
}
91+
cmd := args[2]
92+
err = testWshExec(connName, cmd, *timeoutFlag)
93+
94+
case "shell":
95+
err = testInteractiveShell(connName, *timeoutFlag)
96+
97+
default:
98+
log.Fatalf("Unknown command: %s", command)
99+
}
100+
101+
if err != nil {
102+
log.Fatalf("Error: %v", err)
103+
}
104+
}

0 commit comments

Comments
 (0)