Files
sandbox/envd/internal/services/filesystem/move_test.go
pptx704 a3898d68fb 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
2026-03-09 21:03:19 +06:00

365 lines
11 KiB
Go

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")
})
}