From 5306279d02bc3e4837926e112e97f0888d2961e7 Mon Sep 17 00:00:00 2001 From: Ramon Nogueira Date: Mon, 11 May 2026 10:51:44 -0700 Subject: [PATCH 1/3] Make sandbox detail output consistent --- internal/cmd/sandbox_box.go | 39 ++++++++++++++---------------------- internal/cmd/sandbox_test.go | 37 ++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/internal/cmd/sandbox_box.go b/internal/cmd/sandbox_box.go index d5f8826..157e2f7 100644 --- a/internal/cmd/sandbox_box.go +++ b/internal/cmd/sandbox_box.go @@ -46,6 +46,18 @@ type sandboxCreateInput struct { ProxyConfig string } +var sandboxBoxDetailRender = structured.Template(`Name: {{.Name}} +ID: {{.ID}} +Status: {{.Status}} +Size: {{.SizeClass}} +VCPUs: {{formatCount .Vcpus}} +Memory: {{formatBytesOrDash .MemBytes}} +Rootfs: {{formatBytesOrDash .FsCapacityBytes}} +Snapshot: {{shortID .SnapshotID}} +Idle TTL: {{formatCount .IdleTtlSeconds}}s +Created: {{formatTime .CreatedAt}} +`) + var sandboxCreateCommand = structured.Command[*sandboxCreateInput]{ Use: "create ", Short: "Create a sandbox VM from a snapshot", @@ -137,14 +149,7 @@ Examples: return resp, nil }, - Render: structured.Template(`Name: {{.Name}} -Status: {{.Status}} -VCPUs: {{formatCount .Vcpus}} -Memory: {{formatBytesOrDash .MemBytes}} -Rootfs: {{formatBytesOrDash .FsCapacityBytes}} -Snapshot: {{shortID .SnapshotID}} -Created: {{formatTime .CreatedAt}} -`), + Render: sandboxBoxDetailRender, } var sandboxListCommand = structured.Command[struct{}]{ @@ -194,14 +199,7 @@ var sandboxGetCommand = structured.Command[struct{}]{ return resp, nil }, - Render: structured.Template(`Name: {{.Name}} -Status: {{.Status}} -VCPUs: {{formatCount .Vcpus}} -Memory: {{formatBytesOrDash .MemBytes}} -Rootfs: {{formatBytesOrDash .FsCapacityBytes}} -Snapshot: {{shortID .SnapshotID}} -Created: {{formatTime .CreatedAt}} -`), + Render: sandboxBoxDetailRender, } type sandboxUpdateInput struct { @@ -273,14 +271,7 @@ for the proxy config JSON format.`, return resp, nil }, - Render: structured.Template(`Name: {{.Name}} -Status: {{.Status}} -VCPUs: {{formatCount .Vcpus}} -Memory: {{formatBytesOrDash .MemBytes}} -Rootfs: {{formatBytesOrDash .FsCapacityBytes}} -Snapshot: {{shortID .SnapshotID}} -Created: {{formatTime .CreatedAt}} -`), + Render: sandboxBoxDetailRender, } var sandboxDeleteCommand = structured.Command[struct{}]{ diff --git a/internal/cmd/sandbox_test.go b/internal/cmd/sandbox_test.go index ed19a75..2a37f46 100644 --- a/internal/cmd/sandbox_test.go +++ b/internal/cmd/sandbox_test.go @@ -1,10 +1,13 @@ package cmd import ( + "bytes" "encoding/json" "os" "path/filepath" "testing" + + "github.com/stretchr/testify/require" ) // ==================== parseByteSize ==================== @@ -184,6 +187,40 @@ func TestSandboxCreateCmd_SizeFlags(t *testing.T) { } } +func TestSandboxBoxDetailRenderIncludesIdentityAndTTL(t *testing.T) { + var out bytes.Buffer + model := struct { + ID string + Name string + Status string + SizeClass string + Vcpus int64 + MemBytes int64 + FsCapacityBytes int64 + SnapshotID string + IdleTtlSeconds int64 + CreatedAt string + }{ + ID: "box-123", + Name: "my-vm", + Status: "running", + SizeClass: "small", + Vcpus: 2, + MemBytes: 512 * 1024 * 1024, + FsCapacityBytes: 4 * 1024 * 1024 * 1024, + SnapshotID: "1234567890abcdef", + IdleTtlSeconds: 900, + CreatedAt: "2026-05-11T12:00:00Z", + } + + err := sandboxBoxDetailRender.RenderText(&out, model) + + require.NoError(t, err) + require.Contains(t, out.String(), "ID: box-123") + require.Contains(t, out.String(), "Size: small") + require.Contains(t, out.String(), "Idle TTL: 900s") +} + func TestSandboxUpdateCmd_SizeFlags(t *testing.T) { cmd := sandboxUpdateCommand.Cobra() for _, name := range []string{"memory", "rootfs-capacity"} { From 87f4c946aea291d7e3c4df3a9ea27d07c8391352 Mon Sep 17 00:00:00 2001 From: Ramon Nogueira Date: Mon, 11 May 2026 11:01:24 -0700 Subject: [PATCH 2/3] Use property list for sandbox details --- internal/cmd/sandbox_box.go | 25 ++++++++++++++----------- internal/cmd/sandbox_test.go | 6 +++--- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/internal/cmd/sandbox_box.go b/internal/cmd/sandbox_box.go index 157e2f7..0e64dac 100644 --- a/internal/cmd/sandbox_box.go +++ b/internal/cmd/sandbox_box.go @@ -46,17 +46,20 @@ type sandboxCreateInput struct { ProxyConfig string } -var sandboxBoxDetailRender = structured.Template(`Name: {{.Name}} -ID: {{.ID}} -Status: {{.Status}} -Size: {{.SizeClass}} -VCPUs: {{formatCount .Vcpus}} -Memory: {{formatBytesOrDash .MemBytes}} -Rootfs: {{formatBytesOrDash .FsCapacityBytes}} -Snapshot: {{shortID .SnapshotID}} -Idle TTL: {{formatCount .IdleTtlSeconds}}s -Created: {{formatTime .CreatedAt}} -`) +var sandboxBoxDetailRender = structured.PropertyList{ + Properties: []structured.Property{ + {Label: "Name", Template: "{{.Name}}"}, + {Label: "ID", Template: "{{.ID}}"}, + {Label: "Status", Template: "{{.Status}}"}, + {Label: "Size", Template: "{{.SizeClass}}"}, + {Label: "VCPUs", Template: "{{formatCount .Vcpus}}"}, + {Label: "Memory", Template: "{{formatBytesOrDash .MemBytes}}"}, + {Label: "Rootfs", Template: "{{formatBytesOrDash .FsCapacityBytes}}"}, + {Label: "Snapshot", Template: "{{shortID .SnapshotID}}"}, + {Label: "Idle TTL", Template: "{{formatCount .IdleTtlSeconds}}s"}, + {Label: "Created", Template: "{{formatTime .CreatedAt}}"}, + }, +} var sandboxCreateCommand = structured.Command[*sandboxCreateInput]{ Use: "create ", diff --git a/internal/cmd/sandbox_test.go b/internal/cmd/sandbox_test.go index 2a37f46..b42a4f3 100644 --- a/internal/cmd/sandbox_test.go +++ b/internal/cmd/sandbox_test.go @@ -216,9 +216,9 @@ func TestSandboxBoxDetailRenderIncludesIdentityAndTTL(t *testing.T) { err := sandboxBoxDetailRender.RenderText(&out, model) require.NoError(t, err) - require.Contains(t, out.String(), "ID: box-123") - require.Contains(t, out.String(), "Size: small") - require.Contains(t, out.String(), "Idle TTL: 900s") + require.Contains(t, out.String(), "ID: box-123") + require.Contains(t, out.String(), "Size: small") + require.Contains(t, out.String(), "Idle TTL: 900s") } func TestSandboxUpdateCmd_SizeFlags(t *testing.T) { From 00316468dc184b57583e5e0f24dbc8ddedcbbceb Mon Sep 17 00:00:00 2001 From: Ramon Nogueira Date: Mon, 11 May 2026 11:04:26 -0700 Subject: [PATCH 3/3] Cover sandbox detail SDK response types --- internal/cmd/sandbox_test.go | 103 ++++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 31 deletions(-) diff --git a/internal/cmd/sandbox_test.go b/internal/cmd/sandbox_test.go index b42a4f3..91c2fd0 100644 --- a/internal/cmd/sandbox_test.go +++ b/internal/cmd/sandbox_test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "testing" + langsmith "github.com/langchain-ai/langsmith-go" "github.com/stretchr/testify/require" ) @@ -187,38 +188,78 @@ func TestSandboxCreateCmd_SizeFlags(t *testing.T) { } } -func TestSandboxBoxDetailRenderIncludesIdentityAndTTL(t *testing.T) { - var out bytes.Buffer - model := struct { - ID string - Name string - Status string - SizeClass string - Vcpus int64 - MemBytes int64 - FsCapacityBytes int64 - SnapshotID string - IdleTtlSeconds int64 - CreatedAt string +func TestSandboxBoxDetailRenderSupportsSDKResponseTypes(t *testing.T) { + tests := []struct { + name string + model any + wantID string + wantTTL string }{ - ID: "box-123", - Name: "my-vm", - Status: "running", - SizeClass: "small", - Vcpus: 2, - MemBytes: 512 * 1024 * 1024, - FsCapacityBytes: 4 * 1024 * 1024 * 1024, - SnapshotID: "1234567890abcdef", - IdleTtlSeconds: 900, - CreatedAt: "2026-05-11T12:00:00Z", - } - - err := sandboxBoxDetailRender.RenderText(&out, model) - - require.NoError(t, err) - require.Contains(t, out.String(), "ID: box-123") - require.Contains(t, out.String(), "Size: small") - require.Contains(t, out.String(), "Idle TTL: 900s") + { + name: "new response", + model: langsmith.SandboxBoxNewResponse{ + ID: "box-new", + Name: "new-vm", + Status: "running", + SizeClass: "small", + Vcpus: 2, + MemBytes: 512 * 1024 * 1024, + FsCapacityBytes: 4 * 1024 * 1024 * 1024, + SnapshotID: "1234567890abcdef", + IdleTtlSeconds: 900, + CreatedAt: "2026-05-11T12:00:00Z", + }, + wantID: "ID: box-new", + wantTTL: "Idle TTL: 900s", + }, + { + name: "get response", + model: langsmith.SandboxBoxGetResponse{ + ID: "box-get", + Name: "get-vm", + Status: "running", + SizeClass: "medium", + Vcpus: 4, + MemBytes: 1024 * 1024 * 1024, + FsCapacityBytes: 8 * 1024 * 1024 * 1024, + SnapshotID: "abcdef1234567890", + IdleTtlSeconds: 1800, + CreatedAt: "2026-05-11T12:00:00Z", + }, + wantID: "ID: box-get", + wantTTL: "Idle TTL: 1800s", + }, + { + name: "update response", + model: langsmith.SandboxBoxUpdateResponse{ + ID: "box-update", + Name: "update-vm", + Status: "stopped", + SizeClass: "large", + Vcpus: 8, + MemBytes: 2 * 1024 * 1024 * 1024, + FsCapacityBytes: 16 * 1024 * 1024 * 1024, + SnapshotID: "fedcba0987654321", + IdleTtlSeconds: 3600, + CreatedAt: "2026-05-11T12:00:00Z", + }, + wantID: "ID: box-update", + wantTTL: "Idle TTL: 3600s", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var out bytes.Buffer + + err := sandboxBoxDetailRender.RenderText(&out, tc.model) + + require.NoError(t, err) + require.Contains(t, out.String(), tc.wantID) + require.Contains(t, out.String(), "Size:") + require.Contains(t, out.String(), tc.wantTTL) + }) + } } func TestSandboxUpdateCmd_SizeFlags(t *testing.T) {