@@ -6,9 +6,11 @@ import (
66 "net/http"
77 "os"
88 "path/filepath"
9+ "runtime"
910 "testing"
1011
1112 langsmith "github.com/langchain-ai/langsmith-go"
13+ "github.com/spf13/cobra"
1214 "github.com/stretchr/testify/assert"
1315 "github.com/stretchr/testify/require"
1416)
@@ -157,6 +159,71 @@ func TestSandboxCreateCmd_AllowsNoArgs(t *testing.T) {
157159 require .NotContains (t , body , "snapshot_id" )
158160}
159161
162+ func TestSandboxExecCmd_PositionalNameAndCommandSeparator (t * testing.T ) {
163+ cmd := newSandboxExecCmd ()
164+ require .NoError (t , cmd .Args (cmd , []string {"my-vm" , "echo" , "hi" }))
165+ require .Error (t , cmd .Args (cmd , []string {}))
166+ }
167+
168+ func TestSandboxExecCmd_WritesCommandOutputDirectly (t * testing.T ) {
169+ var body map [string ]any
170+ ts := newTestServer (t , func (w http.ResponseWriter , r * http.Request ) {
171+ w .Header ().Set ("Content-Type" , "application/json" )
172+ switch {
173+ case r .Method == http .MethodGet && r .URL .Path == "/v2/sandboxes/boxes/my-vm" :
174+ _ , err := w .Write ([]byte (`{"id":"box-id","name":"my-vm","status":"ready","dataplane_url":"` + tsURL (t , r ) + `"}` ))
175+ require .NoError (t , err )
176+ case r .Method == http .MethodPost && r .URL .Path == "/execute" :
177+ require .NoError (t , json .NewDecoder (r .Body ).Decode (& body ))
178+ _ , err := w .Write ([]byte (`{"stdout":"hello\n","stderr":"","exit_code":0}` ))
179+ require .NoError (t , err )
180+ default :
181+ t .Fatalf ("unexpected request %s %s" , r .Method , r .URL .Path )
182+ }
183+ })
184+
185+ var out string
186+ var err error
187+ stdout := captureStdout (t , func () {
188+ out , err = executeCommand (t , "--api-key" , "test-key" , "--api-url" , ts .URL , "sandbox" , "exec" , "my-vm" , "--" , "echo" , "hello" )
189+ })
190+
191+ require .NoError (t , err )
192+ assert .Empty (t , out )
193+ assert .Equal (t , "hello\n " , stdout )
194+ assert .Equal (t , "'echo' 'hello'" , body ["command" ])
195+ }
196+
197+ func tsURL (t * testing.T , r * http.Request ) string {
198+ t .Helper ()
199+ return "http://" + r .Host
200+ }
201+
202+ func TestSandboxCustomOutputCommands_Flags (t * testing.T ) {
203+ tests := []struct {
204+ name string
205+ cmd * cobra.Command
206+ want []string
207+ }{
208+ {"tunnel" , newSandboxTunnelCmd (), []string {"url" , "name" , "remote-port" , "local-port" , "stdio" , "log-level" }},
209+ {"ssh-setup" , newSandboxSSHSetupCmd (), []string {"identity" }},
210+ }
211+ if runtime .GOOS != "windows" {
212+ tests = append (tests , struct {
213+ name string
214+ cmd * cobra.Command
215+ want []string
216+ }{"console" , newSandboxConsoleCmd (), []string {"shell" , "forward-ssh-agent" }})
217+ }
218+ for _ , tc := range tests {
219+ t .Run (tc .name , func (t * testing.T ) {
220+ for _ , name := range tc .want {
221+ require .NotNil (t , tc .cmd .Flags ().Lookup (name ), "flag --%s not found" , name )
222+ }
223+ })
224+ }
225+ }
226+
160227func TestSandboxTunnelCmd_PositionalNameOrURL (t * testing.T ) {
161228 cmd := newSandboxTunnelCmd ()
162229 // Should accept 0 or 1 args
0 commit comments