test: add transaction boundary tests for ContentService refactoring

Adds 3 integration tests for transaction boundaries:
- Test 13: Nested operations in uncompleted scope roll back together
- Test 14: Completed scope commits all operations together
- Test 15: MoveToRecycleBin rolls back completely when scope not completed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-20 18:51:34 +00:00
parent 7e989c0f8c
commit 3239a4534e

View File

@@ -547,7 +547,111 @@ internal sealed class ContentServiceRefactoringTests : UmbracoIntegrationTestWit
#region Transaction Boundary Tests
// Tests 13-15 will be added in Task 6
/// <summary>
/// Test 13: Verifies that multiple operations within an uncompleted scope all roll back together.
/// </summary>
[Test]
public void AmbientScope_NestedOperationsShareTransaction()
{
// Arrange
var content1 = ContentBuilder.CreateSimpleContent(ContentType, "RollbackTest1", -1);
var content2 = ContentBuilder.CreateSimpleContent(ContentType, "RollbackTest2", -1);
// Act - Create scope, save content, but don't complete the scope
using (var scope = ScopeProvider.CreateScope())
{
ContentService.Save(content1);
ContentService.Save(content2);
// Verify content has IDs (was saved within transaction)
Assert.That(content1.Id, Is.GreaterThan(0), "Content1 should have an ID");
Assert.That(content2.Id, Is.GreaterThan(0), "Content2 should have an ID");
// v1.2: Note - IDs are captured for debugging but cannot be used after rollback
// since they were assigned within the rolled-back transaction
var id1 = content1.Id;
var id2 = content2.Id;
// DON'T call scope.Complete() - should roll back
}
// Assert - Content should not exist after rollback
// We can't use the IDs because they were assigned in the rolled-back transaction
// Instead, search by name
var foundContent = ContentService.GetRootContent()
.Where(c => c.Name == "RollbackTest1" || c.Name == "RollbackTest2")
.ToList();
Assert.That(foundContent, Is.Empty, "Content should not exist after transaction rollback");
}
/// <summary>
/// Test 14: Verifies that multiple operations within a completed scope all commit together.
/// </summary>
[Test]
public void AmbientScope_CompletedScopeCommitsAllOperations()
{
// Arrange
var content1 = ContentBuilder.CreateSimpleContent(ContentType, "CommitTest1", -1);
var content2 = ContentBuilder.CreateSimpleContent(ContentType, "CommitTest2", -1);
int id1, id2;
// Act - Create scope, save content, and complete the scope
using (var scope = ScopeProvider.CreateScope())
{
ContentService.Save(content1);
ContentService.Save(content2);
id1 = content1.Id;
id2 = content2.Id;
scope.Complete(); // Commit transaction
}
// Assert - Content should exist after commit
var retrieved1 = ContentService.GetById(id1);
var retrieved2 = ContentService.GetById(id2);
Assert.That(retrieved1, Is.Not.Null, "Content1 should exist after commit");
Assert.That(retrieved2, Is.Not.Null, "Content2 should exist after commit");
Assert.That(retrieved1!.Name, Is.EqualTo("CommitTest1"));
Assert.That(retrieved2!.Name, Is.EqualTo("CommitTest2"));
}
/// <summary>
/// Test 15: Verifies MoveToRecycleBin within an uncompleted scope rolls back completely.
/// </summary>
[Test]
public void AmbientScope_MoveToRecycleBinRollsBackCompletely()
{
// Arrange - Create and save content OUTSIDE the test scope so it persists
var content = ContentBuilder.CreateSimpleContent(ContentType, "MoveRollbackTest", -1);
ContentService.Save(content);
var contentId = content.Id;
// Verify content exists and is not trashed
var beforeMove = ContentService.GetById(contentId);
Assert.That(beforeMove, Is.Not.Null, "Content should exist before test");
Assert.That(beforeMove!.Trashed, Is.False, "Content should not be trashed before test");
// Act - Move to recycle bin within an uncompleted scope
using (var scope = ScopeProvider.CreateScope())
{
ContentService.MoveToRecycleBin(content);
// Verify it's trashed within the transaction
var duringMove = ContentService.GetById(contentId);
Assert.That(duringMove!.Trashed, Is.True, "Content should be trashed within transaction");
// DON'T call scope.Complete() - should roll back
}
// Assert - Content should be back to original state after rollback
var afterRollback = ContentService.GetById(contentId);
Assert.That(afterRollback, Is.Not.Null, "Content should still exist after rollback");
Assert.That(afterRollback!.Trashed, Is.False, "Content should not be trashed after rollback");
Assert.That(afterRollback.ParentId, Is.EqualTo(-1), "Content should be at root level after rollback");
}
#endregion