diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceRefactoringTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceRefactoringTests.cs
index 874d4b909c..b5df7e6a90 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceRefactoringTests.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceRefactoringTests.cs
@@ -8,6 +8,7 @@ using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Notifications;
+using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Tests.Common.Builders;
using Umbraco.Cms.Tests.Common.Builders.Extensions;
@@ -1037,6 +1038,193 @@ internal sealed class ContentServiceRefactoringTests : UmbracoIntegrationTestWit
#endregion
+ #region Phase 8 - Exposed Interface Methods Tests
+
+ ///
+ /// Phase 8 Test: Verifies PerformMoveLocked returns non-null collection.
+ ///
+ [Test]
+ public void PerformMoveLocked_ReturnsNonNullCollection()
+ {
+ // Arrange
+ var moveService = GetRequiredService();
+ var content = ContentBuilder.CreateSimpleContent(ContentType, "MoveTest", -1);
+ ContentService.Save(content);
+
+ // Create a destination parent
+ var destination = ContentBuilder.CreateSimpleContent(ContentType, "Destination", -1);
+ ContentService.Save(destination);
+
+ // Act
+ IReadOnlyCollection<(IContent Content, string OriginalPath)> result;
+ using (var scope = ScopeProvider.CreateScope())
+ {
+ scope.WriteLock(Constants.Locks.ContentTree);
+ result = moveService.PerformMoveLocked(content, destination.Id, null, Constants.Security.SuperUserId, null);
+ scope.Complete();
+ }
+
+ // Assert
+ Assert.That(result, Is.Not.Null, "PerformMoveLocked should return non-null collection");
+ }
+
+ ///
+ /// Phase 8 Test: Verifies PerformMoveLocked includes moved item in returned collection.
+ ///
+ [Test]
+ public void PerformMoveLocked_IncludesMovedItemInCollection()
+ {
+ // Arrange
+ var moveService = GetRequiredService();
+ var content = ContentBuilder.CreateSimpleContent(ContentType, "MoveItem", -1);
+ ContentService.Save(content);
+ var contentId = content.Id;
+
+ var destination = ContentBuilder.CreateSimpleContent(ContentType, "Destination", -1);
+ ContentService.Save(destination);
+ var destinationId = destination.Id;
+
+ // Act
+ IReadOnlyCollection<(IContent Content, string OriginalPath)> result;
+ using (var scope = ScopeProvider.CreateScope())
+ {
+ scope.WriteLock(Constants.Locks.ContentTree);
+ result = moveService.PerformMoveLocked(content, destinationId, null, Constants.Security.SuperUserId, null);
+ scope.Complete();
+ }
+
+ // Assert
+ Assert.That(result.Count, Is.GreaterThan(0), "Should return at least one move event");
+ Assert.That(result.Any(e => e.Content.Id == contentId), Is.True,
+ "Moved item should be included in returned collection");
+ }
+
+ ///
+ /// Phase 8 Test: Verifies PerformMoveLocked handles nested hierarchy correctly.
+ ///
+ [Test]
+ public void PerformMoveLocked_HandlesNestedHierarchy()
+ {
+ // Arrange
+ var moveService = GetRequiredService();
+
+ // Create parent with child
+ var parent = ContentBuilder.CreateSimpleContent(ContentType, "ParentToMove", -1);
+ ContentService.Save(parent);
+
+ var child = ContentBuilder.CreateSimpleContent(ContentType, "Child", parent.Id);
+ ContentService.Save(child);
+
+ // Create destination
+ var destination = ContentBuilder.CreateSimpleContent(ContentType, "Destination", -1);
+ ContentService.Save(destination);
+
+ // Act
+ IReadOnlyCollection<(IContent Content, string OriginalPath)> result;
+ using (var scope = ScopeProvider.CreateScope())
+ {
+ scope.WriteLock(Constants.Locks.ContentTree);
+ result = moveService.PerformMoveLocked(parent, destination.Id, null, Constants.Security.SuperUserId, null);
+ scope.Complete();
+ }
+
+ // Assert
+ Assert.That(result.Count, Is.GreaterThan(0), "Should return move events for hierarchy");
+
+ // Verify parent was moved
+ var movedParent = ContentService.GetById(parent.Id);
+ Assert.That(movedParent!.ParentId, Is.EqualTo(destination.Id), "Parent should be moved to destination");
+ }
+
+ ///
+ /// Phase 8 Test: Verifies DeleteLocked handles content with no descendants.
+ ///
+ [Test]
+ public void DeleteLocked_HandlesEmptyTree()
+ {
+ // Arrange
+ var crudService = GetRequiredService();
+ var content = ContentBuilder.CreateSimpleContent(ContentType, "DeleteTest", -1);
+ ContentService.Save(content);
+ var contentId = content.Id;
+
+ // Act & Assert - Should not throw
+ using (ICoreScope scope = ScopeProvider.CreateCoreScope())
+ {
+ scope.WriteLock(Constants.Locks.ContentTree);
+ var eventMessages = new EventMessages();
+ Assert.DoesNotThrow(() => crudService.DeleteLocked(scope, content, eventMessages));
+ scope.Complete();
+ }
+
+ // Verify deletion
+ Assert.That(ContentService.GetById(contentId), Is.Null, "Content should be deleted");
+ }
+
+ ///
+ /// Phase 8 Test: Verifies DeleteLocked handles content with descendants.
+ ///
+ [Test]
+ public void DeleteLocked_HandlesLargeTree()
+ {
+ // Arrange
+ var crudService = GetRequiredService();
+
+ // Create parent with multiple children
+ var parent = ContentBuilder.CreateSimpleContent(ContentType, "ParentToDelete", -1);
+ ContentService.Save(parent);
+
+ var child1 = ContentBuilder.CreateSimpleContent(ContentType, "Child1", parent.Id);
+ var child2 = ContentBuilder.CreateSimpleContent(ContentType, "Child2", parent.Id);
+ var child3 = ContentBuilder.CreateSimpleContent(ContentType, "Child3", parent.Id);
+ ContentService.Save(child1);
+ ContentService.Save(child2);
+ ContentService.Save(child3);
+
+ var parentId = parent.Id;
+ var child1Id = child1.Id;
+ var child2Id = child2.Id;
+ var child3Id = child3.Id;
+
+ // Act
+ using (ICoreScope scope = ScopeProvider.CreateCoreScope())
+ {
+ scope.WriteLock(Constants.Locks.ContentTree);
+ var eventMessages = new EventMessages();
+ crudService.DeleteLocked(scope, parent, eventMessages);
+ scope.Complete();
+ }
+
+ // Assert - All should be deleted
+ Assert.That(ContentService.GetById(parentId), Is.Null, "Parent should be deleted");
+ Assert.That(ContentService.GetById(child1Id), Is.Null, "Child1 should be deleted");
+ Assert.That(ContentService.GetById(child2Id), Is.Null, "Child2 should be deleted");
+ Assert.That(ContentService.GetById(child3Id), Is.Null, "Child3 should be deleted");
+ }
+
+ ///
+ /// Phase 8 Test: Verifies DeleteLocked throws exception for null content.
+ /// Note: Current implementation throws NullReferenceException rather than ArgumentNullException.
+ /// This test documents the actual behavior.
+ ///
+ [Test]
+ public void DeleteLocked_ThrowsForNullContent()
+ {
+ // Arrange
+ var crudService = GetRequiredService();
+
+ // Act & Assert
+ using (ICoreScope scope = ScopeProvider.CreateCoreScope())
+ {
+ scope.WriteLock(Constants.Locks.ContentTree);
+ var eventMessages = new EventMessages();
+ Assert.Throws(() =>
+ crudService.DeleteLocked(scope, null!, eventMessages));
+ }
+ }
+
+ #endregion
+
///
/// Notification handler that tracks the order of notifications for test verification.
///