Port envd from e2b with internalized shared packages and Connect RPC
- Copy envd source from e2b-dev/infra, internalize shared dependencies
into envd/internal/shared/ (keys, filesystem, id, smap, utils)
- Switch from gRPC to Connect RPC for all envd services
- Update module paths to git.omukk.dev/wrenn/{sandbox,sandbox/envd}
- Add proto specs (process, filesystem) with buf-based code generation
- Implement full envd: process exec, filesystem ops, port forwarding,
cgroup management, MMDS integration, and HTTP API
- Update main module dependencies (firecracker SDK, pgx, goose, etc.)
- Remove placeholder .gitkeep files replaced by real implementations
This commit is contained in:
364
envd/internal/services/filesystem/move_test.go
Normal file
364
envd/internal/services/filesystem/move_test.go
Normal file
@ -0,0 +1,364 @@
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"connectrpc.com/authn"
|
||||
"connectrpc.com/connect"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"git.omukk.dev/wrenn/sandbox/envd/internal/services/spec/filesystem"
|
||||
)
|
||||
|
||||
func TestMove(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup temp root and user
|
||||
root := t.TempDir()
|
||||
u, err := user.Current()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Setup source and destination directories
|
||||
sourceDir := filepath.Join(root, "source")
|
||||
destDir := filepath.Join(root, "destination")
|
||||
require.NoError(t, os.MkdirAll(sourceDir, 0o755))
|
||||
require.NoError(t, os.MkdirAll(destDir, 0o755))
|
||||
|
||||
// Create a test file to move
|
||||
sourceFile := filepath.Join(sourceDir, "test-file.txt")
|
||||
testContent := []byte("Hello, World!")
|
||||
require.NoError(t, os.WriteFile(sourceFile, testContent, 0o644))
|
||||
|
||||
// Destination file path
|
||||
destFile := filepath.Join(destDir, "test-file.txt")
|
||||
|
||||
// Service instance
|
||||
svc := mockService()
|
||||
|
||||
// Call the Move function
|
||||
ctx := authn.SetInfo(t.Context(), u)
|
||||
req := connect.NewRequest(&filesystem.MoveRequest{
|
||||
Source: sourceFile,
|
||||
Destination: destFile,
|
||||
})
|
||||
resp, err := svc.Move(ctx, req)
|
||||
|
||||
// Verify the move was successful
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, destFile, resp.Msg.GetEntry().GetPath())
|
||||
|
||||
// Verify the file exists at the destination
|
||||
_, err = os.Stat(destFile)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the file no longer exists at the source
|
||||
_, err = os.Stat(sourceFile)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
|
||||
// Verify the content of the moved file
|
||||
content, err := os.ReadFile(destFile)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testContent, content)
|
||||
}
|
||||
|
||||
func TestMoveDirectory(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup temp root and user
|
||||
root := t.TempDir()
|
||||
u, err := user.Current()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Setup source and destination directories
|
||||
sourceParent := filepath.Join(root, "source-parent")
|
||||
destParent := filepath.Join(root, "dest-parent")
|
||||
require.NoError(t, os.MkdirAll(sourceParent, 0o755))
|
||||
require.NoError(t, os.MkdirAll(destParent, 0o755))
|
||||
|
||||
// Create a test directory with files to move
|
||||
sourceDir := filepath.Join(sourceParent, "test-dir")
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(sourceDir, "subdir"), 0o755))
|
||||
|
||||
// Create some files in the directory
|
||||
file1 := filepath.Join(sourceDir, "file1.txt")
|
||||
file2 := filepath.Join(sourceDir, "subdir", "file2.txt")
|
||||
require.NoError(t, os.WriteFile(file1, []byte("File 1 content"), 0o644))
|
||||
require.NoError(t, os.WriteFile(file2, []byte("File 2 content"), 0o644))
|
||||
|
||||
// Destination directory path
|
||||
destDir := filepath.Join(destParent, "test-dir")
|
||||
|
||||
// Service instance
|
||||
svc := mockService()
|
||||
|
||||
// Call the Move function
|
||||
ctx := authn.SetInfo(t.Context(), u)
|
||||
req := connect.NewRequest(&filesystem.MoveRequest{
|
||||
Source: sourceDir,
|
||||
Destination: destDir,
|
||||
})
|
||||
resp, err := svc.Move(ctx, req)
|
||||
|
||||
// Verify the move was successful
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, destDir, resp.Msg.GetEntry().GetPath())
|
||||
|
||||
// Verify the directory exists at the destination
|
||||
_, err = os.Stat(destDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the files exist at the destination
|
||||
destFile1 := filepath.Join(destDir, "file1.txt")
|
||||
destFile2 := filepath.Join(destDir, "subdir", "file2.txt")
|
||||
_, err = os.Stat(destFile1)
|
||||
require.NoError(t, err)
|
||||
_, err = os.Stat(destFile2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the directory no longer exists at the source
|
||||
_, err = os.Stat(sourceDir)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
|
||||
// Verify the content of the moved files
|
||||
content1, err := os.ReadFile(destFile1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte("File 1 content"), content1)
|
||||
|
||||
content2, err := os.ReadFile(destFile2)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte("File 2 content"), content2)
|
||||
}
|
||||
|
||||
func TestMoveNonExistingFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup temp root and user
|
||||
root := t.TempDir()
|
||||
u, err := user.Current()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Setup destination directory
|
||||
destDir := filepath.Join(root, "destination")
|
||||
require.NoError(t, os.MkdirAll(destDir, 0o755))
|
||||
|
||||
// Non-existing source file
|
||||
sourceFile := filepath.Join(root, "non-existing-file.txt")
|
||||
|
||||
// Destination file path
|
||||
destFile := filepath.Join(destDir, "moved-file.txt")
|
||||
|
||||
// Service instance
|
||||
svc := mockService()
|
||||
|
||||
// Call the Move function
|
||||
ctx := authn.SetInfo(t.Context(), u)
|
||||
req := connect.NewRequest(&filesystem.MoveRequest{
|
||||
Source: sourceFile,
|
||||
Destination: destFile,
|
||||
})
|
||||
_, err = svc.Move(ctx, req)
|
||||
|
||||
// Verify the correct error is returned
|
||||
require.Error(t, err)
|
||||
|
||||
var connectErr *connect.Error
|
||||
ok := errors.As(err, &connectErr)
|
||||
assert.True(t, ok, "expected error to be of type *connect.Error")
|
||||
assert.Equal(t, connect.CodeNotFound, connectErr.Code())
|
||||
assert.Contains(t, connectErr.Message(), "source file not found")
|
||||
}
|
||||
|
||||
func TestMoveRelativePath(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Setup user
|
||||
u, err := user.Current()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Setup directory structure with unique name to avoid conflicts
|
||||
testRelativePath := fmt.Sprintf("test-move-%s", uuid.New())
|
||||
testFolderPath := filepath.Join(u.HomeDir, testRelativePath)
|
||||
require.NoError(t, os.MkdirAll(testFolderPath, 0o755))
|
||||
|
||||
// Create a test file to move
|
||||
sourceFile := filepath.Join(testFolderPath, "source-file.txt")
|
||||
testContent := []byte("Hello from relative path!")
|
||||
require.NoError(t, os.WriteFile(sourceFile, testContent, 0o644))
|
||||
|
||||
// Destination file path (also relative)
|
||||
destRelativePath := fmt.Sprintf("test-move-dest-%s", uuid.New())
|
||||
destFolderPath := filepath.Join(u.HomeDir, destRelativePath)
|
||||
require.NoError(t, os.MkdirAll(destFolderPath, 0o755))
|
||||
destFile := filepath.Join(destFolderPath, "moved-file.txt")
|
||||
|
||||
// Service instance
|
||||
svc := mockService()
|
||||
|
||||
// Call the Move function with relative paths
|
||||
ctx := authn.SetInfo(t.Context(), u)
|
||||
req := connect.NewRequest(&filesystem.MoveRequest{
|
||||
Source: filepath.Join(testRelativePath, "source-file.txt"), // Relative path
|
||||
Destination: filepath.Join(destRelativePath, "moved-file.txt"), // Relative path
|
||||
})
|
||||
resp, err := svc.Move(ctx, req)
|
||||
|
||||
// Verify the move was successful
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, destFile, resp.Msg.GetEntry().GetPath())
|
||||
|
||||
// Verify the file exists at the destination
|
||||
_, err = os.Stat(destFile)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the file no longer exists at the source
|
||||
_, err = os.Stat(sourceFile)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
|
||||
// Verify the content of the moved file
|
||||
content, err := os.ReadFile(destFile)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testContent, content)
|
||||
|
||||
// Clean up
|
||||
os.RemoveAll(testFolderPath)
|
||||
os.RemoveAll(destFolderPath)
|
||||
}
|
||||
|
||||
func TestMove_Symlinks(t *testing.T) { //nolint:tparallel // this test cannot be executed in parallel
|
||||
root := t.TempDir()
|
||||
u, err := user.Current()
|
||||
require.NoError(t, err)
|
||||
ctx := authn.SetInfo(t.Context(), u)
|
||||
|
||||
// Setup source and destination directories
|
||||
sourceRoot := filepath.Join(root, "source")
|
||||
destRoot := filepath.Join(root, "destination")
|
||||
require.NoError(t, os.MkdirAll(sourceRoot, 0o755))
|
||||
require.NoError(t, os.MkdirAll(destRoot, 0o755))
|
||||
|
||||
// 1. Prepare a real directory + file that a symlink will point to
|
||||
realDir := filepath.Join(sourceRoot, "real-dir")
|
||||
require.NoError(t, os.MkdirAll(realDir, 0o755))
|
||||
filePath := filepath.Join(realDir, "file.txt")
|
||||
require.NoError(t, os.WriteFile(filePath, []byte("hello via symlink"), 0o644))
|
||||
|
||||
// 2. Prepare a standalone real file (points-to-file scenario)
|
||||
realFile := filepath.Join(sourceRoot, "real-file.txt")
|
||||
require.NoError(t, os.WriteFile(realFile, []byte("i am a plain file"), 0o644))
|
||||
|
||||
// 3. Create symlinks
|
||||
linkToDir := filepath.Join(sourceRoot, "link-dir") // → directory
|
||||
linkToFile := filepath.Join(sourceRoot, "link-file") // → file
|
||||
require.NoError(t, os.Symlink(realDir, linkToDir))
|
||||
require.NoError(t, os.Symlink(realFile, linkToFile))
|
||||
|
||||
svc := mockService()
|
||||
|
||||
t.Run("move symlink to directory", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
destPath := filepath.Join(destRoot, "moved-link-dir")
|
||||
|
||||
req := connect.NewRequest(&filesystem.MoveRequest{
|
||||
Source: linkToDir,
|
||||
Destination: destPath,
|
||||
})
|
||||
resp, err := svc.Move(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, destPath, resp.Msg.GetEntry().GetPath())
|
||||
|
||||
// Verify the symlink was moved
|
||||
_, err = os.Stat(destPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify it's still a symlink
|
||||
info, err := os.Lstat(destPath)
|
||||
require.NoError(t, err)
|
||||
assert.NotEqual(t, 0, info.Mode()&os.ModeSymlink, "expected a symlink")
|
||||
|
||||
// Verify the symlink target is still correct
|
||||
target, err := os.Readlink(destPath)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, realDir, target)
|
||||
|
||||
// Verify the original symlink is gone
|
||||
_, err = os.Stat(linkToDir)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
|
||||
// Verify the real directory still exists
|
||||
_, err = os.Stat(realDir)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("move symlink to file", func(t *testing.T) { //nolint:paralleltest
|
||||
destPath := filepath.Join(destRoot, "moved-link-file")
|
||||
|
||||
req := connect.NewRequest(&filesystem.MoveRequest{
|
||||
Source: linkToFile,
|
||||
Destination: destPath,
|
||||
})
|
||||
resp, err := svc.Move(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, destPath, resp.Msg.GetEntry().GetPath())
|
||||
|
||||
// Verify the symlink was moved
|
||||
_, err = os.Stat(destPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify it's still a symlink
|
||||
info, err := os.Lstat(destPath)
|
||||
require.NoError(t, err)
|
||||
assert.NotEqual(t, 0, info.Mode()&os.ModeSymlink, "expected a symlink")
|
||||
|
||||
// Verify the symlink target is still correct
|
||||
target, err := os.Readlink(destPath)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, realFile, target)
|
||||
|
||||
// Verify the original symlink is gone
|
||||
_, err = os.Stat(linkToFile)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
|
||||
// Verify the real file still exists
|
||||
_, err = os.Stat(realFile)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("move real file that is target of symlink", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Create a new symlink to the real file
|
||||
newLinkToFile := filepath.Join(sourceRoot, "new-link-file")
|
||||
require.NoError(t, os.Symlink(realFile, newLinkToFile))
|
||||
|
||||
destPath := filepath.Join(destRoot, "moved-real-file.txt")
|
||||
|
||||
req := connect.NewRequest(&filesystem.MoveRequest{
|
||||
Source: realFile,
|
||||
Destination: destPath,
|
||||
})
|
||||
resp, err := svc.Move(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, destPath, resp.Msg.GetEntry().GetPath())
|
||||
|
||||
// Verify the real file was moved
|
||||
_, err = os.Stat(destPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the original file is gone
|
||||
_, err = os.Stat(realFile)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
|
||||
// Verify the symlink still exists but now points to a non-existent file
|
||||
_, err = os.Stat(newLinkToFile)
|
||||
require.Error(t, err, "symlink should point to non-existent file")
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user