package execution import ( "context" "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestNewDockerSandbox(t *testing.T) { sandbox := NewDockerSandbox() assert.NotNil(t, sandbox) assert.NotNil(t, sandbox.environment) assert.Empty(t, sandbox.containerID) } func TestDockerSandbox_Initialize(t *testing.T) { if testing.Short() { t.Skip("Skipping Docker integration test in short mode") } sandbox := NewDockerSandbox() ctx := context.Background() // Create a minimal configuration config := &SandboxConfig{ Type: "docker", Image: "alpine:latest", Architecture: "amd64", Resources: ResourceLimits{ MemoryLimit: 512 * 1024 * 1024, // 512MB CPULimit: 1.0, ProcessLimit: 50, FileLimit: 1024, }, Security: SecurityPolicy{ ReadOnlyRoot: false, NoNewPrivileges: true, AllowNetworking: false, IsolateNetwork: true, IsolateProcess: true, DropCapabilities: []string{"ALL"}, }, Environment: map[string]string{ "TEST_VAR": "test_value", }, WorkingDir: "/workspace", Timeout: 30 * time.Second, } err := sandbox.Initialize(ctx, config) if err != nil { t.Skipf("Docker not available or image pull failed: %v", err) } defer sandbox.Cleanup() // Verify sandbox is initialized assert.NotEmpty(t, sandbox.containerID) assert.Equal(t, config, sandbox.config) assert.Equal(t, StatusRunning, sandbox.info.Status) assert.Equal(t, "docker", sandbox.info.Type) } func TestDockerSandbox_ExecuteCommand(t *testing.T) { if testing.Short() { t.Skip("Skipping Docker integration test in short mode") } sandbox := setupTestSandbox(t) defer sandbox.Cleanup() ctx := context.Background() tests := []struct { name string cmd *Command expectedExit int expectedOutput string shouldError bool }{ { name: "simple echo command", cmd: &Command{ Executable: "echo", Args: []string{"hello world"}, }, expectedExit: 0, expectedOutput: "hello world\n", }, { name: "command with environment", cmd: &Command{ Executable: "sh", Args: []string{"-c", "echo $TEST_VAR"}, Environment: map[string]string{"TEST_VAR": "custom_value"}, }, expectedExit: 0, expectedOutput: "custom_value\n", }, { name: "failing command", cmd: &Command{ Executable: "sh", Args: []string{"-c", "exit 1"}, }, expectedExit: 1, }, { name: "command with timeout", cmd: &Command{ Executable: "sleep", Args: []string{"2"}, Timeout: 1 * time.Second, }, shouldError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := sandbox.ExecuteCommand(ctx, tt.cmd) if tt.shouldError { assert.Error(t, err) return } require.NoError(t, err) assert.Equal(t, tt.expectedExit, result.ExitCode) assert.Equal(t, tt.expectedExit == 0, result.Success) if tt.expectedOutput != "" { assert.Equal(t, tt.expectedOutput, result.Stdout) } assert.NotZero(t, result.Duration) assert.False(t, result.StartTime.IsZero()) assert.False(t, result.EndTime.IsZero()) }) } } func TestDockerSandbox_FileOperations(t *testing.T) { if testing.Short() { t.Skip("Skipping Docker integration test in short mode") } sandbox := setupTestSandbox(t) defer sandbox.Cleanup() ctx := context.Background() // Test WriteFile testContent := []byte("Hello, Docker sandbox!") testPath := "/tmp/test_file.txt" err := sandbox.WriteFile(ctx, testPath, testContent, 0644) require.NoError(t, err) // Test ReadFile readContent, err := sandbox.ReadFile(ctx, testPath) require.NoError(t, err) assert.Equal(t, testContent, readContent) // Test ListFiles files, err := sandbox.ListFiles(ctx, "/tmp") require.NoError(t, err) assert.NotEmpty(t, files) // Find our test file var testFile *FileInfo for _, file := range files { if file.Name == "test_file.txt" { testFile = &file break } } require.NotNil(t, testFile) assert.Equal(t, "test_file.txt", testFile.Name) assert.Equal(t, int64(len(testContent)), testFile.Size) assert.False(t, testFile.IsDir) } func TestDockerSandbox_CopyFiles(t *testing.T) { if testing.Short() { t.Skip("Skipping Docker integration test in short mode") } sandbox := setupTestSandbox(t) defer sandbox.Cleanup() ctx := context.Background() // Create a temporary file on host tempDir := t.TempDir() hostFile := filepath.Join(tempDir, "host_file.txt") hostContent := []byte("Content from host") err := os.WriteFile(hostFile, hostContent, 0644) require.NoError(t, err) // Copy from host to container containerPath := "container:/tmp/copied_file.txt" err = sandbox.CopyFiles(ctx, hostFile, containerPath) require.NoError(t, err) // Verify file exists in container readContent, err := sandbox.ReadFile(ctx, "/tmp/copied_file.txt") require.NoError(t, err) assert.Equal(t, hostContent, readContent) // Copy from container back to host hostDestFile := filepath.Join(tempDir, "copied_back.txt") err = sandbox.CopyFiles(ctx, "container:/tmp/copied_file.txt", hostDestFile) require.NoError(t, err) // Verify file exists on host backContent, err := os.ReadFile(hostDestFile) require.NoError(t, err) assert.Equal(t, hostContent, backContent) } func TestDockerSandbox_Environment(t *testing.T) { if testing.Short() { t.Skip("Skipping Docker integration test in short mode") } sandbox := setupTestSandbox(t) defer sandbox.Cleanup() // Test getting initial environment env := sandbox.GetEnvironment() assert.Equal(t, "test_value", env["TEST_VAR"]) // Test setting additional environment newEnv := map[string]string{ "NEW_VAR": "new_value", "PATH": "/custom/path", } err := sandbox.SetEnvironment(newEnv) require.NoError(t, err) // Verify environment is updated env = sandbox.GetEnvironment() assert.Equal(t, "new_value", env["NEW_VAR"]) assert.Equal(t, "/custom/path", env["PATH"]) assert.Equal(t, "test_value", env["TEST_VAR"]) // Original should still be there } func TestDockerSandbox_WorkingDirectory(t *testing.T) { if testing.Short() { t.Skip("Skipping Docker integration test in short mode") } sandbox := setupTestSandbox(t) defer sandbox.Cleanup() // Test getting initial working directory workDir := sandbox.GetWorkingDirectory() assert.Equal(t, "/workspace", workDir) // Test setting working directory newWorkDir := "/tmp" err := sandbox.SetWorkingDirectory(newWorkDir) require.NoError(t, err) // Verify working directory is updated workDir = sandbox.GetWorkingDirectory() assert.Equal(t, newWorkDir, workDir) } func TestDockerSandbox_ResourceUsage(t *testing.T) { if testing.Short() { t.Skip("Skipping Docker integration test in short mode") } sandbox := setupTestSandbox(t) defer sandbox.Cleanup() ctx := context.Background() // Get resource usage usage, err := sandbox.GetResourceUsage(ctx) require.NoError(t, err) // Verify usage structure assert.NotNil(t, usage) assert.False(t, usage.Timestamp.IsZero()) assert.GreaterOrEqual(t, usage.CPUUsage, 0.0) assert.GreaterOrEqual(t, usage.MemoryUsage, int64(0)) assert.GreaterOrEqual(t, usage.MemoryPercent, 0.0) } func TestDockerSandbox_GetInfo(t *testing.T) { if testing.Short() { t.Skip("Skipping Docker integration test in short mode") } sandbox := setupTestSandbox(t) defer sandbox.Cleanup() info := sandbox.GetInfo() assert.NotEmpty(t, info.ID) assert.Contains(t, info.Name, "chorus-sandbox") assert.Equal(t, "docker", info.Type) assert.Equal(t, StatusRunning, info.Status) assert.Equal(t, "docker", info.Runtime) assert.Equal(t, "alpine:latest", info.Image) assert.False(t, info.CreatedAt.IsZero()) assert.False(t, info.StartedAt.IsZero()) } func TestDockerSandbox_Cleanup(t *testing.T) { if testing.Short() { t.Skip("Skipping Docker integration test in short mode") } sandbox := setupTestSandbox(t) // Verify sandbox is running assert.Equal(t, StatusRunning, sandbox.info.Status) assert.NotEmpty(t, sandbox.containerID) // Cleanup err := sandbox.Cleanup() require.NoError(t, err) // Verify sandbox is destroyed assert.Equal(t, StatusDestroyed, sandbox.info.Status) } func TestDockerSandbox_SecurityPolicies(t *testing.T) { if testing.Short() { t.Skip("Skipping Docker integration test in short mode") } sandbox := NewDockerSandbox() ctx := context.Background() // Create configuration with strict security policies config := &SandboxConfig{ Type: "docker", Image: "alpine:latest", Architecture: "amd64", Resources: ResourceLimits{ MemoryLimit: 256 * 1024 * 1024, // 256MB CPULimit: 0.5, ProcessLimit: 10, FileLimit: 256, }, Security: SecurityPolicy{ ReadOnlyRoot: true, NoNewPrivileges: true, AllowNetworking: false, IsolateNetwork: true, IsolateProcess: true, DropCapabilities: []string{"ALL"}, RunAsUser: "1000", RunAsGroup: "1000", TmpfsPaths: []string{"/tmp", "/var/tmp"}, MaskedPaths: []string{"/proc/kcore", "/proc/keys"}, ReadOnlyPaths: []string{"/etc"}, }, WorkingDir: "/workspace", Timeout: 30 * time.Second, } err := sandbox.Initialize(ctx, config) if err != nil { t.Skipf("Docker not available or security policies not supported: %v", err) } defer sandbox.Cleanup() // Test that we can't write to read-only filesystem result, err := sandbox.ExecuteCommand(ctx, &Command{ Executable: "touch", Args: []string{"/test_readonly"}, }) require.NoError(t, err) assert.NotEqual(t, 0, result.ExitCode) // Should fail due to read-only root // Test that tmpfs is writable result, err = sandbox.ExecuteCommand(ctx, &Command{ Executable: "touch", Args: []string{"/tmp/test_tmpfs"}, }) require.NoError(t, err) assert.Equal(t, 0, result.ExitCode) // Should succeed on tmpfs } // setupTestSandbox creates a basic Docker sandbox for testing func setupTestSandbox(t *testing.T) *DockerSandbox { sandbox := NewDockerSandbox() ctx := context.Background() config := &SandboxConfig{ Type: "docker", Image: "alpine:latest", Architecture: "amd64", Resources: ResourceLimits{ MemoryLimit: 512 * 1024 * 1024, // 512MB CPULimit: 1.0, ProcessLimit: 50, FileLimit: 1024, }, Security: SecurityPolicy{ ReadOnlyRoot: false, NoNewPrivileges: true, AllowNetworking: true, // Allow networking for easier testing IsolateNetwork: false, IsolateProcess: true, DropCapabilities: []string{"NET_ADMIN", "SYS_ADMIN"}, }, Environment: map[string]string{ "TEST_VAR": "test_value", }, WorkingDir: "/workspace", Timeout: 30 * time.Second, } err := sandbox.Initialize(ctx, config) if err != nil { t.Skipf("Docker not available: %v", err) } return sandbox } // Benchmark tests func BenchmarkDockerSandbox_ExecuteCommand(b *testing.B) { if testing.Short() { b.Skip("Skipping Docker benchmark in short mode") } sandbox := &DockerSandbox{} ctx := context.Background() // Setup minimal config for benchmarking config := &SandboxConfig{ Type: "docker", Image: "alpine:latest", Architecture: "amd64", Resources: ResourceLimits{ MemoryLimit: 256 * 1024 * 1024, CPULimit: 1.0, ProcessLimit: 50, }, Security: SecurityPolicy{ NoNewPrivileges: true, AllowNetworking: true, }, WorkingDir: "/workspace", Timeout: 10 * time.Second, } err := sandbox.Initialize(ctx, config) if err != nil { b.Skipf("Docker not available: %v", err) } defer sandbox.Cleanup() cmd := &Command{ Executable: "echo", Args: []string{"benchmark test"}, } b.ResetTimer() for i := 0; i < b.N; i++ { _, err := sandbox.ExecuteCommand(ctx, cmd) if err != nil { b.Fatalf("Command execution failed: %v", err) } } }