diff --git a/src/Umbraco.Core/Factories/NavigationFactory.cs b/src/Umbraco.Core/Factories/NavigationFactory.cs
index 815312e048..a95cbf68a5 100644
--- a/src/Umbraco.Core/Factories/NavigationFactory.cs
+++ b/src/Umbraco.Core/Factories/NavigationFactory.cs
@@ -11,10 +11,10 @@ internal static class NavigationFactory
///
/// A dictionary of objects with key corresponding to their unique Guid.
/// The objects used to build the navigation nodes dictionary.
- public static void BuildNavigationDictionary(ConcurrentDictionary nodesStructure,IEnumerable entities)
+ public static void BuildNavigationDictionary(ConcurrentDictionary nodesStructure, IEnumerable entities)
{
var entityList = entities.ToList();
- var idToKeyMap = entityList.ToDictionary(x => x.Id, x => x.Key);
+ Dictionary idToKeyMap = entityList.ToDictionary(x => x.Id, x => x.Key);
foreach (INavigationModel entity in entityList)
{
diff --git a/src/Umbraco.Core/Services/Navigation/ContentNavigationServiceBase.cs b/src/Umbraco.Core/Services/Navigation/ContentNavigationServiceBase.cs
index 394223c311..4f2d1e1c9b 100644
--- a/src/Umbraco.Core/Services/Navigation/ContentNavigationServiceBase.cs
+++ b/src/Umbraco.Core/Services/Navigation/ContentNavigationServiceBase.cs
@@ -33,12 +33,12 @@ internal abstract class ContentNavigationServiceBase
public bool TryGetParentKey(Guid childKey, out Guid? parentKey)
=> TryGetParentKeyFromStructure(_navigationStructure, childKey, out parentKey);
+ public bool TryGetRootKeys(out IEnumerable rootKeys)
+ => TryGetRootKeysFromStructure(_navigationStructure, out rootKeys);
+
public bool TryGetChildrenKeys(Guid parentKey, out IEnumerable childrenKeys)
=> TryGetChildrenKeysFromStructure(_navigationStructure, parentKey, out childrenKeys);
- public bool TryGetRootKeys(out IEnumerable childrenKeys)
- => TryGetRootKeysFromStructure(_navigationStructure, out childrenKeys);
-
public bool TryGetDescendantsKeys(Guid parentKey, out IEnumerable descendantsKeys)
=> TryGetDescendantsKeysFromStructure(_navigationStructure, parentKey, out descendantsKeys);
@@ -165,7 +165,6 @@ internal abstract class ContentNavigationServiceBase
_recycleBinNavigationStructure.TryRemove(key, out _);
}
-
///
/// Rebuilds the navigation structure based on the specified object type key and whether the items are trashed.
/// Only relevant for items in the content and media trees (which have readLock values of -333 or -334).
@@ -184,11 +183,17 @@ internal abstract class ContentNavigationServiceBase
using ICoreScope scope = _coreScopeProvider.CreateCoreScope(autoComplete: true);
scope.ReadLock(readLock);
- IEnumerable navigationModels = trashed ?
- _navigationRepository.GetTrashedContentNodesByObjectType(objectTypeKey) :
- _navigationRepository.GetContentNodesByObjectType(objectTypeKey);
-
- NavigationFactory.BuildNavigationDictionary(_navigationStructure, navigationModels);
+ // Build the corresponding navigation structure
+ if (trashed)
+ {
+ IEnumerable navigationModels = _navigationRepository.GetTrashedContentNodesByObjectType(objectTypeKey);
+ NavigationFactory.BuildNavigationDictionary(_recycleBinNavigationStructure, navigationModels);
+ }
+ else
+ {
+ IEnumerable navigationModels = _navigationRepository.GetContentNodesByObjectType(objectTypeKey);
+ NavigationFactory.BuildNavigationDictionary(_navigationStructure, navigationModels);
+ }
}
private bool TryGetParentKeyFromStructure(ConcurrentDictionary structure, Guid childKey, out Guid? parentKey)
@@ -204,6 +209,13 @@ internal abstract class ContentNavigationServiceBase
return false;
}
+ private bool TryGetRootKeysFromStructure(ConcurrentDictionary structure, out IEnumerable rootKeys)
+ {
+ // TODO can we make this more efficient?
+ rootKeys = structure.Values.Where(x => x.Parent is null).Select(x => x.Key);
+ return true;
+ }
+
private bool TryGetChildrenKeysFromStructure(ConcurrentDictionary structure, Guid parentKey, out IEnumerable childrenKeys)
{
if (structure.TryGetValue(parentKey, out NavigationNode? parentNode) is false)
@@ -217,13 +229,6 @@ internal abstract class ContentNavigationServiceBase
return true;
}
- private bool TryGetRootKeysFromStructure(ConcurrentDictionary structure, out IEnumerable childrenKeys)
- {
- // TODO can we make this more efficient?
- childrenKeys = structure.Values.Where(x=>x.Parent is null).Select(x=>x.Key);
- return true;
- }
-
private bool TryGetDescendantsKeysFromStructure(ConcurrentDictionary structure, Guid parentKey, out IEnumerable descendantsKeys)
{
var descendants = new List();
diff --git a/src/Umbraco.Core/Services/Navigation/INavigationQueryService.cs b/src/Umbraco.Core/Services/Navigation/INavigationQueryService.cs
index 9b6fb9807d..204ec657eb 100644
--- a/src/Umbraco.Core/Services/Navigation/INavigationQueryService.cs
+++ b/src/Umbraco.Core/Services/Navigation/INavigationQueryService.cs
@@ -8,8 +8,9 @@ public interface INavigationQueryService
{
bool TryGetParentKey(Guid childKey, out Guid? parentKey);
+ bool TryGetRootKeys(out IEnumerable rootKeys);
+
bool TryGetChildrenKeys(Guid parentKey, out IEnumerable childrenKeys);
- bool TryGetRootKeys(out IEnumerable childrenKeys);
bool TryGetDescendantsKeys(Guid parentKey, out IEnumerable descendantsKeys);
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Copy.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Copy.cs
index b3ebfa2215..95659b38be 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Copy.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Copy.cs
@@ -3,17 +3,12 @@ using Umbraco.Cms.Core;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
-// TODO: test that it is added to its new parent - check parent's children
-// TODO: test that it has the same amount of descendants - depending on value of includeDescendants param
-// TODO: test that the number of target parent descendants updates when copying node with descendants
-// TODO: test that copied node descendants have different keys than source node descendants
public partial class DocumentNavigationServiceTests
{
[Test]
[TestCase("A1B1B217-B02F-4307-862C-A5E22DB729EB", "A1B1B217-B02F-4307-862C-A5E22DB729EB")] // Grandchild 2 to itself
- [TestCase("60E0E5C4-084E-4144-A560-7393BEAD2E96", null)] // Child 2 to content root
[TestCase("B606E3FF-E070-4D46-8CB9-D31352029FDF", "C6173927-0C59-4778-825D-D7B9F45D8DDE")] // Child 3 to Child 1
- public async Task Structure_Updates_When_Copying_Content(Guid nodeToCopy, Guid? targetParentKey)
+ public async Task Structure_Updates_When_Copying_Content(Guid nodeToCopy, Guid targetParentKey)
{
// Arrange
DocumentNavigationQueryService.TryGetParentKey(nodeToCopy, out Guid? sourceParentKey);
@@ -29,18 +24,86 @@ public partial class DocumentNavigationServiceTests
Assert.Multiple(() =>
{
- if (targetParentKey is null)
- {
- // Verify the copied node's parent is null (it's been copied to content root)
- Assert.IsNull(copiedItemParentKey);
- }
- else
- {
- Assert.IsNotNull(copiedItemParentKey);
- }
-
+ Assert.IsNotNull(copiedItemParentKey);
Assert.AreEqual(targetParentKey, copiedItemParentKey);
Assert.AreNotEqual(sourceParentKey, copiedItemParentKey);
});
}
+
+ [Test]
+ public async Task Structure_Updates_When_Copying_Content_To_Root()
+ {
+ // Arrange
+ DocumentNavigationQueryService.TryGetParentKey(Grandchild2.Key, out Guid? sourceParentKey);
+ DocumentNavigationQueryService.TryGetSiblingsKeys(Root.Key, out IEnumerable beforeCopyRootSiblingsKeys);
+ var initialRootSiblingsCount = beforeCopyRootSiblingsKeys.Count();
+
+ // Act
+ var copyAttempt = await ContentEditingService.CopyAsync(Grandchild2.Key, null, false, false, Constants.Security.SuperUserKey);
+ Guid copiedItemKey = copyAttempt.Result.Key;
+
+ // Assert
+ Assert.AreNotEqual(Grandchild2.Key, copiedItemKey);
+
+ DocumentNavigationQueryService.TryGetParentKey(copiedItemKey, out Guid? copiedItemParentKey);
+ DocumentNavigationQueryService.TryGetSiblingsKeys(Root.Key, out IEnumerable afterCopyRootSiblingsKeys);
+ DocumentNavigationQueryService.TryGetChildrenKeys(sourceParentKey.Value, out IEnumerable sourceParentChildrenKeys);
+ List rootSiblingsList = afterCopyRootSiblingsKeys.ToList();
+
+ Assert.Multiple(() =>
+ {
+ // Verifies that the node actually has been copied
+ Assert.AreNotEqual(sourceParentKey, copiedItemParentKey);
+ Assert.IsNull(copiedItemParentKey);
+
+ // Verifies that the siblings amount has been updated after copying
+ Assert.AreEqual(initialRootSiblingsCount + 1, rootSiblingsList.Count);
+ Assert.IsTrue(rootSiblingsList.Contains(copiedItemKey));
+
+ // Verifies that the node was copied and not moved
+ Assert.IsTrue(sourceParentChildrenKeys.Contains(Grandchild2.Key));
+ });
+ }
+
+ [Test]
+ public async Task Structure_Updates_When_Copying_Content_With_Descendants()
+ {
+ // Arrange
+ DocumentNavigationQueryService.TryGetParentKey(Grandchild3.Key, out Guid? sourceParentKey);
+ DocumentNavigationQueryService.TryGetDescendantsKeys(Grandchild3.Key, out IEnumerable beforeCopyGrandChild1Descendents);
+ DocumentNavigationQueryService.TryGetChildrenKeys(Child3.Key, out IEnumerable beforeCopyChild3ChildrenKeys);
+ var initialChild3ChildrenCount = beforeCopyChild3ChildrenKeys.Count();
+ var initialGrandChild1DescendentsCount = beforeCopyGrandChild1Descendents.Count();
+
+ // Act
+ var copyAttempt = await ContentEditingService.CopyAsync(Grandchild3.Key, Child3.Key, false, true, Constants.Security.SuperUserKey);
+ Guid copiedItemKey = copyAttempt.Result.Key;
+
+ // Assert
+ Assert.AreNotEqual(Grandchild3.Key, copiedItemKey);
+
+ DocumentNavigationQueryService.TryGetParentKey(copiedItemKey, out Guid? copiedItemParentKey);
+ DocumentNavigationQueryService.TryGetChildrenKeys(Child3.Key, out IEnumerable afterCopyChild3ChildrenKeys);
+ DocumentNavigationQueryService.TryGetChildrenKeys(copiedItemKey, out IEnumerable afterCopyGrandChild1Descendents);
+ List child3ChildrenList = afterCopyChild3ChildrenKeys.ToList();
+ List grandChild1DescendantsList = afterCopyGrandChild1Descendents.ToList();
+
+ // Retrieves the child of the copied item to check its content
+ var copiedGreatGrandChild1 = await ContentEditingService.GetAsync(grandChild1DescendantsList.First());
+
+ Assert.Multiple(() =>
+ {
+ // Verifies that the node actually has been copied
+ Assert.AreNotEqual(sourceParentKey, copiedItemParentKey);
+ Assert.AreEqual(Child3.Key, copiedItemParentKey);
+ Assert.AreEqual(initialChild3ChildrenCount + 1, child3ChildrenList.Count);
+
+ // Verifies that the descendant amount is the same for the original and the moved GrandChild1 node
+ Assert.AreEqual(initialGrandChild1DescendentsCount, grandChild1DescendantsList.Count);
+
+ // Verifies that the keys are not the same
+ Assert.AreEqual(GreatGrandchild1.Name, copiedGreatGrandChild1.Name);
+ Assert.AreNotEqual(GreatGrandchild1.Key, copiedGreatGrandChild1.Key);
+ });
+ }
}
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Create.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Create.cs
index 2ee6c7cabe..4138f80b07 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Create.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Create.cs
@@ -7,18 +7,12 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
public partial class DocumentNavigationServiceTests
{
[Test]
- public async Task Structure_Updates_When_Creating_Content()
+ public async Task Structure_Updates_When_Creating_Content_At_Root()
{
// Arrange
DocumentNavigationQueryService.TryGetSiblingsKeys(Root.Key, out IEnumerable initialSiblingsKeys);
var initialRootNodeSiblingsCount = initialSiblingsKeys.Count();
-
- var createModel = new ContentCreateModel
- {
- ContentTypeKey = ContentType.Key,
- ParentKey = Constants.System.RootKey, // Create node at content root
- InvariantName = "Root 2",
- };
+ var createModel = CreateContentCreateModel("Root 2", Guid.NewGuid(), Constants.System.RootKey);
// Act
var createAttempt = await ContentEditingService.CreateAsync(createModel, Constants.Security.SuperUserKey);
@@ -36,4 +30,29 @@ public partial class DocumentNavigationServiceTests
Assert.AreEqual(createdItemKey, siblingsList.First());
});
}
+
+ [Test]
+ public async Task Structure_Updates_When_Creating_Child_Content()
+ {
+ // Arrange
+ DocumentNavigationQueryService.TryGetChildrenKeys(Child1.Key, out IEnumerable initialChildrenKeys);
+ var initialChild1ChildrenCount = initialChildrenKeys.Count();
+ var createModel = CreateContentCreateModel("Child1Child", Guid.NewGuid(), Child1.Key);
+
+ // Act
+ var createAttempt = await ContentEditingService.CreateAsync(createModel, Constants.Security.SuperUserKey);
+ Guid createdItemKey = createAttempt.Result.Content!.Key;
+
+ // Verify that the structure has updated by checking the children of the Child1 node once again
+ DocumentNavigationQueryService.TryGetChildrenKeys(Child1.Key, out IEnumerable updatedChildrenKeys);
+ List childrenList = updatedChildrenKeys.ToList();
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.IsNotEmpty(childrenList);
+ Assert.AreEqual(initialChild1ChildrenCount + 1, childrenList.Count);
+ Assert.IsTrue(childrenList.Contains(createdItemKey));
+ });
+ }
}
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Delete.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Delete.cs
index 5e6e655d74..8a60eaecc8 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Delete.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Delete.cs
@@ -3,7 +3,6 @@ using Umbraco.Cms.Core;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
-// TODO: Test that the descendants of the node have also been removed from both structures
public partial class DocumentNavigationServiceTests
{
[Test]
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.DeleteFromRecycleBin.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.DeleteFromRecycleBin.cs
index 1f9b819366..ee9f2815aa 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.DeleteFromRecycleBin.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.DeleteFromRecycleBin.cs
@@ -3,7 +3,6 @@ using Umbraco.Cms.Core;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
-// TODO: check that the descendants have also been removed from both structures - navigation and trash
public partial class DocumentNavigationServiceTests
{
[Test]
@@ -12,11 +11,14 @@ public partial class DocumentNavigationServiceTests
// Arrange
Guid nodeToDelete = Child1.Key;
Guid nodeInRecycleBin = Grandchild4.Key;
+ DocumentNavigationQueryService.TryGetDescendantsKeys(nodeToDelete, out IEnumerable initialDescendantsKeys);
// Move nodes to recycle bin
- await ContentEditingService.MoveToRecycleBinAsync(nodeInRecycleBin, Constants.Security.SuperUserKey); // Make sure we have an item already in the recycle bin to act as a sibling
- await ContentEditingService.MoveToRecycleBinAsync(nodeToDelete, Constants.Security.SuperUserKey); // Make sure the item is in the recycle bin
+ await ContentEditingService.MoveToRecycleBinAsync(nodeInRecycleBin, Constants.Security.SuperUserKey);
+ await ContentEditingService.MoveToRecycleBinAsync(nodeToDelete, Constants.Security.SuperUserKey);
DocumentNavigationQueryService.TryGetSiblingsKeysInBin(nodeInRecycleBin, out IEnumerable initialSiblingsKeys);
+ var initialSiblingsCount = initialSiblingsKeys.Count();
+ Assert.AreEqual(initialSiblingsCount, 1);
// Act
await ContentEditingService.DeleteFromRecycleBinAsync(nodeToDelete, Constants.Security.SuperUserKey);
@@ -24,7 +26,17 @@ public partial class DocumentNavigationServiceTests
// Assert
DocumentNavigationQueryService.TryGetSiblingsKeysInBin(nodeInRecycleBin, out IEnumerable updatedSiblingsKeys);
- // Verify siblings count has decreased by one
- Assert.AreEqual(initialSiblingsKeys.Count() - 1, updatedSiblingsKeys.Count());
+ Assert.Multiple(() =>
+ {
+ // Verify siblings count has decreased by one
+ Assert.AreEqual(initialSiblingsCount - 1, updatedSiblingsKeys.Count());
+ foreach (Guid descendant in initialDescendantsKeys)
+ {
+ var descendantExists = DocumentNavigationQueryService.TryGetParentKey(descendant, out _);
+ Assert.IsFalse(descendantExists);
+ var descendantExistsInRecycleBin = DocumentNavigationQueryService.TryGetParentKeyInBin(descendant, out _);
+ Assert.IsFalse(descendantExistsInRecycleBin);
+ }
+ });
}
}
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Move.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Move.cs
index 078e06de2b..f31f4f7907 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Move.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Move.cs
@@ -7,12 +7,17 @@ public partial class DocumentNavigationServiceTests
{
[Test]
[TestCase("E856AC03-C23E-4F63-9AA9-681B42A58573", "60E0E5C4-084E-4144-A560-7393BEAD2E96")] // Grandchild 1 to Child 2
- [TestCase("B606E3FF-E070-4D46-8CB9-D31352029FDF", null)] // Child 3 to content root
[TestCase("60E0E5C4-084E-4144-A560-7393BEAD2E96", "C6173927-0C59-4778-825D-D7B9F45D8DDE")] // Child 2 to Child 1
- public async Task Structure_Updates_When_Moving_Content(Guid nodeToMove, Guid? targetParentKey)
+ public async Task Structure_Updates_When_Moving_Content(Guid nodeToMove, Guid targetParentKey)
{
// Arrange
DocumentNavigationQueryService.TryGetParentKey(nodeToMove, out Guid? originalParentKey);
+ DocumentNavigationQueryService.TryGetDescendantsKeys(nodeToMove, out IEnumerable initialDescendantsKeys);
+ var beforeMoveDescendants = initialDescendantsKeys.ToList();
+ DocumentNavigationQueryService.TryGetChildrenKeys(originalParentKey.Value, out IEnumerable beforeMoveInitialParentChildrenKeys);
+ var beforeMoveInitialParentChildren = beforeMoveInitialParentChildrenKeys.ToList();
+ DocumentNavigationQueryService.TryGetChildrenKeys(targetParentKey, out IEnumerable beforeMoveTargetParentChildrenKeys);
+ var beforeMoveTargetParentChildren = beforeMoveTargetParentChildrenKeys.ToList();
// Act
var moveAttempt = await ContentEditingService.MoveAsync(nodeToMove, targetParentKey, Constants.Security.SuperUserKey);
@@ -21,19 +26,72 @@ public partial class DocumentNavigationServiceTests
DocumentNavigationQueryService.TryGetParentKey(moveAttempt.Result!.Key, out Guid? updatedParentKey);
// Assert
+ DocumentNavigationQueryService.TryGetDescendantsKeys(nodeToMove, out IEnumerable afterMoveDescendantsKeys);
+ var afterMoveDescendants = afterMoveDescendantsKeys.ToList();
+ DocumentNavigationQueryService.TryGetChildrenKeys(originalParentKey.Value, out IEnumerable afterMoveInitialParentChildrenKeys);
+ var afterMoveInitialParentChildren = afterMoveInitialParentChildrenKeys.ToList();
+ DocumentNavigationQueryService.TryGetChildrenKeys(targetParentKey, out IEnumerable afterMoveTargetParentChildrenKeys);
+ var afterMoveTargetParentChildren = afterMoveTargetParentChildrenKeys.ToList();
+
Assert.Multiple(() =>
{
- if (targetParentKey is null)
- {
- Assert.IsNull(updatedParentKey);
- }
- else
- {
- Assert.IsNotNull(updatedParentKey);
- }
-
+ Assert.IsNotNull(updatedParentKey);
Assert.AreNotEqual(originalParentKey, updatedParentKey);
Assert.AreEqual(targetParentKey, updatedParentKey);
+
+ // Verifies that the parent's children have been updated
+ Assert.AreEqual(beforeMoveInitialParentChildren.Count - 1, afterMoveInitialParentChildren.Count);
+ Assert.AreEqual(beforeMoveTargetParentChildren.Count + 1, afterMoveTargetParentChildren.Count);
+
+ // Verifies that the descendants are the same before and after the move
+ Assert.AreEqual(beforeMoveDescendants.Count, afterMoveDescendants.Count);
+ Assert.AreEqual(beforeMoveDescendants, afterMoveDescendants);
+ });
+ }
+
+ [Test]
+ public async Task Structure_Updates_When_Moving_Content_To_Root()
+ {
+ // Arrange
+ Guid nodeToMove = Child3.Key;
+ Guid? targetParentKey = Constants.System.RootKey; // Root
+ DocumentNavigationQueryService.TryGetParentKey(nodeToMove, out Guid? originalParentKey);
+ DocumentNavigationQueryService.TryGetDescendantsKeys(nodeToMove, out IEnumerable initialDescendantsKeys);
+ var beforeMoveDescendants = initialDescendantsKeys.ToList();
+ DocumentNavigationQueryService.TryGetDescendantsKeys(originalParentKey.Value, out IEnumerable beforeMoveInitialParentDescendantsKeys);
+ var beforeMoveInitialParentDescendants = beforeMoveInitialParentDescendantsKeys.ToList();
+
+ // The Root node is the only node at the root
+ DocumentNavigationQueryService.TryGetSiblingsKeys(Root.Key, out IEnumerable beforeMoveSiblingsKeys);
+ var beforeMoveRootSiblings = beforeMoveSiblingsKeys.ToList();
+
+ // Act
+ var moveAttempt = await ContentEditingService.MoveAsync(nodeToMove, targetParentKey, Constants.Security.SuperUserKey);
+
+ // Verify the node's new parent is updated
+ DocumentNavigationQueryService.TryGetParentKey(moveAttempt.Result!.Key, out Guid? updatedParentKey);
+
+ // Assert
+ DocumentNavigationQueryService.TryGetDescendantsKeys(nodeToMove, out IEnumerable afterMoveDescendantsKeys);
+ var afterMoveDescendants = afterMoveDescendantsKeys.ToList();
+ DocumentNavigationQueryService.TryGetDescendantsKeys((Guid)originalParentKey, out IEnumerable afterMoveInitialParentDescendantsKeys);
+ var afterMoveInitialParentDescendants = afterMoveInitialParentDescendantsKeys.ToList();
+ DocumentNavigationQueryService.TryGetSiblingsKeys(Root.Key, out IEnumerable afterMoveSiblingsKeys);
+ var afterMoveRootSiblings = afterMoveSiblingsKeys.ToList();
+
+ Assert.Multiple(() =>
+ {
+ Assert.IsNull(updatedParentKey);
+ Assert.AreNotEqual(originalParentKey, updatedParentKey);
+ Assert.AreEqual(targetParentKey, updatedParentKey);
+
+ // Verifies that the parent's children have been updated
+ Assert.AreEqual(beforeMoveInitialParentDescendants.Count - (afterMoveDescendants.Count + 1), afterMoveInitialParentDescendants.Count);
+ Assert.AreEqual(beforeMoveRootSiblings.Count + 1, afterMoveRootSiblings.Count);
+
+ // Verifies that the descendants are the same before and after the move
+ Assert.AreEqual(beforeMoveDescendants.Count, afterMoveDescendants.Count);
+ Assert.AreEqual(beforeMoveDescendants, afterMoveDescendants);
});
}
}
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.MoveToRecycleBin.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.MoveToRecycleBin.cs
index 1bd4bd9d83..9e5e7dcc69 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.MoveToRecycleBin.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.MoveToRecycleBin.cs
@@ -3,8 +3,6 @@ using Umbraco.Cms.Core;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
-// TODO: also check that initial siblings count's decreased
-// TODO: and that descendants are still the same (i.e. they've also been moved to recycle bin)
public partial class DocumentNavigationServiceTests
{
[Test]
@@ -12,7 +10,16 @@ public partial class DocumentNavigationServiceTests
{
// Arrange
Guid nodeToMoveToRecycleBin = Child3.Key;
+ Guid nodeInRecycleBin = Grandchild4.Key;
+ await ContentEditingService.MoveToRecycleBinAsync(nodeInRecycleBin, Constants.Security.SuperUserKey);
+ DocumentNavigationQueryService.TryGetSiblingsKeysInBin(nodeInRecycleBin, out IEnumerable initialSiblingsKeys);
+ var beforeMoveRecycleBinSiblingsCount = initialSiblingsKeys.Count();
+ Assert.AreEqual(beforeMoveRecycleBinSiblingsCount, 0);
DocumentNavigationQueryService.TryGetParentKey(nodeToMoveToRecycleBin, out Guid? originalParentKey);
+ DocumentNavigationQueryService.TryGetDescendantsKeys(nodeToMoveToRecycleBin, out IEnumerable initialDescendantsKeys);
+ var beforeMoveDescendants = initialDescendantsKeys.ToList();
+ DocumentNavigationQueryService.TryGetChildrenKeys(originalParentKey.Value, out IEnumerable initialParentChildrenKeys);
+ var beforeMoveParentSiblingsCount = initialParentChildrenKeys.Count();
// Act
await ContentEditingService.MoveToRecycleBinAsync(nodeToMoveToRecycleBin, Constants.Security.SuperUserKey);
@@ -20,13 +27,23 @@ public partial class DocumentNavigationServiceTests
// Assert
var nodeExists = DocumentNavigationQueryService.TryGetParentKey(nodeToMoveToRecycleBin, out _); // Verify that the item is no longer in the document structure
var nodeExistsInRecycleBin = DocumentNavigationQueryService.TryGetParentKeyInBin(nodeToMoveToRecycleBin, out Guid? updatedParentKeyInRecycleBin);
+ DocumentNavigationQueryService.TryGetDescendantsKeysInBin(nodeToMoveToRecycleBin, out IEnumerable afterMoveDescendantsKeys);
+ var afterMoveDescendants = afterMoveDescendantsKeys.ToList();
+ DocumentNavigationQueryService.TryGetChildrenKeys((Guid)originalParentKey, out IEnumerable afterMoveParentChildrenKeys);
+ var afterMoveParentSiblingsCount = afterMoveParentChildrenKeys.Count();
+ DocumentNavigationQueryService.TryGetSiblingsKeysInBin(nodeInRecycleBin, out IEnumerable afterMoveRecycleBinSiblingsKeys);
+ var afterMoveRecycleBinSiblingsCount = afterMoveRecycleBinSiblingsKeys.Count();
Assert.Multiple(() =>
{
Assert.IsFalse(nodeExists);
Assert.IsTrue(nodeExistsInRecycleBin);
Assert.AreNotEqual(originalParentKey, updatedParentKeyInRecycleBin);
+
Assert.IsNull(updatedParentKeyInRecycleBin); // Verify the node's parent is now located at the root of the recycle bin (null)
+ Assert.AreEqual(beforeMoveDescendants, afterMoveDescendants);
+ Assert.AreEqual(beforeMoveParentSiblingsCount - 1, afterMoveParentSiblingsCount);
+ Assert.AreEqual(beforeMoveRecycleBinSiblingsCount + 1, afterMoveRecycleBinSiblingsCount);
});
}
}
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Rebuild.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Rebuild.cs
index 6d70870a32..addf668cb3 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Rebuild.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Rebuild.cs
@@ -1,4 +1,5 @@
using NUnit.Framework;
+using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services.Navigation;
@@ -13,14 +14,14 @@ public partial class DocumentNavigationServiceTests
// Arrange
Guid nodeKey = Root.Key;
- // Capture original built state of DocumentNavigationService
+ // Capture original built state of DocumentNavigationQueryService
DocumentNavigationQueryService.TryGetParentKey(nodeKey, out Guid? originalParentKey);
DocumentNavigationQueryService.TryGetChildrenKeys(nodeKey, out IEnumerable originalChildrenKeys);
DocumentNavigationQueryService.TryGetDescendantsKeys(nodeKey, out IEnumerable originalDescendantsKeys);
DocumentNavigationQueryService.TryGetAncestorsKeys(nodeKey, out IEnumerable originalAncestorsKeys);
DocumentNavigationQueryService.TryGetSiblingsKeys(nodeKey, out IEnumerable originalSiblingsKeys);
- // Im-memory navigation structure is empty here
+ // In-memory navigation structure is empty here
var newDocumentNavigationService = new DocumentNavigationService(GetRequiredService(), GetRequiredService());
var initialNodeExists = newDocumentNavigationService.TryGetParentKey(nodeKey, out _);
@@ -52,8 +53,47 @@ public partial class DocumentNavigationServiceTests
}
[Test]
- // TODO: Test that you can rebuild bin structure as well
public async Task Bin_Structure_Can_Rebuild()
{
+ // Arrange
+ Guid nodeKey = Root.Key;
+ await ContentEditingService.MoveToRecycleBinAsync(nodeKey, Constants.Security.SuperUserKey);
+
+ // Capture original built state of DocumentNavigationQueryService
+ DocumentNavigationQueryService.TryGetParentKeyInBin(nodeKey, out Guid? originalParentKey);
+ DocumentNavigationQueryService.TryGetChildrenKeysInBin(nodeKey, out IEnumerable originalChildrenKeys);
+ DocumentNavigationQueryService.TryGetDescendantsKeysInBin(nodeKey, out IEnumerable originalDescendantsKeys);
+ DocumentNavigationQueryService.TryGetAncestorsKeysInBin(nodeKey, out IEnumerable originalAncestorsKeys);
+ DocumentNavigationQueryService.TryGetSiblingsKeysInBin(nodeKey, out IEnumerable originalSiblingsKeys);
+
+ // In-memory navigation structure is empty here
+ var newDocumentNavigationService = new DocumentNavigationService(GetRequiredService(), GetRequiredService());
+ var initialNodeExists = newDocumentNavigationService.TryGetParentKeyInBin(nodeKey, out _);
+
+ // Act
+ await newDocumentNavigationService.RebuildBinAsync();
+
+ // Capture rebuilt state
+ var nodeExists = newDocumentNavigationService.TryGetParentKeyInBin(nodeKey, out Guid? parentKeyFromRebuild);
+ newDocumentNavigationService.TryGetChildrenKeysInBin(nodeKey, out IEnumerable childrenKeysFromRebuild);
+ newDocumentNavigationService.TryGetDescendantsKeysInBin(nodeKey, out IEnumerable descendantsKeysFromRebuild);
+ newDocumentNavigationService.TryGetAncestorsKeysInBin(nodeKey, out IEnumerable ancestorsKeysFromRebuild);
+ newDocumentNavigationService.TryGetSiblingsKeysInBin(nodeKey, out IEnumerable siblingsKeysFromRebuild);
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.IsFalse(initialNodeExists);
+
+ // Verify that the item is present in the navigation structure after a rebuild
+ Assert.IsTrue(nodeExists);
+
+ // Verify that we have the same items as in the original built state of DocumentNavigationService
+ Assert.AreEqual(originalParentKey, parentKeyFromRebuild);
+ CollectionAssert.AreEquivalent(originalChildrenKeys, childrenKeysFromRebuild);
+ CollectionAssert.AreEquivalent(originalDescendantsKeys, descendantsKeysFromRebuild);
+ CollectionAssert.AreEquivalent(originalAncestorsKeys, ancestorsKeysFromRebuild);
+ CollectionAssert.AreEquivalent(originalSiblingsKeys, siblingsKeysFromRebuild);
+ });
}
}
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Restore.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Restore.cs
index 3151fb83e4..e57f0c652c 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Restore.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.Restore.cs
@@ -3,7 +3,6 @@ using Umbraco.Cms.Core;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
-// TODO: test that descendants are also restored in the right place
public partial class DocumentNavigationServiceTests
{
[Test]
@@ -20,6 +19,8 @@ public partial class DocumentNavigationServiceTests
await ContentEditingService.MoveToRecycleBinAsync(nodeToRestore, Constants.Security.SuperUserKey); // Make sure the item is in the recycle bin
DocumentNavigationQueryService.TryGetParentKeyInBin(nodeToRestore, out Guid? initialParentKey);
DocumentNavigationQueryService.TryGetSiblingsKeysInBin(nodeInRecycleBin, out IEnumerable initialSiblingsKeys);
+ DocumentNavigationQueryService.TryGetDescendantsKeysInBin(nodeToRestore, out IEnumerable initialDescendantsKeys);
+ var beforeRestoreDescendants = initialDescendantsKeys.ToList();
// Act
var restoreAttempt = await ContentEditingService.RestoreAsync(nodeToRestore, targetParentKey, Constants.Security.SuperUserKey);
@@ -28,7 +29,8 @@ public partial class DocumentNavigationServiceTests
// Assert
DocumentNavigationQueryService.TryGetParentKey(restoredItemKey, out Guid? restoredItemParentKey);
DocumentNavigationQueryService.TryGetSiblingsKeysInBin(nodeInRecycleBin, out IEnumerable updatedSiblingsKeys);
-
+ DocumentNavigationQueryService.TryGetDescendantsKeys(restoredItemKey, out IEnumerable afterRestoreDescendantsKeys);
+ var afterRestoreDescendants = afterRestoreDescendantsKeys.ToList();
Assert.Multiple(() =>
{
// Verify siblings count has decreased by one
@@ -44,6 +46,7 @@ public partial class DocumentNavigationServiceTests
Assert.AreNotEqual(initialParentKey, restoredItemParentKey);
}
+ Assert.AreEqual(beforeRestoreDescendants, afterRestoreDescendants);
Assert.AreEqual(targetParentKey, restoredItemParentKey);
});
}
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Create.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Create.cs
index 1b3b6551a5..3111e5c8e5 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Create.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Create.cs
@@ -5,5 +5,53 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
public partial class MediaNavigationServiceTests
{
+ [Test]
+ public async Task Structure_Updates_When_Creating_Media_At_Root()
+ {
+ // Arrange
+ MediaNavigationQueryService.TryGetSiblingsKeys(Album.Key, out IEnumerable initialSiblingsKeys);
+ var initialRootNodeSiblingsCount = initialSiblingsKeys.Count();
+ var createModel = CreateMediaCreateModel("Root Image", Guid.NewGuid(), ImageMediaType.Key);
+ // Act
+ var createAttempt = await MediaEditingService.CreateAsync(createModel, Constants.Security.SuperUserKey);
+ Guid createdItemKey = createAttempt.Result.Content!.Key;
+
+ // Verify that the structure has updated by checking the siblings list of the Root once again
+ MediaNavigationQueryService.TryGetSiblingsKeys(Album.Key, out IEnumerable updatedSiblingsKeys);
+ List siblingsList = updatedSiblingsKeys.ToList();
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.IsNotEmpty(siblingsList);
+ Assert.AreEqual(initialRootNodeSiblingsCount + 1, siblingsList.Count);
+ Assert.AreEqual(createdItemKey, siblingsList.First());
+ });
+ }
+
+ [Test]
+ public async Task Structure_Updates_When_Creating_Child_Media()
+ {
+ // Arrange
+ MediaNavigationQueryService.TryGetChildrenKeys(Album.Key, out IEnumerable initialChildrenKeys);
+ var initialChild1ChildrenCount = initialChildrenKeys.Count();
+ var createModel = CreateMediaCreateModel("Child Image", Guid.NewGuid(), ImageMediaType.Key, Album.Key);
+
+ // Act
+ var createAttempt = await MediaEditingService.CreateAsync(createModel, Constants.Security.SuperUserKey);
+ Guid createdItemKey = createAttempt.Result.Content!.Key;
+
+ // Verify that the structure has updated by checking the children of the Child1 node once again
+ MediaNavigationQueryService.TryGetChildrenKeys(Album.Key, out IEnumerable updatedChildrenKeys);
+ List childrenList = updatedChildrenKeys.ToList();
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.IsNotEmpty(childrenList);
+ Assert.AreEqual(initialChild1ChildrenCount + 1, childrenList.Count);
+ Assert.IsTrue(childrenList.Contains(createdItemKey));
+ });
+ }
}
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Delete.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Delete.cs
index 1b3b6551a5..5eeadb2484 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Delete.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Delete.cs
@@ -5,5 +5,36 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
public partial class MediaNavigationServiceTests
{
+ [Test]
+ [TestCase("1CD97C02-8534-4B72-AE9E-AE52EC94CF31")] // Album
+ [TestCase("DBCAFF2F-BFA4-4744-A948-C290C432D564")] // Sub-album 2
+ [TestCase("3E489C32-9315-42DA-95CE-823D154B09C8")] // Image 2
+ public async Task Structure_Updates_When_Deleting_Media(Guid nodeToDelete)
+ {
+ // Arrange
+ MediaNavigationQueryService.TryGetDescendantsKeys(nodeToDelete, out IEnumerable initialDescendantsKeys);
+ // Act
+ // Deletes the item whether it is in the recycle bin or not
+ var deleteAttempt = await MediaEditingService.DeleteAsync(nodeToDelete, Constants.Security.SuperUserKey);
+ Guid deletedItemKey = deleteAttempt.Result.Key;
+
+ // Assert
+ var nodeExists = MediaNavigationQueryService.TryGetDescendantsKeys(deletedItemKey, out _);
+ var nodeExistsInRecycleBin = MediaNavigationQueryService.TryGetDescendantsKeysInBin(nodeToDelete, out _);
+
+ Assert.Multiple(() =>
+ {
+ Assert.AreEqual(nodeToDelete, deletedItemKey);
+ Assert.IsFalse(nodeExists);
+ Assert.IsFalse(nodeExistsInRecycleBin);
+ foreach (Guid descendant in initialDescendantsKeys)
+ {
+ var descendantExists = MediaNavigationQueryService.TryGetParentKey(descendant, out _);
+ Assert.IsFalse(descendantExists);
+ var descendantExistsInRecycleBin = MediaNavigationQueryService.TryGetParentKeyInBin(descendant, out _);
+ Assert.IsFalse(descendantExistsInRecycleBin);
+ }
+ });
+ }
}
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.DeleteFromRecycleBin.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.DeleteFromRecycleBin.cs
index 1b3b6551a5..c5a69b30f3 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.DeleteFromRecycleBin.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.DeleteFromRecycleBin.cs
@@ -5,5 +5,38 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
public partial class MediaNavigationServiceTests
{
+ [Test]
+ public async Task Structure_Updates_When_Deleting_From_Recycle_Bin()
+ {
+ // Arrange
+ Guid nodeToDelete = Image1.Key;
+ Guid nodeInRecycleBin = Image2.Key;
+ MediaNavigationQueryService.TryGetDescendantsKeys(nodeToDelete, out IEnumerable initialDescendantsKeys);
+ // Move nodes to recycle bin
+ await MediaEditingService.MoveToRecycleBinAsync(nodeInRecycleBin, Constants.Security.SuperUserKey);
+ await MediaEditingService.MoveToRecycleBinAsync(nodeToDelete, Constants.Security.SuperUserKey);
+ MediaNavigationQueryService.TryGetSiblingsKeysInBin(nodeInRecycleBin, out IEnumerable initialSiblingsKeys);
+ var initialSiblingsCount = initialSiblingsKeys.Count();
+ Assert.AreEqual(initialSiblingsCount, 1);
+
+ // Act
+ await MediaEditingService.DeleteFromRecycleBinAsync(nodeToDelete, Constants.Security.SuperUserKey);
+
+ // Assert
+ MediaNavigationQueryService.TryGetSiblingsKeysInBin(nodeInRecycleBin, out IEnumerable updatedSiblingsKeys);
+
+ Assert.Multiple(() =>
+ {
+ // Verify siblings count has decreased by one
+ Assert.AreEqual(initialSiblingsCount - 1, updatedSiblingsKeys.Count());
+ foreach (Guid descendant in initialDescendantsKeys)
+ {
+ var descendantExists = MediaNavigationQueryService.TryGetParentKey(descendant, out _);
+ Assert.IsFalse(descendantExists);
+ var descendantExistsInRecycleBin = MediaNavigationQueryService.TryGetParentKeyInBin(descendant, out _);
+ Assert.IsFalse(descendantExistsInRecycleBin);
+ }
+ });
+ }
}
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Move.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Move.cs
index 1b3b6551a5..1677dcec45 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Move.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Move.cs
@@ -5,5 +5,93 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
public partial class MediaNavigationServiceTests
{
+ [Test]
+ [TestCase("62BCE72F-8C18-420E-BCAC-112B5ECC95FD", "139DC977-E50F-4382-9728-B278C4B7AC6A")] // Image 4 to Sub-album 1
+ [TestCase("E0B23D56-9A0E-4FC4-BD42-834B73B4C7AB", "1CD97C02-8534-4B72-AE9E-AE52EC94CF31")] // Sub-sub-album 1 to Album
+ public async Task Structure_Updates_When_Moving_Media(Guid nodeToMove, Guid targetParentKey)
+ {
+ // Arrange
+ MediaNavigationQueryService.TryGetParentKey(nodeToMove, out Guid? originalParentKey);
+ MediaNavigationQueryService.TryGetDescendantsKeys(nodeToMove, out IEnumerable initialDescendantsKeys);
+ var beforeMoveDescendants = initialDescendantsKeys.ToList();
+ MediaNavigationQueryService.TryGetDescendantsKeys(originalParentKey.Value, out IEnumerable beforeMoveInitialParentDescendantsKeys);
+ var beforeMoveInitialParentDescendants = beforeMoveInitialParentDescendantsKeys.ToList();
+ MediaNavigationQueryService.TryGetChildrenKeys(targetParentKey, out IEnumerable beforeMoveTargetParentChildrenKeys);
+ var beforeMoveTargetParentChildren = beforeMoveTargetParentChildrenKeys.ToList();
+ // Act
+ var moveAttempt = await MediaEditingService.MoveAsync(nodeToMove, targetParentKey, Constants.Security.SuperUserKey);
+
+ // Verify the node's new parent is updated
+ MediaNavigationQueryService.TryGetParentKey(moveAttempt.Result!.Key, out Guid? updatedParentKey);
+
+ // Assert
+ MediaNavigationQueryService.TryGetDescendantsKeys(nodeToMove, out IEnumerable afterMoveDescendantsKeys);
+ var afterMoveDescendants = afterMoveDescendantsKeys.ToList();
+ MediaNavigationQueryService.TryGetDescendantsKeys(originalParentKey.Value, out IEnumerable afterMoveInitialParentDescendantsKeys);
+ var afterMoveInitialParentDescendants = afterMoveInitialParentDescendantsKeys.ToList();
+ MediaNavigationQueryService.TryGetChildrenKeys(targetParentKey, out IEnumerable afterMoveTargetParentChildrenKeys);
+ var afterMoveTargetParentChildren = afterMoveTargetParentChildrenKeys.ToList();
+
+ Assert.Multiple(() =>
+ {
+ Assert.IsNotNull(updatedParentKey);
+ Assert.AreNotEqual(originalParentKey, updatedParentKey);
+ Assert.AreEqual(targetParentKey, updatedParentKey);
+
+ // Verifies that the parent's children have been updated
+ Assert.AreEqual(beforeMoveInitialParentDescendants.Count - (afterMoveDescendants.Count + 1), afterMoveInitialParentDescendants.Count);
+ Assert.AreEqual(beforeMoveTargetParentChildren.Count + 1, afterMoveTargetParentChildren.Count);
+
+ // Verifies that the descendants are the same before and after the move
+ Assert.AreEqual(beforeMoveDescendants.Count, afterMoveDescendants.Count);
+ Assert.AreEqual(beforeMoveDescendants, afterMoveDescendants);
+ });
+ }
+
+ [Test]
+ public async Task Structure_Updates_When_Moving_Media_To_Root()
+ {
+ // Arrange
+ Guid nodeToMove = SubAlbum2.Key;
+ Guid? targetParentKey = Constants.System.RootKey;
+ MediaNavigationQueryService.TryGetParentKey(nodeToMove, out Guid? originalParentKey);
+ MediaNavigationQueryService.TryGetDescendantsKeys(nodeToMove, out IEnumerable initialDescendantsKeys);
+ var beforeMoveDescendants = initialDescendantsKeys.ToList();
+ MediaNavigationQueryService.TryGetDescendantsKeys(originalParentKey.Value, out IEnumerable beforeMoveInitialParentDescendantsKeys);
+ var beforeMoveInitialParentDescendants = beforeMoveInitialParentDescendantsKeys.ToList();
+
+ // The Root node is the only node at the root
+ MediaNavigationQueryService.TryGetSiblingsKeys(Album.Key, out IEnumerable beforeMoveSiblingsKeys);
+ var beforeMoveRootSiblings = beforeMoveSiblingsKeys.ToList();
+
+ // Act
+ var moveAttempt = await MediaEditingService.MoveAsync(nodeToMove, targetParentKey, Constants.Security.SuperUserKey);
+
+ // Verify the node's new parent is updated
+ MediaNavigationQueryService.TryGetParentKey(moveAttempt.Result!.Key, out Guid? updatedParentKey);
+
+ // Assert
+ MediaNavigationQueryService.TryGetDescendantsKeys(nodeToMove, out IEnumerable afterMoveDescendantsKeys);
+ var afterMoveDescendants = afterMoveDescendantsKeys.ToList();
+ MediaNavigationQueryService.TryGetDescendantsKeys((Guid)originalParentKey, out IEnumerable afterMoveInitialParentDescendantsKeys);
+ var afterMoveInitialParentDescendants = afterMoveInitialParentDescendantsKeys.ToList();
+ MediaNavigationQueryService.TryGetSiblingsKeys(Album.Key, out IEnumerable afterMoveSiblingsKeys);
+ var afterMoveRootSiblings = afterMoveSiblingsKeys.ToList();
+
+ Assert.Multiple(() =>
+ {
+ Assert.IsNull(updatedParentKey);
+ Assert.AreNotEqual(originalParentKey, updatedParentKey);
+ Assert.AreEqual(targetParentKey, updatedParentKey);
+
+ // Verifies that the parent's children have been updated
+ Assert.AreEqual(beforeMoveInitialParentDescendants.Count - (afterMoveDescendants.Count + 1), afterMoveInitialParentDescendants.Count);
+ Assert.AreEqual(beforeMoveRootSiblings.Count + 1, afterMoveRootSiblings.Count);
+
+ // Verifies that the descendants are the same before and after the move
+ Assert.AreEqual(beforeMoveDescendants.Count, afterMoveDescendants.Count);
+ Assert.AreEqual(beforeMoveDescendants, afterMoveDescendants);
+ });
+ }
}
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.MoveToRecycleBin.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.MoveToRecycleBin.cs
index 1b3b6551a5..f54eae0924 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.MoveToRecycleBin.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.MoveToRecycleBin.cs
@@ -5,5 +5,44 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
public partial class MediaNavigationServiceTests
{
+ [Test]
+ public async Task Structure_Updates_When_Moving_Media_To_Recycle_Bin()
+ {
+ // Arrange
+ Guid nodeToMoveToRecycleBin = Image3.Key;
+ Guid nodeInRecycleBin = SubSubAlbum1.Key;
+ await MediaEditingService.MoveToRecycleBinAsync(nodeInRecycleBin, Constants.Security.SuperUserKey);
+ MediaNavigationQueryService.TryGetSiblingsKeysInBin(nodeInRecycleBin, out IEnumerable initialSiblingsKeys);
+ var beforeMoveRecycleBinSiblingsCount = initialSiblingsKeys.Count();
+ Assert.AreEqual(beforeMoveRecycleBinSiblingsCount, 0);
+ MediaNavigationQueryService.TryGetParentKey(nodeToMoveToRecycleBin, out Guid? originalParentKey);
+ MediaNavigationQueryService.TryGetDescendantsKeys(nodeToMoveToRecycleBin, out IEnumerable initialDescendantsKeys);
+ var beforeMoveDescendants = initialDescendantsKeys.ToList();
+ MediaNavigationQueryService.TryGetChildrenKeys(originalParentKey.Value, out IEnumerable initialParentChildrenKeys);
+ var beforeMoveParentSiblingsCount = initialParentChildrenKeys.Count();
+ // Act
+ await MediaEditingService.MoveToRecycleBinAsync(nodeToMoveToRecycleBin, Constants.Security.SuperUserKey);
+
+ // Assert
+ var nodeExists = MediaNavigationQueryService.TryGetParentKey(nodeToMoveToRecycleBin, out _); // Verify that the item is no longer in the document structure
+ var nodeExistsInRecycleBin = MediaNavigationQueryService.TryGetParentKeyInBin(nodeToMoveToRecycleBin, out Guid? updatedParentKeyInRecycleBin);
+ MediaNavigationQueryService.TryGetDescendantsKeysInBin(nodeToMoveToRecycleBin, out IEnumerable afterMoveDescendantsKeys);
+ var afterMoveDescendants = afterMoveDescendantsKeys.ToList();
+ MediaNavigationQueryService.TryGetChildrenKeys(originalParentKey.Value, out IEnumerable afterMoveParentChildrenKeys);
+ var afterMoveParentSiblingsCount = afterMoveParentChildrenKeys.Count();
+ MediaNavigationQueryService.TryGetSiblingsKeysInBin(nodeInRecycleBin, out IEnumerable afterMoveRecycleBinSiblingsKeys);
+ var afterMoveRecycleBinSiblingsCount = afterMoveRecycleBinSiblingsKeys.Count();
+
+ Assert.Multiple(() =>
+ {
+ Assert.IsFalse(nodeExists);
+ Assert.IsTrue(nodeExistsInRecycleBin);
+ Assert.AreNotEqual(originalParentKey, updatedParentKeyInRecycleBin);
+ Assert.IsNull(updatedParentKeyInRecycleBin); // Verify the node's parent is now located at the root of the recycle bin (null)
+ Assert.AreEqual(beforeMoveDescendants, afterMoveDescendants);
+ Assert.AreEqual(beforeMoveParentSiblingsCount - 1, afterMoveParentSiblingsCount);
+ Assert.AreEqual(beforeMoveRecycleBinSiblingsCount + 1, afterMoveRecycleBinSiblingsCount);
+ });
+ }
}
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Rebuild.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Rebuild.cs
index 0f36e8b8ec..65244743e3 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Rebuild.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Rebuild.cs
@@ -1,4 +1,5 @@
using NUnit.Framework;
+using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services.Navigation;
@@ -13,14 +14,14 @@ public partial class MediaNavigationServiceTests
// Arrange
Guid nodeKey = Album.Key;
- // Capture original built state of MediaNavigationService
+ // Capture original built state of MediaNavigationQueryService
MediaNavigationQueryService.TryGetParentKey(nodeKey, out Guid? originalParentKey);
MediaNavigationQueryService.TryGetChildrenKeys(nodeKey, out IEnumerable originalChildrenKeys);
MediaNavigationQueryService.TryGetDescendantsKeys(nodeKey, out IEnumerable originalDescendantsKeys);
MediaNavigationQueryService.TryGetAncestorsKeys(nodeKey, out IEnumerable originalAncestorsKeys);
MediaNavigationQueryService.TryGetSiblingsKeys(nodeKey, out IEnumerable originalSiblingsKeys);
- // Im-memory navigation structure is empty here
+ // In-memory navigation structure is empty here
var newMediaNavigationService = new MediaNavigationService(GetRequiredService(), GetRequiredService());
var initialNodeExists = newMediaNavigationService.TryGetParentKey(nodeKey, out _);
@@ -52,8 +53,47 @@ public partial class MediaNavigationServiceTests
}
[Test]
- // TODO: Test that you can rebuild bin structure as well
public async Task Bin_Structure_Can_Rebuild()
{
+ // Arrange
+ Guid nodeKey = Album.Key;
+ await MediaEditingService.MoveToRecycleBinAsync(nodeKey, Constants.Security.SuperUserKey);
+
+ // Capture original built state of MediaNavigationQueryService
+ MediaNavigationQueryService.TryGetParentKeyInBin(nodeKey, out Guid? originalParentKey);
+ MediaNavigationQueryService.TryGetChildrenKeysInBin(nodeKey, out IEnumerable originalChildrenKeys);
+ MediaNavigationQueryService.TryGetDescendantsKeysInBin(nodeKey, out IEnumerable originalDescendantsKeys);
+ MediaNavigationQueryService.TryGetAncestorsKeysInBin(nodeKey, out IEnumerable originalAncestorsKeys);
+ MediaNavigationQueryService.TryGetSiblingsKeysInBin(nodeKey, out IEnumerable originalSiblingsKeys);
+
+ // In-memory navigation structure is empty here
+ var newMediaNavigationService = new MediaNavigationService(GetRequiredService(), GetRequiredService());
+ var initialNodeExists = newMediaNavigationService.TryGetParentKeyInBin(nodeKey, out _);
+
+ // Act
+ await newMediaNavigationService.RebuildBinAsync();
+
+ // Capture rebuilt state
+ var nodeExists = newMediaNavigationService.TryGetParentKeyInBin(nodeKey, out Guid? parentKeyFromRebuild);
+ newMediaNavigationService.TryGetChildrenKeysInBin(nodeKey, out IEnumerable childrenKeysFromRebuild);
+ newMediaNavigationService.TryGetDescendantsKeysInBin(nodeKey, out IEnumerable descendantsKeysFromRebuild);
+ newMediaNavigationService.TryGetAncestorsKeysInBin(nodeKey, out IEnumerable ancestorsKeysFromRebuild);
+ newMediaNavigationService.TryGetSiblingsKeysInBin(nodeKey, out IEnumerable siblingsKeysFromRebuild);
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.IsFalse(initialNodeExists);
+
+ // Verify that the item is present in the navigation structure after a rebuild
+ Assert.IsTrue(nodeExists);
+
+ // Verify that we have the same items as in the original built state of MediaNavigationService
+ Assert.AreEqual(originalParentKey, parentKeyFromRebuild);
+ CollectionAssert.AreEquivalent(originalChildrenKeys, childrenKeysFromRebuild);
+ CollectionAssert.AreEquivalent(originalDescendantsKeys, descendantsKeysFromRebuild);
+ CollectionAssert.AreEquivalent(originalAncestorsKeys, ancestorsKeysFromRebuild);
+ CollectionAssert.AreEquivalent(originalSiblingsKeys, siblingsKeysFromRebuild);
+ });
}
}
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Restore.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Restore.cs
index 1b3b6551a5..22e5e3d799 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Restore.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Restore.cs
@@ -5,5 +5,49 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
public partial class MediaNavigationServiceTests
{
+ [Test]
+ [TestCase("62BCE72F-8C18-420E-BCAC-112B5ECC95FD", "139DC977-E50F-4382-9728-B278C4B7AC6A")] // Image 4 to Sub-album 1
+ [TestCase("DBCAFF2F-BFA4-4744-A948-C290C432D564", "1CD97C02-8534-4B72-AE9E-AE52EC94CF31")] // Sub-album 2 to Album
+ [TestCase("3E489C32-9315-42DA-95CE-823D154B09C8", null)] // Image 2 to media root
+ public async Task Structure_Updates_When_Restoring_Media(Guid nodeToRestore, Guid? targetParentKey)
+ {
+ // Arrange
+ Guid nodeInRecycleBin = Image3.Key;
+ // Move nodes to recycle bin
+ await MediaEditingService.MoveToRecycleBinAsync(nodeInRecycleBin, Constants.Security.SuperUserKey);
+ await MediaEditingService.MoveToRecycleBinAsync(nodeToRestore, Constants.Security.SuperUserKey);
+ MediaNavigationQueryService.TryGetParentKeyInBin(nodeToRestore, out Guid? initialParentKey);
+ MediaNavigationQueryService.TryGetSiblingsKeysInBin(nodeInRecycleBin, out IEnumerable initialSiblingsKeys);
+ MediaNavigationQueryService.TryGetDescendantsKeysInBin(nodeToRestore, out IEnumerable initialDescendantsKeys);
+ var beforeRestoreDescendants = initialDescendantsKeys.ToList();
+
+ // Act
+ var restoreAttempt = await MediaEditingService.RestoreAsync(nodeToRestore, targetParentKey, Constants.Security.SuperUserKey);
+ Guid restoredItemKey = restoreAttempt.Result.Key;
+
+ // Assert
+ MediaNavigationQueryService.TryGetParentKey(restoredItemKey, out Guid? restoredItemParentKey);
+ MediaNavigationQueryService.TryGetSiblingsKeysInBin(nodeInRecycleBin, out IEnumerable updatedSiblingsKeys);
+ MediaNavigationQueryService.TryGetDescendantsKeys(restoredItemKey, out IEnumerable afterRestoreDescendantsKeys);
+ var afterRestoreDescendants = afterRestoreDescendantsKeys.ToList();
+
+ Assert.Multiple(() =>
+ {
+ // Verify siblings count has decreased by one
+ Assert.AreEqual(initialSiblingsKeys.Count() - 1, updatedSiblingsKeys.Count());
+ if (targetParentKey is null)
+ {
+ Assert.IsNull(restoredItemParentKey);
+ }
+ else
+ {
+ Assert.IsNotNull(restoredItemParentKey);
+ Assert.AreNotEqual(initialParentKey, restoredItemParentKey);
+ }
+
+ Assert.AreEqual(beforeRestoreDescendants, afterRestoreDescendants);
+ Assert.AreEqual(targetParentKey, restoredItemParentKey);
+ });
+ }
}
diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Update.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Update.cs
index 1b3b6551a5..0c24dbd0c8 100644
--- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Update.cs
+++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MediaNavigationServiceTests.Update.cs
@@ -1,9 +1,53 @@
using NUnit.Framework;
using Umbraco.Cms.Core;
+using Umbraco.Cms.Core.Models.ContentEditing;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
public partial class MediaNavigationServiceTests
{
+[Test]
+ public async Task Structure_Does_Not_Update_When_Updating_Media()
+ {
+ // Arrange
+ Guid nodeToUpdate = Album.Key;
+ // Capture initial state
+ MediaNavigationQueryService.TryGetParentKey(nodeToUpdate, out Guid? initialParentKey);
+ MediaNavigationQueryService.TryGetChildrenKeys(nodeToUpdate, out IEnumerable initialChildrenKeys);
+ MediaNavigationQueryService.TryGetDescendantsKeys(nodeToUpdate, out IEnumerable initialDescendantsKeys);
+ MediaNavigationQueryService.TryGetAncestorsKeys(nodeToUpdate, out IEnumerable initialAncestorsKeys);
+ MediaNavigationQueryService.TryGetSiblingsKeys(nodeToUpdate, out IEnumerable initialSiblingsKeys);
+
+ var updateModel = new MediaUpdateModel
+ {
+ InvariantName = "Updated Album",
+ };
+
+ // Act
+ var updateAttempt = await MediaEditingService.UpdateAsync(nodeToUpdate, updateModel, Constants.Security.SuperUserKey);
+ Guid updatedItemKey = updateAttempt.Result.Content!.Key;
+
+ // Capture updated state
+ var nodeExists = MediaNavigationQueryService.TryGetParentKey(updatedItemKey, out Guid? updatedParentKey);
+ MediaNavigationQueryService.TryGetChildrenKeys(updatedItemKey, out IEnumerable childrenKeysAfterUpdate);
+ MediaNavigationQueryService.TryGetDescendantsKeys(updatedItemKey, out IEnumerable descendantsKeysAfterUpdate);
+ MediaNavigationQueryService.TryGetAncestorsKeys(updatedItemKey, out IEnumerable ancestorsKeysAfterUpdate);
+ MediaNavigationQueryService.TryGetSiblingsKeys(updatedItemKey, out IEnumerable siblingsKeysAfterUpdate);
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ // Verify that the item is still present in the navigation structure
+ Assert.IsTrue(nodeExists);
+ Assert.AreEqual(nodeToUpdate, updatedItemKey);
+
+ // Verify that nothing's changed
+ Assert.AreEqual(initialParentKey, updatedParentKey);
+ CollectionAssert.AreEquivalent(initialChildrenKeys, childrenKeysAfterUpdate);
+ CollectionAssert.AreEquivalent(initialDescendantsKeys, descendantsKeysAfterUpdate);
+ CollectionAssert.AreEquivalent(initialAncestorsKeys, ancestorsKeysAfterUpdate);
+ CollectionAssert.AreEquivalent(initialSiblingsKeys, siblingsKeysAfterUpdate);
+ });
+ }
}