- Introduce new unit test targets for Workspace and Sandbox V2 integration in the Makefile, ensuring comprehensive testing of the new features. - Update CI workflows to support MongoDB service for Sandbox V2 tests and improve Docker image handling. - Implement Workspace as a first-class entity, allowing for persistent storage decoupled from container lifecycles. - Enhance the Box struct to manage workspace IDs and update related methods for improved functionality. - Refactor tests to accommodate new Workspace features, ensuring robust testing of the integration. These changes significantly improve the testing framework and functionality of the Sandbox V2, enhancing the overall architecture and user experience.
215 lines
4.7 KiB
Go
215 lines
4.7 KiB
Go
package sandbox_test
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
sandbox "github.com/yaoapp/yao/sandbox/v2"
|
|
)
|
|
|
|
func TestBoxExec(t *testing.T) {
|
|
skipIfNoDocker(t)
|
|
|
|
for _, pc := range testPools() {
|
|
t.Run(pc.Name, func(t *testing.T) {
|
|
m := setupManagerForPool(t, pc)
|
|
box := createTestBox(t, m)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
result, err := box.Exec(ctx, []string{"echo", "box-exec"})
|
|
if err != nil {
|
|
t.Fatalf("Exec: %v", err)
|
|
}
|
|
if result.Stdout != "box-exec\n" {
|
|
t.Errorf("stdout = %q, want %q", result.Stdout, "box-exec\n")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBoxExecWithOptions(t *testing.T) {
|
|
skipIfNoDocker(t)
|
|
|
|
for _, pc := range testPools() {
|
|
t.Run(pc.Name, func(t *testing.T) {
|
|
m := setupManagerForPool(t, pc)
|
|
box := createTestBox(t, m)
|
|
|
|
ctx := context.Background()
|
|
result, err := box.Exec(ctx, []string{"pwd"},
|
|
sandbox.WithWorkDir("/tmp"),
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("Exec: %v", err)
|
|
}
|
|
if result.Stdout != "/tmp\n" {
|
|
t.Errorf("stdout = %q, want %q", result.Stdout, "/tmp\n")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBoxStream(t *testing.T) {
|
|
skipIfNoDocker(t)
|
|
|
|
for _, pc := range testPools() {
|
|
t.Run(pc.Name, func(t *testing.T) {
|
|
m := setupManagerForPool(t, pc)
|
|
box := createTestBox(t, m)
|
|
|
|
ctx := context.Background()
|
|
stream, err := box.Stream(ctx, []string{"sh", "-c", "echo line1; echo line2"})
|
|
if err != nil {
|
|
t.Fatalf("Stream: %v", err)
|
|
}
|
|
|
|
out, err := io.ReadAll(stream.Stdout)
|
|
if err != nil {
|
|
t.Fatalf("ReadAll: %v", err)
|
|
}
|
|
if string(out) != "line1\nline2\n" {
|
|
t.Errorf("stdout = %q, want %q", string(out), "line1\nline2\n")
|
|
}
|
|
|
|
code, err := stream.Wait()
|
|
if err != nil {
|
|
t.Fatalf("Wait: %v", err)
|
|
}
|
|
if code != 0 {
|
|
t.Errorf("exit code = %d, want 0", code)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBoxWorkspace(t *testing.T) {
|
|
skipIfNoDocker(t)
|
|
|
|
for _, pc := range testPools() {
|
|
t.Run(pc.Name, func(t *testing.T) {
|
|
m := setupManagerForPool(t, pc)
|
|
box := createTestBox(t, m)
|
|
|
|
ws := box.Workspace()
|
|
if ws == nil {
|
|
t.Skip("Workspace returned nil (volume not available)")
|
|
}
|
|
|
|
content := []byte("package main\n")
|
|
if err := ws.WriteFile("main.go", content, 0644); err != nil {
|
|
t.Fatalf("WriteFile: %v", err)
|
|
}
|
|
|
|
data, err := ws.ReadFile("main.go")
|
|
if err != nil {
|
|
t.Fatalf("ReadFile: %v", err)
|
|
}
|
|
if string(data) != string(content) {
|
|
t.Errorf("content = %q, want %q", string(data), string(content))
|
|
}
|
|
|
|
if err := ws.MkdirAll("src/pkg", 0755); err != nil {
|
|
t.Fatalf("MkdirAll: %v", err)
|
|
}
|
|
|
|
entries, err := ws.ReadDir("src")
|
|
if err != nil {
|
|
t.Fatalf("ReadDir: %v", err)
|
|
}
|
|
if len(entries) == 0 {
|
|
t.Error("expected non-empty directory listing")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBoxInfo(t *testing.T) {
|
|
skipIfNoDocker(t)
|
|
|
|
for _, pc := range testPools() {
|
|
t.Run(pc.Name, func(t *testing.T) {
|
|
m := setupManagerForPool(t, pc)
|
|
box := createTestBox(t, m)
|
|
|
|
ctx := context.Background()
|
|
info, err := box.Info(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Info: %v", err)
|
|
}
|
|
if info.ID != box.ID() {
|
|
t.Errorf("ID = %q, want %q", info.ID, box.ID())
|
|
}
|
|
if s := strings.ToLower(info.Status); s != "running" {
|
|
t.Errorf("status = %q, want running", info.Status)
|
|
}
|
|
if info.Owner != "test-user" {
|
|
t.Errorf("owner = %q, want test-user", info.Owner)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBoxStopStart(t *testing.T) {
|
|
skipIfNoDocker(t)
|
|
|
|
for _, pc := range testPools() {
|
|
t.Run(pc.Name, func(t *testing.T) {
|
|
m := setupManagerForPool(t, pc)
|
|
box := createTestBox(t, m)
|
|
ctx := context.Background()
|
|
|
|
if err := box.Stop(ctx); err != nil {
|
|
t.Fatalf("Stop: %v", err)
|
|
}
|
|
|
|
if err := box.Start(ctx); err != nil {
|
|
t.Fatalf("Start: %v", err)
|
|
}
|
|
|
|
result, err := box.Exec(ctx, []string{"echo", "after-restart"})
|
|
if err != nil {
|
|
t.Fatalf("Exec after restart: %v", err)
|
|
}
|
|
if result.Stdout != "after-restart\n" {
|
|
t.Errorf("stdout = %q", result.Stdout)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBoxGetOrCreate(t *testing.T) {
|
|
skipIfNoDocker(t)
|
|
|
|
for _, pc := range testPools() {
|
|
t.Run(pc.Name, func(t *testing.T) {
|
|
m := setupManagerForPool(t, pc)
|
|
ctx := context.Background()
|
|
box1, err := m.GetOrCreate(ctx, sandbox.CreateOptions{
|
|
ID: "goc-" + pc.Name,
|
|
Image: testImage(),
|
|
Owner: "test-user",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("GetOrCreate first: %v", err)
|
|
}
|
|
defer m.Remove(ctx, box1.ID())
|
|
|
|
box2, err := m.GetOrCreate(ctx, sandbox.CreateOptions{
|
|
ID: "goc-" + pc.Name,
|
|
Image: testImage(),
|
|
Owner: "test-user",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("GetOrCreate second: %v", err)
|
|
}
|
|
if box2.ContainerID() != box1.ContainerID() {
|
|
t.Error("expected same container for GetOrCreate with same ID")
|
|
}
|
|
})
|
|
}
|
|
}
|