yao/tai/sandbox/sandbox_test.go
Max 43d4ace13c Add Tai SDK tests and update Makefile for Tai integration
- Introduce new Tai SDK tests in the GitHub workflows, requiring a Tai container with Docker socket mount for execution.
- Update the Makefile to include a dedicated target for running Tai SDK tests, enhancing test coverage for the Tai integration.
- Modify the Go module dependencies to include the pierrec/lz4 package, ensuring compatibility with the new tests.
- Adjust test folder selection logic in the Makefile to exclude additional directories, streamlining the testing process.
2026-03-03 20:49:34 +08:00

344 lines
7.2 KiB
Go

package sandbox
import (
"context"
"os"
"testing"
"time"
)
func taiTestDocker() string {
if addr := os.Getenv("TAI_TEST_DOCKER"); addr != "" {
return addr
}
return "tcp://127.0.0.1:2375"
}
func TestHelpers(t *testing.T) {
t.Run("envSlice", func(t *testing.T) {
if got := envSlice(nil); got != nil {
t.Errorf("envSlice(nil) = %v", got)
}
s := envSlice(map[string]string{"A": "1", "B": "2"})
if len(s) != 2 {
t.Errorf("len = %d, want 2", len(s))
}
})
t.Run("proto", func(t *testing.T) {
if got := proto(""); got != "tcp" {
t.Errorf("proto empty = %q", got)
}
if got := proto("udp"); got != "udp" {
t.Errorf("proto udp = %q", got)
}
})
t.Run("hostIP", func(t *testing.T) {
if got := hostIP(""); got != "127.0.0.1" {
t.Errorf("hostIP empty = %q", got)
}
if got := hostIP("10.0.0.1"); got != "10.0.0.1" {
t.Errorf("hostIP explicit = %q", got)
}
})
}
func TestLocalSandbox(t *testing.T) {
sb, err := NewLocal("")
if err != nil {
t.Skipf("Docker not available: %v", err)
}
defer sb.Close()
ctx := context.Background()
var containerID string
t.Run("Create", func(t *testing.T) {
id, err := sb.Create(ctx, CreateOptions{
Name: "tai-sdk-test",
Image: "alpine:latest",
Cmd: []string{"sleep", "30"},
})
if err != nil {
t.Fatalf("Create: %v", err)
}
if id == "" {
t.Fatal("expected non-empty ID")
}
containerID = id
})
t.Run("Start", func(t *testing.T) {
if containerID == "" {
t.Skip("no container")
}
if err := sb.Start(ctx, containerID); err != nil {
t.Fatalf("Start: %v", err)
}
})
t.Run("Inspect", func(t *testing.T) {
if containerID == "" {
t.Skip("no container")
}
info, err := sb.Inspect(ctx, containerID)
if err != nil {
t.Fatalf("Inspect: %v", err)
}
if info.Status != "running" {
t.Errorf("status = %q, want running", info.Status)
}
if info.Image != "alpine:latest" {
t.Errorf("image = %q", info.Image)
}
})
t.Run("Exec", func(t *testing.T) {
if containerID == "" {
t.Skip("no container")
}
result, err := sb.Exec(ctx, containerID, []string{"echo", "hello"}, ExecOptions{})
if err != nil {
t.Fatalf("Exec: %v", err)
}
if result.ExitCode != 0 {
t.Errorf("exitCode = %d", result.ExitCode)
}
if result.Stdout != "hello\n" {
t.Errorf("stdout = %q, want %q", result.Stdout, "hello\n")
}
})
t.Run("List", func(t *testing.T) {
if containerID == "" {
t.Skip("no container")
}
containers, err := sb.List(ctx, ListOptions{All: true})
if err != nil {
t.Fatalf("List: %v", err)
}
found := false
for _, c := range containers {
if c.ID == containerID {
found = true
break
}
}
if !found {
t.Error("container not found in list")
}
})
t.Run("Stop", func(t *testing.T) {
if containerID == "" {
t.Skip("no container")
}
if err := sb.Stop(ctx, containerID, 5*time.Second); err != nil {
t.Fatalf("Stop: %v", err)
}
})
t.Run("Remove", func(t *testing.T) {
if containerID == "" {
t.Skip("no container")
}
if err := sb.Remove(ctx, containerID, true); err != nil {
t.Fatalf("Remove: %v", err)
}
})
}
func TestLocalCreateWithPorts(t *testing.T) {
sb, err := NewLocal("")
if err != nil {
t.Skipf("Docker not available: %v", err)
}
defer sb.Close()
ctx := context.Background()
id, err := sb.Create(ctx, CreateOptions{
Name: "tai-sdk-port-test",
Image: "alpine:latest",
Cmd: []string{"sleep", "5"},
Memory: 64 * 1024 * 1024,
CPUs: 0.5,
Ports: []PortMapping{
{ContainerPort: 8080, HostPort: 0, Protocol: "tcp"},
},
})
if err != nil {
t.Fatalf("Create: %v", err)
}
defer sb.Remove(ctx, id, true)
if err := sb.Start(ctx, id); err != nil {
t.Fatalf("Start: %v", err)
}
info, err := sb.Inspect(ctx, id)
if err != nil {
t.Fatalf("Inspect: %v", err)
}
found := false
for _, p := range info.Ports {
if p.ContainerPort == 8080 {
found = true
if p.HostPort == 0 {
t.Error("HostPort should be resolved")
}
}
}
if !found {
t.Error("port 8080 not in Ports")
}
}
func TestLocalCreateWithVNC(t *testing.T) {
sb, err := NewLocal("")
if err != nil {
t.Skipf("Docker not available: %v", err)
}
defer sb.Close()
ctx := context.Background()
id, err := sb.Create(ctx, CreateOptions{
Name: "tai-sdk-vnc-test",
Image: "alpine:latest",
Cmd: []string{"sleep", "5"},
Memory: 512 * 1024 * 1024,
VNC: true,
})
if err != nil {
t.Fatalf("Create: %v", err)
}
defer sb.Remove(ctx, id, true)
}
func TestLocalCreateWithEnvAndWorkDir(t *testing.T) {
sb, err := NewLocal("")
if err != nil {
t.Skipf("Docker not available: %v", err)
}
defer sb.Close()
ctx := context.Background()
id, err := sb.Create(ctx, CreateOptions{
Name: "tai-sdk-env-test",
Image: "alpine:latest",
Cmd: []string{"sleep", "5"},
WorkingDir: "/tmp",
Env: map[string]string{"FOO": "bar"},
Binds: []string{},
})
if err != nil {
t.Fatalf("Create: %v", err)
}
defer sb.Remove(ctx, id, true)
if err := sb.Start(ctx, id); err != nil {
t.Fatalf("Start: %v", err)
}
result, err := sb.Exec(ctx, id, []string{"printenv", "FOO"}, ExecOptions{WorkDir: "/tmp"})
if err != nil {
t.Fatalf("Exec: %v", err)
}
if result.Stdout != "bar\n" {
t.Errorf("FOO = %q, want %q", result.Stdout, "bar\n")
}
}
func TestDockerSandboxViaTai(t *testing.T) {
addr := taiTestDocker()
sb, err := NewDocker(addr)
if err != nil {
t.Skipf("Tai Docker proxy not available at %s: %v", addr, err)
}
defer sb.Close()
ctx := context.Background()
id, err := sb.Create(ctx, CreateOptions{
Name: "tai-docker-proxy-test",
Image: "alpine:latest",
Cmd: []string{"sleep", "10"},
})
if err != nil {
t.Fatalf("Create: %v", err)
}
defer sb.Remove(ctx, id, true)
if err := sb.Start(ctx, id); err != nil {
t.Fatalf("Start: %v", err)
}
info, err := sb.Inspect(ctx, id)
if err != nil {
t.Fatalf("Inspect: %v", err)
}
if info.Status != "running" {
t.Errorf("status = %q", info.Status)
}
result, err := sb.Exec(ctx, id, []string{"echo", "via-tai"}, ExecOptions{})
if err != nil {
t.Fatalf("Exec: %v", err)
}
if result.Stdout != "via-tai\n" {
t.Errorf("stdout = %q", result.Stdout)
}
containers, err := sb.List(ctx, ListOptions{All: true})
if err != nil {
t.Fatalf("List: %v", err)
}
found := false
for _, c := range containers {
if c.ID == id {
found = true
}
}
if !found {
t.Error("container not in list")
}
if err := sb.Stop(ctx, id, 5*time.Second); err != nil {
t.Fatalf("Stop: %v", err)
}
}
func TestListWithLabels(t *testing.T) {
sb, err := NewLocal("")
if err != nil {
t.Skipf("Docker not available: %v", err)
}
defer sb.Close()
// List with non-matching labels should return empty
result, err := sb.List(context.Background(), ListOptions{
Labels: map[string]string{"tai-test-nonexist": "true"},
})
if err != nil {
t.Fatalf("List: %v", err)
}
if len(result) != 0 {
t.Errorf("expected 0, got %d", len(result))
}
}
func TestNewLocalInvalidAddr(t *testing.T) {
_, err := NewLocal("tcp://192.168.254.254:1")
if err == nil {
t.Error("expected error for unreachable Docker")
}
}
func TestPortStr(t *testing.T) {
if got := portStr(0); got != "" {
t.Errorf("portStr(0) = %q", got)
}
if got := portStr(8080); got != "8080" {
t.Errorf("portStr(8080) = %q", got)
}
}