package agate import ( "context" "os" "path/filepath" "testing" ) // setupTestAPI creates a temporary directory and initializes an Agate instance func setupTestAPI(t *testing.T) (*Agate, string, func()) { // Create a temporary directory for tests tempDir, err := os.MkdirTemp("", "agate-test-*") if err != nil { t.Fatalf("Failed to create temp directory: %v", err) } // Create a data directory dataDir := filepath.Join(tempDir) if err := os.MkdirAll(dataDir, 0755); err != nil { os.RemoveAll(tempDir) t.Fatalf("Failed to create data directory: %v", err) } // Create test files createAPITestFiles(t, filepath.Join(dataDir, "blobs", "active")) // Create Agate options options := AgateOptions{ WorkDir: dataDir, OpenFunc: func(dir string) error { return nil }, CloseFunc: func() error { return nil }, } // Create Agate instance ag, err := New(options) if err != nil { os.RemoveAll(tempDir) t.Fatalf("Failed to create Agate instance: %v", err) } // Return a cleanup function cleanup := func() { ag.Close() os.RemoveAll(tempDir) } return ag, tempDir, cleanup } // createAPITestFiles creates test files in the specified directory func createAPITestFiles(t *testing.T, dir string) { // Create a subdirectory subDir := filepath.Join(dir, "subdir") if err := os.MkdirAll(subDir, 0755); err != nil { t.Fatalf("Failed to create subdirectory: %v", err) } // Create some test files testFiles := map[string]string{ filepath.Join(dir, "file1.txt"): "This is file 1", filepath.Join(dir, "file2.txt"): "This is file 2", filepath.Join(subDir, "subfile1.txt"): "This is subfile 1", filepath.Join(subDir, "subfile2.txt"): "This is subfile 2", } for path, content := range testFiles { if err := os.WriteFile(path, []byte(content), 0644); err != nil { t.Fatalf("Failed to create test file %s: %v", path, err) } } } func TestNewAgate(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 a data directory dataDir := filepath.Join(tempDir, "data") if err := os.MkdirAll(dataDir, 0755); err != nil { t.Fatalf("Failed to create data directory: %v", err) } // Create Agate options options := AgateOptions{ WorkDir: dataDir, 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() // Check that the Agate instance was created successfully if ag == nil { t.Fatalf("Agate instance is nil") } } func TestSaveAndRestoreSnapshot(t *testing.T) { ag, _, cleanup := setupTestAPI(t) defer cleanup() // Create a snapshot ctx := context.Background() snapshotID, err := ag.SaveSnapshot(ctx, "Test Snapshot", "") if err != nil { t.Fatalf("Failed to create snapshot: %v", err) } // Check that the snapshot was created with the correct name snapshot, err := ag.GetSnapshotDetails(ctx, snapshotID) if err != nil { t.Fatalf("Failed to get snapshot details: %v", err) } if snapshot.Name != "Test Snapshot" { t.Errorf("Snapshot has wrong name: got %s, want %s", snapshot.Name, "Test Snapshot") } // Modify a file dataDir := ag.options.BlobStore.GetActiveDir() if err := os.WriteFile(filepath.Join(dataDir, "file1.txt"), []byte("Modified file 1"), 0644); err != nil { t.Fatalf("Failed to modify test file: %v", err) } // Restore the snapshot err = ag.RestoreSnapshot(ctx, snapshotID) if err != nil { t.Fatalf("Failed to restore snapshot: %v", err) } // Check that the file was restored content, err := os.ReadFile(filepath.Join(dataDir, "file1.txt")) if err != nil { t.Fatalf("Failed to read restored file: %v", err) } if string(content) != "This is file 1" { t.Errorf("File content was not restored: got %s, want %s", string(content), "This is file 1") } } func TestRestoreSnapshotToDir(t *testing.T) { ag, tempDir, cleanup := setupTestAPI(t) defer cleanup() // Create a snapshot ctx := context.Background() snapshotID, err := ag.SaveSnapshot(ctx, "Test Snapshot", "") if err != nil { t.Fatalf("Failed to create snapshot: %v", err) } // Create a target directory targetDir := filepath.Join(tempDir, "target") if err := os.MkdirAll(targetDir, 0755); err != nil { t.Fatalf("Failed to create target directory: %v", err) } // Restore the snapshot to the target directory err = ag.RestoreSnapshotToDir(ctx, snapshotID, targetDir) if err != nil { t.Fatalf("Failed to restore snapshot to directory: %v", err) } // Check that the files were restored testFiles := map[string]string{ filepath.Join(targetDir, "file1.txt"): "This is file 1", filepath.Join(targetDir, "file2.txt"): "This is file 2", filepath.Join(targetDir, "subdir/subfile1.txt"): "This is subfile 1", filepath.Join(targetDir, "subdir/subfile2.txt"): "This is subfile 2", } for path, expectedContent := range testFiles { 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) } } } func TestAPIListSnapshots(t *testing.T) { ag, _, cleanup := setupTestAPI(t) defer cleanup() // Create multiple snapshots ctx := context.Background() snapshotID1, err := ag.SaveSnapshot(ctx, "Snapshot 1", "") if err != nil { t.Fatalf("Failed to create snapshot: %v", err) } // Modify a file dataDir := ag.options.WorkDir if err := os.WriteFile(filepath.Join(dataDir, "file1.txt"), []byte("Modified file 1"), 0644); err != nil { t.Fatalf("Failed to modify test file: %v", err) } snapshotID2, err := ag.SaveSnapshot(ctx, "Snapshot 2", snapshotID1) if err != nil { t.Fatalf("Failed to create snapshot: %v", err) } // List the snapshots snapshots, err := ag.ListSnapshots(ctx) if err != nil { t.Fatalf("Failed to list snapshots: %v", err) } // Check that both snapshots are listed if len(snapshots) != 2 { t.Errorf("Wrong number of snapshots listed: got %d, want %d", len(snapshots), 2) } // Check that the snapshots have the correct information for _, snap := range snapshots { if snap.ID == snapshotID1 { if snap.Name != "Snapshot 1" { t.Errorf("Snapshot 1 has wrong name: got %s, want %s", snap.Name, "Snapshot 1") } if snap.ParentID != "" { t.Errorf("Snapshot 1 has wrong parent ID: got %s, want %s", snap.ParentID, "") } } else if snap.ID == snapshotID2 { if snap.Name != "Snapshot 2" { t.Errorf("Snapshot 2 has wrong name: got %s, want %s", snap.Name, "Snapshot 2") } if snap.ParentID != snapshotID1 { t.Errorf("Snapshot 2 has wrong parent ID: got %s, want %s", snap.ParentID, snapshotID1) } } else { t.Errorf("Unexpected snapshot ID: %s", snap.ID) } } } func TestAPIDeleteSnapshot(t *testing.T) { ag, _, cleanup := setupTestAPI(t) defer cleanup() // Create a snapshot ctx := context.Background() snapshotID, err := ag.SaveSnapshot(ctx, "Test Snapshot", "") if err != nil { t.Fatalf("Failed to create snapshot: %v", err) } // Delete the snapshot err = ag.DeleteSnapshot(ctx, snapshotID) if err != nil { t.Fatalf("Failed to delete snapshot: %v", err) } // Try to get the deleted snapshot _, err = ag.GetSnapshotDetails(ctx, snapshotID) if err == nil { t.Fatalf("Expected error when getting deleted snapshot, got nil") } // List snapshots to confirm it's gone snapshots, err := ag.ListSnapshots(ctx) if err != nil { t.Fatalf("Failed to list snapshots: %v", err) } if len(snapshots) != 0 { t.Errorf("Expected 0 snapshots after deletion, got %d", len(snapshots)) } }