yao/sandbox/v2/manager_test.go
Max ce0a97c0af feat(sandbox/v2): refactor benchmarks and tests to use TaiID
- Updated benchmark functions to utilize TaiID instead of pool names for improved consistency and accuracy in tests.
- Refactored test cases across various files to ensure compatibility with the new TaiID structure.
- Enhanced setup functions to accept pointers to poolConfig for better memory management.
- Removed deprecated config struct and adjusted related documentation to reflect the changes in the sandbox architecture.

Made-with: Cursor
2026-03-09 02:50:28 +08:00

247 lines
5.5 KiB
Go

package sandbox_test
import (
"context"
"testing"
"time"
sandbox "github.com/yaoapp/yao/sandbox/v2"
)
func TestCreateAndExec(t *testing.T) {
skipIfNoDocker(t)
for _, pc := range testPools() {
pc := pc
t.Run(pc.Name, func(t *testing.T) {
m := setupManagerForPool(t, &pc)
box := createTestBox(t, m, pc)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
result, err := box.Exec(ctx, []string{"echo", "hello"})
if err != nil {
t.Fatalf("Exec: %v", err)
}
if result.ExitCode != 0 {
t.Errorf("exit code = %d, want 0", result.ExitCode)
}
if result.Stdout != "hello\n" {
t.Errorf("stdout = %q, want %q", result.Stdout, "hello\n")
}
})
}
}
func TestCreateWithLabels(t *testing.T) {
skipIfNoDocker(t)
for _, pc := range testPools() {
pc := pc
t.Run(pc.Name, func(t *testing.T) {
m := setupManagerForPool(t, &pc)
box := createTestBox(t, m, pc, func(co *sandbox.CreateOptions) {
co.Labels = map[string]string{"app": "test-app"}
})
ctx := context.Background()
info, err := box.Info(ctx)
if err != nil {
t.Fatalf("Info: %v", err)
}
if info.Labels["app"] != "test-app" {
t.Errorf("label app = %q, want %q", info.Labels["app"], "test-app")
}
})
}
}
func TestGet(t *testing.T) {
skipIfNoDocker(t)
for _, pc := range testPools() {
pc := pc
t.Run(pc.Name, func(t *testing.T) {
m := setupManagerForPool(t, &pc)
box := createTestBox(t, m, pc)
got, err := m.Get(context.Background(), box.ID())
if err != nil {
t.Fatalf("Get: %v", err)
}
if got.ID() != box.ID() {
t.Errorf("ID = %q, want %q", got.ID(), box.ID())
}
})
}
}
func TestGetNotFound(t *testing.T) {
for _, pc := range testPools() {
pc := pc
t.Run(pc.Name, func(t *testing.T) {
m := setupManagerForPool(t, &pc)
_, err := m.Get(context.Background(), "nonexistent")
if err != sandbox.ErrNotFound {
t.Errorf("err = %v, want ErrNotFound", err)
}
})
}
}
func TestList(t *testing.T) {
skipIfNoDocker(t)
for _, pc := range testPools() {
pc := pc
t.Run(pc.Name, func(t *testing.T) {
m := setupManagerForPool(t, &pc)
box := createTestBox(t, m, pc, func(co *sandbox.CreateOptions) {
co.Owner = "user-list"
})
boxes, err := m.List(context.Background(), sandbox.ListOptions{Owner: "user-list"})
if err != nil {
t.Fatalf("List: %v", err)
}
found := false
for _, b := range boxes {
if b.ID() == box.ID() {
found = true
}
}
if !found {
t.Error("created box not found in list")
}
empty, err := m.List(context.Background(), sandbox.ListOptions{Owner: "nobody"})
if err != nil {
t.Fatalf("List: %v", err)
}
if len(empty) != 0 {
t.Errorf("expected 0 results for unknown owner, got %d", len(empty))
}
})
}
}
func TestRemove(t *testing.T) {
skipIfNoDocker(t)
for _, pc := range testPools() {
pc := pc
t.Run(pc.Name, func(t *testing.T) {
m := setupManagerForPool(t, &pc)
ensureTestImage(t, m, pc.TaiID)
ctx := context.Background()
box, err := m.Create(ctx, sandbox.CreateOptions{
Image: testImage(),
Owner: "test-user",
Pool: pc.TaiID,
})
if err != nil {
t.Fatalf("Create: %v", err)
}
if err := m.Remove(ctx, box.ID()); err != nil {
t.Fatalf("Remove: %v", err)
}
_, err = m.Get(ctx, box.ID())
if err != sandbox.ErrNotFound {
t.Errorf("after Remove, Get err = %v, want ErrNotFound", err)
}
})
}
}
func TestCreateNoImage(t *testing.T) {
m, pools := setupManager(t, poolConfig{Name: "local", Addr: testLocalAddr()})
_, err := m.Create(context.Background(), sandbox.CreateOptions{
Owner: "test",
Pool: pools[0].TaiID,
})
if err == nil {
t.Error("expected error for missing image")
}
}
func TestCreateNoPool(t *testing.T) {
m, _ := setupManager(t, poolConfig{Name: "local", Addr: testLocalAddr()})
_, err := m.Create(context.Background(), sandbox.CreateOptions{
Image: testImage(),
})
if err != sandbox.ErrPoolMissing {
t.Errorf("err = %v, want ErrPoolMissing", err)
}
}
func TestMultiPool(t *testing.T) {
skipIfNoDocker(t)
skipIfNoTai(t)
pools := testPools()
if len(pools) < 2 {
t.Skip("need at least 2 pools (local + remote) for multi-pool test")
}
m, registered := setupManager(t, pools...)
for _, pc := range registered {
ensureTestImage(t, m, pc.TaiID)
}
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
localBox, err := m.Create(ctx, sandbox.CreateOptions{
Image: testImage(),
Owner: "test-user",
Pool: registered[0].TaiID,
})
if err != nil {
t.Fatalf("Create on local: %v", err)
}
defer m.Remove(ctx, localBox.ID())
remoteBox, err := m.Create(ctx, sandbox.CreateOptions{
Image: testImage(),
Owner: "test-user",
Pool: registered[1].TaiID,
})
if err != nil {
t.Fatalf("Create on remote: %v", err)
}
defer m.Remove(ctx, remoteBox.ID())
r1, err := localBox.Exec(ctx, []string{"echo", "local"})
if err != nil {
t.Fatalf("Exec on local: %v", err)
}
if r1.Stdout != "local\n" {
t.Errorf("local stdout = %q, want %q", r1.Stdout, "local\n")
}
r2, err := remoteBox.Exec(ctx, []string{"echo", "remote"})
if err != nil {
t.Fatalf("Exec on remote: %v", err)
}
if r2.Stdout != "remote\n" {
t.Errorf("remote stdout = %q, want %q", r2.Stdout, "remote\n")
}
localInfo, err := localBox.Info(ctx)
if err != nil {
t.Fatalf("Info local: %v", err)
}
remoteInfo, err := remoteBox.Info(ctx)
if err != nil {
t.Fatalf("Info remote: %v", err)
}
if localInfo.ID == remoteInfo.ID {
t.Error("local and remote boxes should have different IDs")
}
}