Add comprehensive test coverage for core functionalities
This commit introduces test cases for the API, archive, store, and filesystem functionalities, as well as a functional test for a full workflow. It ensures robust testing for snapshot operations, archiving, and blob management, significantly improving reliability.
This commit is contained in:
114
manager.go
114
manager.go
@ -8,6 +8,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -59,19 +60,11 @@ func (data *SnapshotManagerData) CreateSnapshot(ctx context.Context, sourceDir s
|
||||
// Generate a unique ID for the snapshot
|
||||
snapshotID := uuid.New().String()
|
||||
|
||||
// Clean the active directory to avoid conflicts
|
||||
if err := data.blobStore.CleanActiveDir(ctx); err != nil {
|
||||
return nil, fmt.Errorf("failed to clean active directory: %w", err)
|
||||
}
|
||||
|
||||
// Get the active directory for operations
|
||||
activeDir := data.blobStore.GetActiveDir()
|
||||
|
||||
// Create a temporary file for the archive in the active directory
|
||||
tempFilePath := filepath.Join(activeDir, "temp-"+snapshotID+".zip")
|
||||
// Create a temporary file for the archive in the working directory
|
||||
tempFilePath := filepath.Join(data.blobStore.GetBaseDir(), "temp-"+snapshotID+".zip")
|
||||
tempFile, err := os.Create(tempFilePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create temporary file in active directory: %w", err)
|
||||
return nil, fmt.Errorf("failed to create temporary file in working directory: %w", err)
|
||||
}
|
||||
tempFile.Close() // Close it as CreateArchive will reopen it
|
||||
defer os.Remove(tempFilePath) // Clean up temp file after we're done
|
||||
@ -264,16 +257,11 @@ func (data *SnapshotManagerData) ExtractSnapshot(ctx context.Context, snapshotID
|
||||
|
||||
// If no specific path is provided, use the active directory
|
||||
if path == "" {
|
||||
// Clean the active directory to avoid conflicts
|
||||
if err := data.blobStore.CleanActiveDir(ctx); err != nil {
|
||||
return fmt.Errorf("failed to clean active directory: %w", err)
|
||||
}
|
||||
|
||||
path = filepath.Join(data.blobStore.GetActiveDir(), snapshotID)
|
||||
path = data.blobStore.GetActiveDir()
|
||||
}
|
||||
|
||||
// First check if the snapshot exists
|
||||
_, err := data.metadataStore.GetSnapshotMetadata(ctx, snapshotID)
|
||||
// First check if the snapshot exists and get its metadata
|
||||
snapshot, err := data.metadataStore.GetSnapshotMetadata(ctx, snapshotID)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
return ErrNotFound
|
||||
@ -297,6 +285,94 @@ func (data *SnapshotManagerData) ExtractSnapshot(ctx context.Context, snapshotID
|
||||
return fmt.Errorf("failed to extract snapshot: %w", err)
|
||||
}
|
||||
|
||||
// Create maps for files and directories in the snapshot for quick lookup
|
||||
snapshotFiles := make(map[string]bool)
|
||||
snapshotDirs := make(map[string]bool)
|
||||
for _, file := range snapshot.Files {
|
||||
if file.IsDir {
|
||||
snapshotDirs[file.Path] = true
|
||||
} else {
|
||||
snapshotFiles[file.Path] = true
|
||||
}
|
||||
}
|
||||
|
||||
// First pass: Collect all files and directories in the target
|
||||
var allPaths []string
|
||||
err = filepath.Walk(path, func(filePath string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Skip the root directory itself
|
||||
if filePath == path {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create relative path
|
||||
relPath, err := filepath.Rel(path, filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get relative path: %w", err)
|
||||
}
|
||||
relPath = filepath.ToSlash(relPath)
|
||||
|
||||
allPaths = append(allPaths, filePath)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to scan target directory: %w", err)
|
||||
}
|
||||
|
||||
// Sort paths by length in descending order to process deepest paths first
|
||||
// This ensures we process files before their parent directories
|
||||
sort.Slice(allPaths, func(i, j int) bool {
|
||||
return len(allPaths[i]) > len(allPaths[j])
|
||||
})
|
||||
|
||||
// Second pass: Remove files and directories that aren't in the snapshot
|
||||
for _, filePath := range allPaths {
|
||||
info, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
// Skip if file no longer exists (might have been in a directory we already removed)
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("failed to stat file %s: %w", filePath, err)
|
||||
}
|
||||
|
||||
// Create relative path
|
||||
relPath, err := filepath.Rel(path, filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get relative path: %w", err)
|
||||
}
|
||||
relPath = filepath.ToSlash(relPath)
|
||||
|
||||
if info.IsDir() {
|
||||
// For directories, check if it's in the snapshot or if it's empty
|
||||
if !snapshotDirs[relPath] {
|
||||
// Check if directory is empty
|
||||
entries, err := os.ReadDir(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read directory %s: %w", filePath, err)
|
||||
}
|
||||
|
||||
// If directory is empty, remove it
|
||||
if len(entries) == 0 {
|
||||
if err := os.Remove(filePath); err != nil {
|
||||
return fmt.Errorf("failed to remove directory %s: %w", filePath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For files, remove if not in the snapshot
|
||||
if !snapshotFiles[relPath] {
|
||||
if err := os.Remove(filePath); err != nil {
|
||||
return fmt.Errorf("failed to remove file %s: %w", filePath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user