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:
2025-05-10 20:13:29 +03:00
parent 65b1daa52c
commit 047e8d2df0
20 changed files with 3623 additions and 49 deletions

View File

@ -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
}