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.
285 lines
8.2 KiB
Go
285 lines
8.2 KiB
Go
package agate
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestFullWorkflow tests a complete workflow of creating snapshots, modifying files,
|
|
// creating more snapshots, and restoring snapshots.
|
|
func TestFullWorkflow(t *testing.T) {
|
|
// Create a temporary directory for tests
|
|
tempDir, err := os.MkdirTemp("", "agate-test-*")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp directory: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
// Create Agate options
|
|
options := AgateOptions{
|
|
WorkDir: tempDir,
|
|
}
|
|
|
|
// Create Agate instance
|
|
ag, err := New(options)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create Agate instance: %v", err)
|
|
}
|
|
defer ag.Close()
|
|
|
|
// Create a data directory
|
|
dataDir := ag.options.BlobStore.GetActiveDir()
|
|
if err := os.MkdirAll(dataDir, 0755); err != nil {
|
|
t.Fatalf("Failed to create data directory: %v", err)
|
|
}
|
|
|
|
// Create initial test files
|
|
initialFiles := map[string]string{
|
|
filepath.Join(dataDir, "file1.txt"): "Initial content of file 1",
|
|
filepath.Join(dataDir, "file2.txt"): "Initial content of file 2",
|
|
filepath.Join(dataDir, "subdir", "file3.txt"): "Initial content of file 3",
|
|
}
|
|
|
|
// Create subdirectory
|
|
if err := os.MkdirAll(filepath.Join(dataDir, "subdir"), 0755); err != nil {
|
|
t.Fatalf("Failed to create subdirectory: %v", err)
|
|
}
|
|
|
|
// Create the files
|
|
for path, content := range initialFiles {
|
|
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
|
|
t.Fatalf("Failed to create test file %s: %v", path, err)
|
|
}
|
|
}
|
|
|
|
// Step 1: Create the first snapshot
|
|
ctx := context.Background()
|
|
snapshot1ID, err := ag.SaveSnapshot(ctx, "Snapshot 1", "")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create first snapshot: %v", err)
|
|
}
|
|
t.Logf("Created first snapshot with ID: %s", snapshot1ID)
|
|
|
|
// Step 2: Modify some files and add a new file
|
|
modifiedFiles := map[string]string{
|
|
filepath.Join(dataDir, "file1.txt"): "Modified content of file 1",
|
|
filepath.Join(dataDir, "file4.txt"): "Content of new file 4",
|
|
}
|
|
|
|
for path, content := range modifiedFiles {
|
|
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
|
|
t.Fatalf("Failed to modify/create test file %s: %v", path, err)
|
|
}
|
|
}
|
|
|
|
// Step 3: Create the second snapshot
|
|
snapshot2ID, err := ag.SaveSnapshot(ctx, "Snapshot 2", snapshot1ID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create second snapshot: %v", err)
|
|
}
|
|
t.Logf("Created second snapshot with ID: %s", snapshot2ID)
|
|
|
|
// Step 4: Delete a file and modify another
|
|
if err := os.Remove(filepath.Join(dataDir, "file2.txt")); err != nil {
|
|
t.Fatalf("Failed to delete test file: %v", err)
|
|
}
|
|
|
|
if err := os.WriteFile(filepath.Join(dataDir, "subdir/file3.txt"), []byte("Modified content of file 3"), 0644); err != nil {
|
|
t.Fatalf("Failed to modify test file: %v", err)
|
|
}
|
|
|
|
// Step 5: Create the third snapshot
|
|
snapshot3ID, err := ag.SaveSnapshot(ctx, "Snapshot 3", snapshot2ID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create third snapshot: %v", err)
|
|
}
|
|
t.Logf("Created third snapshot with ID: %s", snapshot3ID)
|
|
|
|
// Step 6: List all snapshots
|
|
snapshots, err := ag.ListSnapshots(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Failed to list snapshots: %v", err)
|
|
}
|
|
|
|
if len(snapshots) != 3 {
|
|
t.Errorf("Expected 3 snapshots, got %d", len(snapshots))
|
|
}
|
|
|
|
// Step 7: Restore the first snapshot
|
|
err = ag.RestoreSnapshot(ctx, snapshot1ID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to restore first snapshot: %v", err)
|
|
}
|
|
t.Logf("Restored first snapshot")
|
|
|
|
// Step 8: Verify the restored files match the initial state
|
|
for path, expectedContent := range initialFiles {
|
|
content, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read restored file %s: %v", path, err)
|
|
}
|
|
if string(content) != expectedContent {
|
|
t.Errorf("Restored file %s has wrong content: got %s, want %s", path, string(content), expectedContent)
|
|
}
|
|
}
|
|
|
|
// Check that file4.txt doesn't exist
|
|
if _, err := os.Stat(filepath.Join(dataDir, "file4.txt")); !os.IsNotExist(err) {
|
|
t.Errorf("File4.txt should not exist after restoring first snapshot")
|
|
}
|
|
|
|
// Step 9: Restore the third snapshot
|
|
err = ag.RestoreSnapshot(ctx, snapshot3ID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to restore third snapshot: %v", err)
|
|
}
|
|
t.Logf("Restored third snapshot")
|
|
|
|
// Step 10: Verify the restored files match the final state
|
|
expectedFiles := map[string]string{
|
|
filepath.Join(dataDir, "file1.txt"): "Modified content of file 1",
|
|
filepath.Join(dataDir, "file4.txt"): "Content of new file 4",
|
|
filepath.Join(dataDir, "subdir/file3.txt"): "Modified content of file 3",
|
|
}
|
|
|
|
for path, expectedContent := range expectedFiles {
|
|
content, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read restored file %s: %v", path, err)
|
|
}
|
|
if string(content) != expectedContent {
|
|
t.Errorf("Restored file %s has wrong content: got %s, want %s", path, string(content), expectedContent)
|
|
}
|
|
}
|
|
|
|
// Check that file2.txt doesn't exist
|
|
if _, err := os.Stat(filepath.Join(dataDir, "file2.txt")); !os.IsNotExist(err) {
|
|
t.Errorf("File2.txt should not exist after restoring third snapshot")
|
|
}
|
|
|
|
// Step 11: Delete a snapshot
|
|
err = ag.DeleteSnapshot(ctx, snapshot2ID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to delete snapshot: %v", err)
|
|
}
|
|
t.Logf("Deleted second snapshot")
|
|
|
|
// Step 12: Verify the snapshot was deleted
|
|
snapshots, err = ag.ListSnapshots(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Failed to list snapshots: %v", err)
|
|
}
|
|
|
|
if len(snapshots) != 2 {
|
|
t.Errorf("Expected 2 snapshots after deletion, got %d", len(snapshots))
|
|
}
|
|
|
|
for _, snap := range snapshots {
|
|
if snap.ID == snapshot2ID {
|
|
t.Errorf("Snapshot 2 should have been deleted")
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestLargeFiles tests creating and restoring snapshots with large files
|
|
func TestLargeFiles(t *testing.T) {
|
|
// Skip this test in short mode
|
|
if testing.Short() {
|
|
t.Skip("Skipping large file test in short mode")
|
|
}
|
|
|
|
// Create a temporary directory for tests
|
|
tempDir, err := os.MkdirTemp("", "agate-test-*")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp directory: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
// Create Agate options
|
|
options := AgateOptions{
|
|
WorkDir: tempDir,
|
|
OpenFunc: func(dir string) error {
|
|
return nil
|
|
},
|
|
CloseFunc: func() error {
|
|
return nil
|
|
},
|
|
}
|
|
|
|
// Create Agate instance
|
|
ag, err := New(options)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create Agate instance: %v", err)
|
|
}
|
|
defer ag.Close()
|
|
|
|
// Create a data directory
|
|
dataDir := ag.options.BlobStore.GetActiveDir()
|
|
if err := os.MkdirAll(dataDir, 0755); err != nil {
|
|
t.Fatalf("Failed to create data directory: %v", err)
|
|
}
|
|
|
|
// Create a large file (10 MB)
|
|
largeFilePath := filepath.Join(dataDir, "large_file.bin")
|
|
largeFileSize := 10 * 1024 * 1024 // 10 MB
|
|
largeFile, err := os.Create(largeFilePath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create large test file: %v", err)
|
|
}
|
|
|
|
// Fill the file with a repeating pattern
|
|
pattern := []byte("0123456789ABCDEF")
|
|
buffer := make([]byte, 8192) // 8 KB buffer
|
|
for i := 0; i < len(buffer); i += len(pattern) {
|
|
copy(buffer[i:], pattern)
|
|
}
|
|
|
|
// Write the buffer multiple times to reach the desired size
|
|
bytesWritten := 0
|
|
for bytesWritten < largeFileSize {
|
|
n, err := largeFile.Write(buffer)
|
|
if err != nil {
|
|
largeFile.Close()
|
|
t.Fatalf("Failed to write to large test file: %v", err)
|
|
}
|
|
bytesWritten += n
|
|
}
|
|
largeFile.Close()
|
|
|
|
// Create a snapshot
|
|
ctx := context.Background()
|
|
startTime := time.Now()
|
|
snapshotID, err := ag.SaveSnapshot(ctx, "Large File Snapshot", "")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create snapshot: %v", err)
|
|
}
|
|
duration := time.Since(startTime)
|
|
t.Logf("Created snapshot with large file in %v", duration)
|
|
|
|
// Modify the large file
|
|
if err := os.WriteFile(largeFilePath, []byte("Modified content"), 0644); err != nil {
|
|
t.Fatalf("Failed to modify large file: %v", err)
|
|
}
|
|
|
|
// Restore the snapshot
|
|
startTime = time.Now()
|
|
err = ag.RestoreSnapshot(ctx, snapshotID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to restore snapshot: %v", err)
|
|
}
|
|
duration = time.Since(startTime)
|
|
t.Logf("Restored snapshot with large file in %v", duration)
|
|
|
|
// Verify the file size is correct
|
|
fileInfo, err := os.Stat(largeFilePath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to stat restored large file: %v", err)
|
|
}
|
|
if fileInfo.Size() != int64(largeFileSize) {
|
|
t.Errorf("Restored large file has wrong size: got %d, want %d", fileInfo.Size(), largeFileSize)
|
|
}
|
|
}
|