package commands import ( "path/filepath" "testing" "git.dws.rip/DWS/onyx/internal/core" "git.dws.rip/DWS/onyx/internal/storage" ) func TestUndoWithEmptyOplog(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() // Initialize the repository repo := &core.OnyxRepository{} err := repo.Init(tempDir) if err != nil { t.Fatalf("Failed to initialize repository: %v", err) } // Open the repository openedRepo, err := core.Open(tempDir) if err != nil { t.Fatalf("Failed to open repository: %v", err) } defer openedRepo.Close() // Try to undo with empty oplog oplogPath := filepath.Join(openedRepo.GetOnyxPath(), "oplog") reader := storage.NewOplogReader(oplogPath) isEmpty, err := reader.IsEmpty() if err != nil { t.Fatalf("Failed to check if oplog is empty: %v", err) } if !isEmpty { t.Errorf("Expected oplog to be empty after init") } _, err = reader.ReadLastEntry() if err == nil { t.Errorf("Expected error when reading from empty oplog, got nil") } } func TestUndoAfterOperation(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() // Initialize the repository repo := &core.OnyxRepository{} err := repo.Init(tempDir) if err != nil { t.Fatalf("Failed to initialize repository: %v", err) } // Open the repository openedRepo, err := core.Open(tempDir) if err != nil { t.Fatalf("Failed to open repository: %v", err) } defer openedRepo.Close() // Perform an operation with transaction txn, err := core.NewTransaction(openedRepo) if err != nil { t.Fatalf("Failed to create transaction: %v", err) } err = txn.ExecuteWithTransaction("test_operation", "Test operation for undo", func() error { // Simulate some operation return nil }) txn.Close() if err != nil { t.Fatalf("Failed to execute transaction: %v", err) } // Verify the oplog has an entry oplogPath := filepath.Join(openedRepo.GetOnyxPath(), "oplog") reader := storage.NewOplogReader(oplogPath) isEmpty, err := reader.IsEmpty() if err != nil { t.Fatalf("Failed to check if oplog is empty: %v", err) } if isEmpty { t.Errorf("Expected oplog to have entries after operation") } // Read the last entry lastEntry, err := reader.ReadLastEntry() if err != nil { t.Fatalf("Failed to read last entry: %v", err) } if lastEntry.Operation != "test_operation" { t.Errorf("Expected operation to be 'test_operation', got %q", lastEntry.Operation) } if lastEntry.Description != "Test operation for undo" { t.Errorf("Expected description to be 'Test operation for undo', got %q", lastEntry.Description) } // Verify state_before and state_after are captured if lastEntry.StateBefore == nil { t.Errorf("Expected state_before to be captured") } if lastEntry.StateAfter == nil { t.Errorf("Expected state_after to be captured") } } func TestSequentialUndos(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() // Initialize the repository repo := &core.OnyxRepository{} err := repo.Init(tempDir) if err != nil { t.Fatalf("Failed to initialize repository: %v", err) } // Open the repository openedRepo, err := core.Open(tempDir) if err != nil { t.Fatalf("Failed to open repository: %v", err) } defer openedRepo.Close() // Perform multiple operations operations := []string{"operation1", "operation2", "operation3"} for _, op := range operations { txn, err := core.NewTransaction(openedRepo) if err != nil { t.Fatalf("Failed to create transaction: %v", err) } err = txn.ExecuteWithTransaction(op, "Test "+op, func() error { return nil }) txn.Close() if err != nil { t.Fatalf("Failed to execute transaction for %s: %v", op, err) } } // Verify we have 3 entries oplogPath := filepath.Join(openedRepo.GetOnyxPath(), "oplog") reader := storage.NewOplogReader(oplogPath) count, err := reader.Count() if err != nil { t.Fatalf("Failed to count oplog entries: %v", err) } if count != 3 { t.Errorf("Expected 3 oplog entries, got %d", count) } // Read all entries to verify order entries, err := reader.ReadAllEntries() if err != nil { t.Fatalf("Failed to read all entries: %v", err) } for i, entry := range entries { expectedOp := operations[i] if entry.Operation != expectedOp { t.Errorf("Entry %d: expected operation %q, got %q", i, expectedOp, entry.Operation) } } } func TestUndoStack(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() // Initialize the repository repo := &core.OnyxRepository{} err := repo.Init(tempDir) if err != nil { t.Fatalf("Failed to initialize repository: %v", err) } // Open the repository openedRepo, err := core.Open(tempDir) if err != nil { t.Fatalf("Failed to open repository: %v", err) } defer openedRepo.Close() // Perform multiple operations operations := []string{"op1", "op2", "op3"} for _, op := range operations { txn, err := core.NewTransaction(openedRepo) if err != nil { t.Fatalf("Failed to create transaction: %v", err) } err = txn.ExecuteWithTransaction(op, "Test "+op, func() error { return nil }) txn.Close() if err != nil { t.Fatalf("Failed to execute transaction for %s: %v", op, err) } } // Get the undo stack oplogPath := filepath.Join(openedRepo.GetOnyxPath(), "oplog") reader := storage.NewOplogReader(oplogPath) undoStack, err := reader.GetUndoStack() if err != nil { t.Fatalf("Failed to get undo stack: %v", err) } if len(undoStack) != 3 { t.Errorf("Expected undo stack size of 3, got %d", len(undoStack)) } // Verify the stack is in reverse order (most recent first) expectedOps := []string{"op3", "op2", "op1"} for i, entry := range undoStack { if entry.Operation != expectedOps[i] { t.Errorf("Undo stack[%d]: expected operation %q, got %q", i, expectedOps[i], entry.Operation) } } } func TestOplogEntryMetadata(t *testing.T) { // Create a temporary directory for testing tempDir := t.TempDir() // Initialize the repository repo := &core.OnyxRepository{} err := repo.Init(tempDir) if err != nil { t.Fatalf("Failed to initialize repository: %v", err) } // Open the repository openedRepo, err := core.Open(tempDir) if err != nil { t.Fatalf("Failed to open repository: %v", err) } defer openedRepo.Close() // Perform an operation with metadata txn, err := core.NewTransaction(openedRepo) if err != nil { t.Fatalf("Failed to create transaction: %v", err) } metadata := map[string]string{ "key1": "value1", "key2": "value2", } err = txn.ExecuteWithTransactionAndMetadata("test_op", "Test with metadata", metadata, func() error { return nil }) txn.Close() if err != nil { t.Fatalf("Failed to execute transaction with metadata: %v", err) } // Read the entry and verify metadata oplogPath := filepath.Join(openedRepo.GetOnyxPath(), "oplog") reader := storage.NewOplogReader(oplogPath) lastEntry, err := reader.ReadLastEntry() if err != nil { t.Fatalf("Failed to read last entry: %v", err) } if lastEntry.Metadata == nil { t.Fatalf("Expected metadata to be present") } if lastEntry.Metadata["key1"] != "value1" { t.Errorf("Expected metadata key1=value1, got %q", lastEntry.Metadata["key1"]) } if lastEntry.Metadata["key2"] != "value2" { t.Errorf("Expected metadata key2=value2, got %q", lastEntry.Metadata["key2"]) } }