- Updated benchmark and test functions to utilize node configurations instead of pool configurations for improved clarity and consistency. - Refactored related setup functions and test cases to align with the new node-based architecture. - Adjusted error messages and documentation to reflect the transition from pool to node terminology. Made-with: Cursor
247 lines
5.5 KiB
Go
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 testNodes() {
|
|
pc := pc
|
|
t.Run(pc.Name, func(t *testing.T) {
|
|
m := setupManagerForNode(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 testNodes() {
|
|
pc := pc
|
|
t.Run(pc.Name, func(t *testing.T) {
|
|
m := setupManagerForNode(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 testNodes() {
|
|
pc := pc
|
|
t.Run(pc.Name, func(t *testing.T) {
|
|
m := setupManagerForNode(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 testNodes() {
|
|
pc := pc
|
|
t.Run(pc.Name, func(t *testing.T) {
|
|
m := setupManagerForNode(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 testNodes() {
|
|
pc := pc
|
|
t.Run(pc.Name, func(t *testing.T) {
|
|
m := setupManagerForNode(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 testNodes() {
|
|
pc := pc
|
|
t.Run(pc.Name, func(t *testing.T) {
|
|
m := setupManagerForNode(t, &pc)
|
|
ensureTestImage(t, m, pc.TaiID)
|
|
ctx := context.Background()
|
|
box, err := m.Create(ctx, sandbox.CreateOptions{
|
|
Image: testImage(),
|
|
Owner: "test-user",
|
|
NodeID: 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, nodes := setupManager(t, nodeConfig{Name: "local", Addr: testLocalAddr()})
|
|
|
|
_, err := m.Create(context.Background(), sandbox.CreateOptions{
|
|
Owner: "test",
|
|
NodeID: nodes[0].TaiID,
|
|
})
|
|
if err == nil {
|
|
t.Error("expected error for missing image")
|
|
}
|
|
}
|
|
|
|
func TestCreateNoNodeID(t *testing.T) {
|
|
m, _ := setupManager(t, nodeConfig{Name: "local", Addr: testLocalAddr()})
|
|
|
|
_, err := m.Create(context.Background(), sandbox.CreateOptions{
|
|
Image: testImage(),
|
|
})
|
|
if err != sandbox.ErrNodeMissing {
|
|
t.Errorf("err = %v, want ErrNodeMissing", err)
|
|
}
|
|
}
|
|
|
|
func TestMultiNode(t *testing.T) {
|
|
skipIfNoDocker(t)
|
|
skipIfNoTai(t)
|
|
|
|
nodes := testNodes()
|
|
if len(nodes) < 2 {
|
|
t.Skip("need at least 2 nodes (local + remote) for multi-node test")
|
|
}
|
|
|
|
m, registered := setupManager(t, nodes...)
|
|
|
|
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",
|
|
NodeID: 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",
|
|
NodeID: 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")
|
|
}
|
|
}
|